diff options
| author | Rasmus | 2017-09-19 09:00:00 +0200 |
|---|---|---|
| committer | Rasmus | 2017-09-19 09:00:00 +0200 |
| commit | eaefbc26d5c6cffbe4a22d3a9f4c7e6209a7b5a7 (patch) | |
| tree | 644cfb7f14d74a80453e311c9d9a2298a1e8212d | |
| parent | ab351d442d7bb4d17cbb43638aaed1775d8c0344 (diff) | |
| download | emacs-scratch/org-mode-merge.tar.gz emacs-scratch/org-mode-merge.zip | |
; Add files missing in ab351d442d7scratch/org-mode-merge
| -rw-r--r-- | lisp/org/ob-hledger.el | 70 | ||||
| -rw-r--r-- | lisp/org/ob-scala.el | 114 | ||||
| -rw-r--r-- | lisp/org/ob-vala.el | 115 | ||||
| -rw-r--r-- | lisp/org/org-duration.el | 446 |
4 files changed, 631 insertions, 114 deletions
diff --git a/lisp/org/ob-hledger.el b/lisp/org/ob-hledger.el new file mode 100644 index 00000000000..86276aad810 --- /dev/null +++ b/lisp/org/ob-hledger.el | |||
| @@ -0,0 +1,70 @@ | |||
| 1 | ;; ob-ledger.el --- Babel Functions for hledger -*- lexical-binding: t; -*- | ||
| 2 | |||
| 3 | ;; Copyright (C) 2010-2017 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | ;; Author: Simon Michael | ||
| 6 | ;; Keywords: literate programming, reproducible research, plain text accounting | ||
| 7 | ;; Homepage: http://orgmode.org | ||
| 8 | |||
| 9 | ;; This file is part of GNU Emacs. | ||
| 10 | |||
| 11 | ;; GNU Emacs is free software: you can redistribute it and/or modify | ||
| 12 | ;; it under the terms of the GNU General Public License as published by | ||
| 13 | ;; the Free Software Foundation, either version 3 of the License, or | ||
| 14 | ;; (at your option) any later version. | ||
| 15 | |||
| 16 | ;; GNU Emacs is distributed in the hope that it will be useful, | ||
| 17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 19 | ;; GNU General Public License for more details. | ||
| 20 | |||
| 21 | ;; You should have received a copy of the GNU General Public License | ||
| 22 | ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. | ||
| 23 | |||
| 24 | ;;; Commentary: | ||
| 25 | |||
| 26 | ;; Babel support for evaluating hledger entries. | ||
| 27 | ;; | ||
| 28 | ;; Based on ob-ledger.el. | ||
| 29 | ;; If the source block is empty, hledger will use a default journal file, | ||
| 30 | ;; probably ~/.hledger.journal (it may not notice your $LEDGER_FILE env var). | ||
| 31 | ;; So make ~/.hledger.journal a symbolic link to the real file if necessary. | ||
| 32 | |||
| 33 | ;;; Code: | ||
| 34 | (require 'ob) | ||
| 35 | |||
| 36 | (defvar org-babel-default-header-args:hledger | ||
| 37 | '((:results . "output") (:exports . "results") (:cmdline . "bal")) | ||
| 38 | "Default arguments to use when evaluating a hledger source block.") | ||
| 39 | |||
| 40 | (defun org-babel-execute:hledger (body params) | ||
| 41 | "Execute a block of hledger entries with org-babel. | ||
| 42 | This function is called by `org-babel-execute-src-block'." | ||
| 43 | (message "executing hledger source code block") | ||
| 44 | (letrec ( ;(result-params (split-string (or (cdr (assq :results params)) ""))) | ||
| 45 | (cmdline (cdr (assq :cmdline params))) | ||
| 46 | (in-file (org-babel-temp-file "hledger-")) | ||
| 47 | (out-file (org-babel-temp-file "hledger-output-")) | ||
| 48 | (hledgercmd (concat "hledger" | ||
| 49 | (if (> (length body) 0) | ||
| 50 | (concat " -f " (org-babel-process-file-name in-file)) | ||
| 51 | "") | ||
| 52 | " " cmdline))) | ||
| 53 | (with-temp-file in-file (insert body)) | ||
| 54 | ;; TODO This is calling for some refactoring: | ||
| 55 | ;; (concat "hledger" (if ...) " " cmdline) | ||
| 56 | ;; could be built only once and bound to a symbol. | ||
| 57 | (message "%s" hledgercmd) | ||
| 58 | (with-output-to-string | ||
| 59 | (shell-command (concat hledgercmd " > " (org-babel-process-file-name out-file)))) | ||
| 60 | (with-temp-buffer (insert-file-contents out-file) (buffer-string)))) | ||
| 61 | |||
| 62 | (defun org-babel-prep-session:hledger (_session _params) | ||
| 63 | (error "hledger does not support sessions")) | ||
| 64 | |||
| 65 | (provide 'ob-hledger) | ||
| 66 | |||
| 67 | |||
| 68 | |||
| 69 | ;;; ob-hledger.el ends here | ||
| 70 | ;; 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 d00b97c3db4..00000000000 --- a/lisp/org/ob-scala.el +++ /dev/null | |||
| @@ -1,114 +0,0 @@ | |||
| 1 | ;;; ob-scala.el --- Babel Functions for Scala -*- lexical-binding: t; -*- | ||
| 2 | |||
| 3 | ;; Copyright (C) 2012-2017 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | ;; Author: Andrzej Lichnerowicz | ||
| 6 | ;; Keywords: literate programming, reproducible research | ||
| 7 | ;; Homepage: http://orgmode.org | ||
| 8 | |||
| 9 | ;; This file is part of GNU Emacs. | ||
| 10 | |||
| 11 | ;; GNU Emacs is free software: you can redistribute it and/or modify | ||
| 12 | ;; it under the terms of the GNU General Public License as published by | ||
| 13 | ;; the Free Software Foundation, either version 3 of the License, or | ||
| 14 | ;; (at your option) any later version. | ||
| 15 | |||
| 16 | ;; GNU Emacs is distributed in the hope that it will be useful, | ||
| 17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 19 | ;; GNU General Public License for more details. | ||
| 20 | |||
| 21 | ;; You should have received a copy of the GNU General Public License | ||
| 22 | ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. | ||
| 23 | |||
| 24 | ;;; Commentary: | ||
| 25 | ;; Currently only supports the external execution. No session support yet. | ||
| 26 | |||
| 27 | ;;; Requirements: | ||
| 28 | ;; - Scala language :: http://www.scala-lang.org/ | ||
| 29 | ;; - Scala major mode :: Can be installed from Scala sources | ||
| 30 | ;; https://github.com/scala/scala-dist/blob/master/tool-support/src/emacs/scala-mode.el | ||
| 31 | |||
| 32 | ;;; Code: | ||
| 33 | (require 'ob) | ||
| 34 | |||
| 35 | (defvar org-babel-tangle-lang-exts) ;; Autoloaded | ||
| 36 | (add-to-list 'org-babel-tangle-lang-exts '("scala" . "scala")) | ||
| 37 | (defvar org-babel-default-header-args:scala '()) | ||
| 38 | (defvar org-babel-scala-command "scala" | ||
| 39 | "Name of the command to use for executing Scala code.") | ||
| 40 | |||
| 41 | (defun org-babel-execute:scala (body params) | ||
| 42 | "Execute a block of Scala code with org-babel. This function is | ||
| 43 | called by `org-babel-execute-src-block'" | ||
| 44 | (message "executing Scala source code block") | ||
| 45 | (let* ((processed-params (org-babel-process-params params)) | ||
| 46 | (session (org-babel-scala-initiate-session (nth 0 processed-params))) | ||
| 47 | (result-params (nth 2 processed-params)) | ||
| 48 | (result-type (cdr (assq :result-type params))) | ||
| 49 | (full-body (org-babel-expand-body:generic | ||
| 50 | body params)) | ||
| 51 | (result (org-babel-scala-evaluate | ||
| 52 | session full-body result-type result-params))) | ||
| 53 | |||
| 54 | (org-babel-reassemble-table | ||
| 55 | result | ||
| 56 | (org-babel-pick-name | ||
| 57 | (cdr (assq :colname-names params)) (cdr (assq :colnames params))) | ||
| 58 | (org-babel-pick-name | ||
| 59 | (cdr (assq :rowname-names params)) (cdr (assq :rownames params)))))) | ||
| 60 | |||
| 61 | (defvar org-babel-scala-wrapper-method | ||
| 62 | |||
| 63 | "var str_result :String = null; | ||
| 64 | |||
| 65 | Console.withOut(new java.io.OutputStream() {def write(b: Int){ | ||
| 66 | }}) { | ||
| 67 | str_result = { | ||
| 68 | %s | ||
| 69 | }.toString | ||
| 70 | } | ||
| 71 | |||
| 72 | print(str_result) | ||
| 73 | ") | ||
| 74 | |||
| 75 | |||
| 76 | (defun org-babel-scala-evaluate | ||
| 77 | (session body &optional result-type result-params) | ||
| 78 | "Evaluate BODY in external Scala process. | ||
| 79 | If RESULT-TYPE equals `output' then return standard output as a string. | ||
| 80 | If RESULT-TYPE equals `value' then return the value of the last statement | ||
| 81 | in BODY as elisp." | ||
| 82 | (when session (error "Sessions are not (yet) supported for Scala")) | ||
| 83 | (pcase result-type | ||
| 84 | (`output | ||
| 85 | (let ((src-file (org-babel-temp-file "scala-"))) | ||
| 86 | (with-temp-file src-file (insert body)) | ||
| 87 | (org-babel-eval | ||
| 88 | (concat org-babel-scala-command " " src-file) ""))) | ||
| 89 | (`value | ||
| 90 | (let* ((src-file (org-babel-temp-file "scala-")) | ||
| 91 | (wrapper (format org-babel-scala-wrapper-method body))) | ||
| 92 | (with-temp-file src-file (insert wrapper)) | ||
| 93 | (let ((raw (org-babel-eval | ||
| 94 | (concat org-babel-scala-command " " src-file) ""))) | ||
| 95 | (org-babel-result-cond result-params | ||
| 96 | raw | ||
| 97 | (org-babel-script-escape raw))))))) | ||
| 98 | |||
| 99 | |||
| 100 | (defun org-babel-prep-session:scala (_session _params) | ||
| 101 | "Prepare SESSION according to the header arguments specified in PARAMS." | ||
| 102 | (error "Sessions are not (yet) supported for Scala")) | ||
| 103 | |||
| 104 | (defun org-babel-scala-initiate-session (&optional _session) | ||
| 105 | "If there is not a current inferior-process-buffer in SESSION | ||
| 106 | then create. Return the initialized session. Sessions are not | ||
| 107 | supported in Scala." | ||
| 108 | nil) | ||
| 109 | |||
| 110 | (provide 'ob-scala) | ||
| 111 | |||
| 112 | |||
| 113 | |||
| 114 | ;;; ob-scala.el ends here | ||
diff --git a/lisp/org/ob-vala.el b/lisp/org/ob-vala.el new file mode 100644 index 00000000000..3998e2d4e28 --- /dev/null +++ b/lisp/org/ob-vala.el | |||
| @@ -0,0 +1,115 @@ | |||
| 1 | ;;; ob-vala.el --- Babel functions for Vala evaluation -*- lexical-binding: t; -*- | ||
| 2 | |||
| 3 | ;; Copyright (C) 2017 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | ;; Author: Christian Garbs <mitch@cgarbs.de> | ||
| 6 | ;; Keywords: literate programming, reproducible research | ||
| 7 | ;; Homepage: http://orgmode.org | ||
| 8 | |||
| 9 | ;;; License: | ||
| 10 | |||
| 11 | ;; GNU Emacs is free software: you can redistribute it and/or modify | ||
| 12 | ;; it under the terms of the GNU General Public License as published by | ||
| 13 | ;; the Free Software Foundation, either version 3 of the License, or | ||
| 14 | ;; (at your option) any later version. | ||
| 15 | |||
| 16 | ;; GNU Emacs is distributed in the hope that it will be useful, | ||
| 17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 19 | ;; GNU General Public License for more details. | ||
| 20 | |||
| 21 | ;; You should have received a copy of the GNU General Public License | ||
| 22 | ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. | ||
| 23 | |||
| 24 | ;;; Commentary: | ||
| 25 | |||
| 26 | ;; ob-vala.el provides Babel support for the Vala language | ||
| 27 | ;; (see http://live.gnome.org/Vala for details) | ||
| 28 | |||
| 29 | ;;; Requirements: | ||
| 30 | |||
| 31 | ;; - Vala compiler binary (valac) | ||
| 32 | ;; - Vala development environment (Vala libraries etc.) | ||
| 33 | ;; | ||
| 34 | ;; vala-mode.el is nice to have for code formatting, but is not needed | ||
| 35 | ;; for ob-vala.el | ||
| 36 | |||
| 37 | ;;; Code: | ||
| 38 | |||
| 39 | (require 'ob) | ||
| 40 | |||
| 41 | (declare-function org-trim "org" (s &optional keep-lead)) | ||
| 42 | |||
| 43 | ;; File extension. | ||
| 44 | (add-to-list 'org-babel-tangle-lang-exts '("vala" . "vala")) | ||
| 45 | |||
| 46 | ;; Header arguments empty by default. | ||
| 47 | (defvar org-babel-default-header-args:vala '()) | ||
| 48 | |||
| 49 | (defcustom org-babel-vala-compiler "valac" | ||
| 50 | "Command used to compile a C source code file into an executable. | ||
| 51 | May be either a command in the path, like \"valac\" | ||
| 52 | or an absolute path name, like \"/usr/local/bin/valac\". | ||
| 53 | Parameters may be used like this: \"valac -v\"" | ||
| 54 | :group 'org-babel | ||
| 55 | :version "26.1" | ||
| 56 | :package-version '(Org . "9.1") | ||
| 57 | :type 'string) | ||
| 58 | |||
| 59 | ;; This is the main function which is called to evaluate a code | ||
| 60 | ;; block. | ||
| 61 | ;; | ||
| 62 | ;; - run Vala compiler and create a binary in a temporary file | ||
| 63 | ;; - compiler/linker flags can be set via :flags header argument | ||
| 64 | ;; - if compilation succeeded, run the binary | ||
| 65 | ;; - commandline parameters to the binary can be set via :cmdline | ||
| 66 | ;; header argument | ||
| 67 | ;; - stdout will be parsed as RESULT (control via :result-params | ||
| 68 | ;; header argument) | ||
| 69 | ;; | ||
| 70 | ;; There is no session support because Vala is a compiled language. | ||
| 71 | ;; | ||
| 72 | ;; This function is heavily based on ob-C.el | ||
| 73 | (defun org-babel-execute:vala (body params) | ||
| 74 | "Execute a block of Vala code with Babel. | ||
| 75 | This function is called by `org-babel-execute-src-block'." | ||
| 76 | (message "executing Vala source code block") | ||
| 77 | (let* ((tmp-src-file (org-babel-temp-file | ||
| 78 | "vala-src-" | ||
| 79 | ".vala")) | ||
| 80 | (tmp-bin-file (org-babel-temp-file "vala-bin-" org-babel-exeext)) | ||
| 81 | (cmdline (cdr (assq :cmdline params))) | ||
| 82 | (flags (cdr (assq :flags params)))) | ||
| 83 | (with-temp-file tmp-src-file (insert body)) | ||
| 84 | (org-babel-eval | ||
| 85 | (format "%s %s -o %s %s" | ||
| 86 | org-babel-vala-compiler | ||
| 87 | (mapconcat #'identity | ||
| 88 | (if (listp flags) flags (list flags)) " ") | ||
| 89 | (org-babel-process-file-name tmp-bin-file) | ||
| 90 | (org-babel-process-file-name tmp-src-file)) "") | ||
| 91 | (when (file-executable-p tmp-bin-file) | ||
| 92 | (let ((results | ||
| 93 | (org-trim | ||
| 94 | (org-babel-eval | ||
| 95 | (concat tmp-bin-file (if cmdline (concat " " cmdline) "")) "")))) | ||
| 96 | (org-babel-reassemble-table | ||
| 97 | (org-babel-result-cond (cdr (assq :result-params params)) | ||
| 98 | (org-babel-read results) | ||
| 99 | (let ((tmp-file (org-babel-temp-file "vala-"))) | ||
| 100 | (with-temp-file tmp-file (insert results)) | ||
| 101 | (org-babel-import-elisp-from-file tmp-file))) | ||
| 102 | (org-babel-pick-name | ||
| 103 | (cdr (assq :colname-names params)) (cdr (assq :colnames params))) | ||
| 104 | (org-babel-pick-name | ||
| 105 | (cdr (assq :rowname-names params)) (cdr (assq :rownames params)))))))) | ||
| 106 | |||
| 107 | (defun org-babel-prep-session:vala (_session _params) | ||
| 108 | "Prepare a session. | ||
| 109 | This function does nothing as Vala is a compiled language with no | ||
| 110 | support for sessions." | ||
| 111 | (error "Vala is a compiled language -- no support for sessions")) | ||
| 112 | |||
| 113 | (provide 'ob-vala) | ||
| 114 | |||
| 115 | ;;; ob-vala.el ends here | ||
diff --git a/lisp/org/org-duration.el b/lisp/org/org-duration.el new file mode 100644 index 00000000000..3e5f0f56a5b --- /dev/null +++ b/lisp/org/org-duration.el | |||
| @@ -0,0 +1,446 @@ | |||
| 1 | ;;; org-duration.el --- Library handling durations -*- lexical-binding: t; -*- | ||
| 2 | |||
| 3 | ;; Copyright (C) 2017 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | ;; Author: Nicolas Goaziou <mail@nicolasgoaziou.fr> | ||
| 6 | ;; Keywords: outlines, hypermedia, calendar, wp | ||
| 7 | |||
| 8 | ;; This program is free software; you can redistribute it and/or modify | ||
| 9 | ;; it under the terms of the GNU General Public License as published by | ||
| 10 | ;; the Free Software Foundation, either version 3 of the License, or | ||
| 11 | ;; (at your option) any later version. | ||
| 12 | |||
| 13 | ;; This program is distributed in the hope that it will be useful, | ||
| 14 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | ;; GNU General Public License for more details. | ||
| 17 | |||
| 18 | ;; You should have received a copy of the GNU General Public License | ||
| 19 | ;; along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
| 20 | |||
| 21 | ;;; Commentary: | ||
| 22 | |||
| 23 | ;; This library provides tools to manipulate durations. A duration | ||
| 24 | ;; can have multiple formats: | ||
| 25 | ;; | ||
| 26 | ;; - 3:12 | ||
| 27 | ;; - 1:23:45 | ||
| 28 | ;; - 1y 3d 3h 4min | ||
| 29 | ;; - 3d 13:35 | ||
| 30 | ;; - 2.35h | ||
| 31 | ;; | ||
| 32 | ;; More accurately, it consists of numbers and units, as defined in | ||
| 33 | ;; variable `org-duration-units', separated with white spaces, and | ||
| 34 | ;; a "H:MM" or "H:MM:SS" part. White spaces are tolerated between the | ||
| 35 | ;; number and its relative unit. Variable `org-duration-format' | ||
| 36 | ;; controls durations default representation. | ||
| 37 | ;; | ||
| 38 | ;; The library provides functions allowing to convert a duration to, | ||
| 39 | ;; and from, a number of minutes: `org-duration-to-minutes' and | ||
| 40 | ;; `org-duration-from-minutes'. It also provides two lesser tools: | ||
| 41 | ;; `org-duration-p', and `org-duration-h:mm-only-p'. | ||
| 42 | ;; | ||
| 43 | ;; Users can set the number of minutes per unit, or define new units, | ||
| 44 | ;; in `org-duration-units'. The library also supports canonical | ||
| 45 | ;; duration, i.e., a duration that doesn't depend on user's settings, | ||
| 46 | ;; through optional arguments. | ||
| 47 | |||
| 48 | ;;; Code: | ||
| 49 | |||
| 50 | (require 'cl-lib) | ||
| 51 | (require 'org-macs) | ||
| 52 | (declare-function org-trim "org-trim" (s &optional keep-lead)) | ||
| 53 | |||
| 54 | |||
| 55 | ;;; Public variables | ||
| 56 | |||
| 57 | (defconst org-duration-canonical-units | ||
| 58 | `(("min" . 1) | ||
| 59 | ("h" . 60) | ||
| 60 | ("d" . ,(* 60 24))) | ||
| 61 | "Canonical time duration units. | ||
| 62 | See `org-duration-units' for details.") | ||
| 63 | |||
| 64 | (defcustom org-duration-units | ||
| 65 | `(("min" . 1) | ||
| 66 | ("h" . 60) | ||
| 67 | ("d" . ,(* 60 24)) | ||
| 68 | ("w" . ,(* 60 24 7)) | ||
| 69 | ("m" . ,(* 60 24 30)) | ||
| 70 | ("y" . ,(* 60 24 365.25))) | ||
| 71 | "Conversion factor to minutes for a duration. | ||
| 72 | |||
| 73 | Each entry has the form (UNIT . MODIFIER). | ||
| 74 | |||
| 75 | In a duration string, a number followed by UNIT is multiplied by | ||
| 76 | the specified number of MODIFIER to obtain a duration in minutes. | ||
| 77 | |||
| 78 | For example, the following value | ||
| 79 | |||
| 80 | \\=`((\"min\" . 1) | ||
| 81 | (\"h\" . 60) | ||
| 82 | (\"d\" . ,(* 60 8)) | ||
| 83 | (\"w\" . ,(* 60 8 5)) | ||
| 84 | (\"m\" . ,(* 60 8 5 4)) | ||
| 85 | (\"y\" . ,(* 60 8 5 4 10))) | ||
| 86 | |||
| 87 | is meaningful if you work an average of 8 hours per day, 5 days | ||
| 88 | a week, 4 weeks a month and 10 months a year. | ||
| 89 | |||
| 90 | When setting this variable outside the Customize interface, make | ||
| 91 | sure to call the following command: | ||
| 92 | |||
| 93 | \\[org-duration-set-regexps]" | ||
| 94 | :group 'org-agenda | ||
| 95 | :version "26.1" | ||
| 96 | :package-version '(Org . "9.1") | ||
| 97 | :set (lambda (var val) (set-default var val) (org-duration-set-regexps)) | ||
| 98 | :initialize 'custom-initialize-changed | ||
| 99 | :type '(choice | ||
| 100 | (const :tag "H:MM" 'h:mm) | ||
| 101 | (const :tag "H:MM:SS" 'h:mm:ss) | ||
| 102 | (alist :key-type (string :tag "Unit") | ||
| 103 | :value-type (number :tag "Modifier")))) | ||
| 104 | |||
| 105 | (defcustom org-duration-format '(("d" . nil) (special . h:mm)) | ||
| 106 | "Format definition for a duration. | ||
| 107 | |||
| 108 | The value can be set to, respectively, the symbols `h:mm:ss' or | ||
| 109 | `h:mm', which means a duration is expressed as, respectively, | ||
| 110 | a \"H:MM:SS\" or \"H:MM\" string. | ||
| 111 | |||
| 112 | Alternatively, the value can be a list of entries following the | ||
| 113 | pattern: | ||
| 114 | |||
| 115 | (UNIT . REQUIRED?) | ||
| 116 | |||
| 117 | UNIT is a unit string, as defined in `org-duration-units'. The | ||
| 118 | time duration is formatted using only the time components that | ||
| 119 | are specified here. | ||
| 120 | |||
| 121 | Units with a zero value are skipped, unless REQUIRED? is non-nil. | ||
| 122 | In that case, the unit is always used. | ||
| 123 | |||
| 124 | Eventually, the list can contain one of the following special | ||
| 125 | entries: | ||
| 126 | |||
| 127 | (special . h:mm) | ||
| 128 | (special . h:mm:ss) | ||
| 129 | |||
| 130 | Units shorter than an hour are ignored. The hours and | ||
| 131 | minutes part of the duration is expressed unconditionally | ||
| 132 | with H:MM, or H:MM:SS, pattern. | ||
| 133 | |||
| 134 | (special . PRECISION) | ||
| 135 | |||
| 136 | A duration is expressed with a single unit, PRECISION being | ||
| 137 | the number of decimal places to show. The unit chosen is the | ||
| 138 | first one required or with a non-zero integer part. If there | ||
| 139 | is no such unit, the smallest one is used. | ||
| 140 | |||
| 141 | For example, | ||
| 142 | |||
| 143 | ((\"d\" . nil) (\"h\" . t) (\"min\" . t)) | ||
| 144 | |||
| 145 | means a duration longer than a day is expressed in days, hours | ||
| 146 | and minutes, whereas a duration shorter than a day is always | ||
| 147 | expressed in hours and minutes, even when shorter than an hour. | ||
| 148 | |||
| 149 | On the other hand, the value | ||
| 150 | |||
| 151 | ((\"d\" . nil) (\"min\" . nil)) | ||
| 152 | |||
| 153 | means a duration longer than a day is expressed in days and | ||
| 154 | minutes, whereas a duration shorter than a day is expressed | ||
| 155 | entirely in minutes, even when longer than an hour. | ||
| 156 | |||
| 157 | The following format | ||
| 158 | |||
| 159 | ((\"d\" . nil) (special . h:mm)) | ||
| 160 | |||
| 161 | means that any duration longer than a day is expressed with both | ||
| 162 | a \"d\" unit and a \"H:MM\" part, whereas a duration shorter than | ||
| 163 | a day is expressed only as a \"H:MM\" string. | ||
| 164 | |||
| 165 | Eventually, | ||
| 166 | |||
| 167 | ((\"d\" . nil) (\"h\" . nil) (special . 2)) | ||
| 168 | |||
| 169 | expresses a duration longer than a day as a decimal number, with | ||
| 170 | a 2-digits fractional part, of \"d\" unit. A duration shorter | ||
| 171 | than a day uses \"h\" unit instead." | ||
| 172 | :group 'org-time | ||
| 173 | :group 'org-clock | ||
| 174 | :version "26.1" | ||
| 175 | :package-version '(Org . "9.1") | ||
| 176 | :type '(choice | ||
| 177 | (const :tag "Use H:MM" h:mm) | ||
| 178 | (const :tag "Use H:MM:SS" h:mm:ss) | ||
| 179 | (repeat :tag "Use units" | ||
| 180 | (choice | ||
| 181 | (cons :tag "Use units" | ||
| 182 | (string :tag "Unit") | ||
| 183 | (choice (const :tag "Skip when zero" nil) | ||
| 184 | (const :tag "Always used" t))) | ||
| 185 | (cons :tag "Use a single decimal unit" | ||
| 186 | (const special) | ||
| 187 | (integer :tag "Number of decimals")) | ||
| 188 | (cons :tag "Use both units and H:MM" | ||
| 189 | (const special) | ||
| 190 | (const h:mm)) | ||
| 191 | (cons :tag "Use both units and H:MM:SS" | ||
| 192 | (const special) | ||
| 193 | (const h:mm:ss)))))) | ||
| 194 | |||
| 195 | |||
| 196 | ;;; Internal variables and functions | ||
| 197 | |||
| 198 | (defconst org-duration--h:mm-re | ||
| 199 | "\\`[ \t]*[0-9]+\\(?::[0-9]\\{2\\}\\)\\{1,2\\}[ \t]*\\'" | ||
| 200 | "Regexp matching a duration expressed with H:MM or H:MM:SS format. | ||
| 201 | See `org-duration--h:mm:ss-re' to only match the latter. Hours | ||
| 202 | can use any number of digits.") | ||
| 203 | |||
| 204 | (defconst org-duration--h:mm:ss-re | ||
| 205 | "\\`[ \t]*[0-9]+\\(?::[0-9]\\{2\\}\\)\\{2\\}[ \t]*\\'" | ||
| 206 | "Regexp matching a duration expressed H:MM:SS format. | ||
| 207 | See `org-duration--h:mm-re' to also support H:MM format. Hours | ||
| 208 | can use any number of digits.") | ||
| 209 | |||
| 210 | (defvar org-duration--unit-re nil | ||
| 211 | "Regexp matching a duration with an unit. | ||
| 212 | Allowed units are defined in `org-duration-units'. Match group | ||
| 213 | 1 contains the bare number. Match group 2 contains the unit.") | ||
| 214 | |||
| 215 | (defvar org-duration--full-re nil | ||
| 216 | "Regexp matching a duration expressed with units. | ||
| 217 | Allowed units are defined in `org-duration-units'.") | ||
| 218 | |||
| 219 | (defvar org-duration--mixed-re nil | ||
| 220 | "Regexp matching a duration expressed with units and H:MM or H:MM:SS format. | ||
| 221 | Allowed units are defined in `org-duration-units'. Match group | ||
| 222 | 1 contains units part. Match group 2 contains H:MM or H:MM:SS | ||
| 223 | part.") | ||
| 224 | |||
| 225 | (defun org-duration--modifier (unit &optional canonical) | ||
| 226 | "Return modifier associated to string UNIT. | ||
| 227 | When optional argument CANONICAL is non-nil, refer to | ||
| 228 | `org-duration-canonical-units' instead of `org-duration-units'." | ||
| 229 | (or (cdr (assoc unit (if canonical | ||
| 230 | org-duration-canonical-units | ||
| 231 | org-duration-units))) | ||
| 232 | (error "Unknown unit: %S" unit))) | ||
| 233 | |||
| 234 | |||
| 235 | ;;; Public functions | ||
| 236 | |||
| 237 | ;;;###autoload | ||
| 238 | (defun org-duration-set-regexps () | ||
| 239 | "Set duration related regexps." | ||
| 240 | (interactive) | ||
| 241 | (setq org-duration--unit-re | ||
| 242 | (concat "\\([0-9]+\\(?:\\.[0-9]*\\)?\\)[ \t]*" | ||
| 243 | ;; Since user-defined units in `org-duration-units' | ||
| 244 | ;; can differ from canonical units in | ||
| 245 | ;; `org-duration-canonical-units', include both in | ||
| 246 | ;; regexp. | ||
| 247 | (regexp-opt (mapcar #'car (append org-duration-canonical-units | ||
| 248 | org-duration-units)) | ||
| 249 | t))) | ||
| 250 | (setq org-duration--full-re | ||
| 251 | (format "\\`[ \t]*%s\\(?:[ \t]+%s\\)*[ \t]*\\'" | ||
| 252 | org-duration--unit-re | ||
| 253 | org-duration--unit-re)) | ||
| 254 | (setq org-duration--mixed-re | ||
| 255 | (format "\\`[ \t]*\\(?1:%s\\(?:[ \t]+%s\\)*\\)[ \t]+\ | ||
| 256 | \\(?2:[0-9]+\\(?::[0-9][0-9]\\)\\{1,2\\}\\)[ \t]*\\'" | ||
| 257 | org-duration--unit-re | ||
| 258 | org-duration--unit-re))) | ||
| 259 | |||
| 260 | ;;;###autoload | ||
| 261 | (defun org-duration-p (s) | ||
| 262 | "Non-nil when string S is a time duration." | ||
| 263 | (and (stringp s) | ||
| 264 | (or (string-match-p org-duration--full-re s) | ||
| 265 | (string-match-p org-duration--mixed-re s) | ||
| 266 | (string-match-p org-duration--h:mm-re s)))) | ||
| 267 | |||
| 268 | ;;;###autoload | ||
| 269 | (defun org-duration-to-minutes (duration &optional canonical) | ||
| 270 | "Return number of minutes of DURATION string. | ||
| 271 | |||
| 272 | When optional argument CANONICAL is non-nil, ignore | ||
| 273 | `org-duration-units' and use standard time units value. | ||
| 274 | |||
| 275 | A bare number is translated into minutes. The empty string is | ||
| 276 | translated into 0.0. | ||
| 277 | |||
| 278 | Return value as a float. Raise an error if duration format is | ||
| 279 | not recognized." | ||
| 280 | (cond | ||
| 281 | ((equal duration "") 0.0) | ||
| 282 | ((numberp duration) (float duration)) | ||
| 283 | ((string-match-p org-duration--h:mm-re duration) | ||
| 284 | (pcase-let ((`(,hours ,minutes ,seconds) | ||
| 285 | (mapcar #'string-to-number (split-string duration ":")))) | ||
| 286 | (+ (/ (or seconds 0) 60.0) minutes (* 60 hours)))) | ||
| 287 | ((string-match-p org-duration--full-re duration) | ||
| 288 | (let ((minutes 0) | ||
| 289 | (s 0)) | ||
| 290 | (while (string-match org-duration--unit-re duration s) | ||
| 291 | (setq s (match-end 0)) | ||
| 292 | (let ((value (string-to-number (match-string 1 duration))) | ||
| 293 | (unit (match-string 2 duration))) | ||
| 294 | (cl-incf minutes (* value (org-duration--modifier unit canonical))))) | ||
| 295 | (float minutes))) | ||
| 296 | ((string-match org-duration--mixed-re duration) | ||
| 297 | (let ((units-part (match-string 1 duration)) | ||
| 298 | (hms-part (match-string 2 duration))) | ||
| 299 | (+ (org-duration-to-minutes units-part) | ||
| 300 | (org-duration-to-minutes hms-part)))) | ||
| 301 | ((string-match-p "\\`[0-9]+\\(\\.[0-9]*\\)?\\'" duration) | ||
| 302 | (float (string-to-number duration))) | ||
| 303 | (t (error "Invalid duration format: %S" duration)))) | ||
| 304 | |||
| 305 | ;;;###autoload | ||
| 306 | (defun org-duration-from-minutes (minutes &optional fmt canonical) | ||
| 307 | "Return duration string for a given number of MINUTES. | ||
| 308 | |||
| 309 | Format duration according to `org-duration-format' or FMT, when | ||
| 310 | non-nil. | ||
| 311 | |||
| 312 | When optional argument CANONICAL is non-nil, ignore | ||
| 313 | `org-duration-units' and use standard time units value. | ||
| 314 | |||
| 315 | Raise an error if expected format is unknown." | ||
| 316 | (pcase (or fmt org-duration-format) | ||
| 317 | (`h:mm | ||
| 318 | (let ((minutes (floor minutes))) | ||
| 319 | (format "%d:%02d" (/ minutes 60) (mod minutes 60)))) | ||
| 320 | (`h:mm:ss | ||
| 321 | (let* ((whole-minutes (floor minutes)) | ||
| 322 | (seconds (floor (* 60 (- minutes whole-minutes))))) | ||
| 323 | (format "%s:%02d" | ||
| 324 | (org-duration-from-minutes whole-minutes 'h:mm) | ||
| 325 | seconds))) | ||
| 326 | ((pred atom) (error "Invalid duration format specification: %S" fmt)) | ||
| 327 | ;; Mixed format. Call recursively the function on both parts. | ||
| 328 | ((and duration-format | ||
| 329 | (let `(special . ,(and mode (or `h:mm:ss `h:mm))) | ||
| 330 | (assq 'special duration-format))) | ||
| 331 | (let* ((truncated-format | ||
| 332 | ;; Remove "special" mode from duration format in order to | ||
| 333 | ;; recurse properly. Also remove units smaller or equal | ||
| 334 | ;; to an hour since H:MM part takes care of it. | ||
| 335 | (cl-remove-if-not | ||
| 336 | (lambda (pair) | ||
| 337 | (pcase pair | ||
| 338 | (`(,(and unit (pred stringp)) . ,_) | ||
| 339 | (> (org-duration--modifier unit canonical) 60)) | ||
| 340 | (_ nil))) | ||
| 341 | duration-format)) | ||
| 342 | (min-modifier ;smallest modifier above hour | ||
| 343 | (and truncated-format | ||
| 344 | (apply #'min | ||
| 345 | (mapcar (lambda (p) | ||
| 346 | (org-duration--modifier (car p) canonical)) | ||
| 347 | truncated-format))))) | ||
| 348 | (if (or (null min-modifier) (< minutes min-modifier)) | ||
| 349 | ;; There is not unit above the hour or the smallest unit | ||
| 350 | ;; above the hour is too large for the number of minutes we | ||
| 351 | ;; need to represent. Use H:MM or H:MM:SS syntax. | ||
| 352 | (org-duration-from-minutes minutes mode canonical) | ||
| 353 | ;; Represent minutes above hour using provided units and H:MM | ||
| 354 | ;; or H:MM:SS below. | ||
| 355 | (let* ((units-part (* min-modifier (/ (floor minutes) min-modifier))) | ||
| 356 | (minutes-part (- minutes units-part))) | ||
| 357 | (concat | ||
| 358 | (org-duration-from-minutes units-part truncated-format canonical) | ||
| 359 | " " | ||
| 360 | (org-duration-from-minutes minutes-part mode)))))) | ||
| 361 | ;; Units format. | ||
| 362 | (duration-format | ||
| 363 | (let* ((fractional | ||
| 364 | (let ((digits (cdr (assq 'special duration-format)))) | ||
| 365 | (and digits | ||
| 366 | (or (wholenump digits) | ||
| 367 | (error "Unknown formatting directive: %S" digits)) | ||
| 368 | (format "%%.%df" digits)))) | ||
| 369 | (selected-units | ||
| 370 | (sort (cl-remove-if | ||
| 371 | ;; Ignore special format cells. | ||
| 372 | (lambda (pair) (pcase pair (`(special . ,_) t) (_ nil))) | ||
| 373 | duration-format) | ||
| 374 | (lambda (a b) | ||
| 375 | (> (org-duration--modifier (car a) canonical) | ||
| 376 | (org-duration--modifier (car b) canonical)))))) | ||
| 377 | (cond | ||
| 378 | ;; Fractional duration: use first unit that is either required | ||
| 379 | ;; or smaller than MINUTES. | ||
| 380 | (fractional | ||
| 381 | (let* ((unit (car | ||
| 382 | (or (cl-find-if | ||
| 383 | (lambda (pair) | ||
| 384 | (pcase pair | ||
| 385 | (`(,u . ,req?) | ||
| 386 | (or req? | ||
| 387 | (<= (org-duration--modifier u canonical) | ||
| 388 | minutes))))) | ||
| 389 | selected-units) | ||
| 390 | ;; Fall back to smallest unit. | ||
| 391 | (org-last selected-units)))) | ||
| 392 | (modifier (org-duration--modifier unit canonical))) | ||
| 393 | (concat (format fractional (/ (float minutes) modifier)) unit))) | ||
| 394 | ;; Otherwise build duration string according to available | ||
| 395 | ;; units. | ||
| 396 | ((org-string-nw-p | ||
| 397 | (org-trim | ||
| 398 | (mapconcat | ||
| 399 | (lambda (units) | ||
| 400 | (pcase-let* ((`(,unit . ,required?) units) | ||
| 401 | (modifier (org-duration--modifier unit canonical))) | ||
| 402 | (cond ((<= modifier minutes) | ||
| 403 | (let ((value (if (integerp modifier) | ||
| 404 | (/ (floor minutes) modifier) | ||
| 405 | (floor (/ minutes modifier))))) | ||
| 406 | (cl-decf minutes (* value modifier)) | ||
| 407 | (format " %d%s" value unit))) | ||
| 408 | (required? (concat " 0" unit)) | ||
| 409 | (t "")))) | ||
| 410 | selected-units | ||
| 411 | "")))) | ||
| 412 | ;; No unit can properly represent MINUTES. Use the smallest | ||
| 413 | ;; one anyway. | ||
| 414 | (t | ||
| 415 | (pcase-let ((`((,unit . ,_)) (last selected-units))) | ||
| 416 | (concat "0" unit)))))))) | ||
| 417 | |||
| 418 | ;;;###autoload | ||
| 419 | (defun org-duration-h:mm-only-p (times) | ||
| 420 | "Non-nil when every duration in TIMES has \"H:MM\" or \"H:MM:SS\" format. | ||
| 421 | |||
| 422 | TIMES is a list of duration strings. | ||
| 423 | |||
| 424 | Return nil if any duration is expressed with units, as defined in | ||
| 425 | `org-duration-units'. Otherwise, if any duration is expressed | ||
| 426 | with \"H:MM:SS\" format, return `h:mm:ss'. Otherwise, return | ||
| 427 | `h:mm'." | ||
| 428 | (let (hms-flag) | ||
| 429 | (catch :exit | ||
| 430 | (dolist (time times) | ||
| 431 | (cond ((string-match-p org-duration--full-re time) | ||
| 432 | (throw :exit nil)) | ||
| 433 | ((string-match-p org-duration--mixed-re time) | ||
| 434 | (throw :exit nil)) | ||
| 435 | (hms-flag nil) | ||
| 436 | ((string-match-p org-duration--h:mm:ss-re time) | ||
| 437 | (setq hms-flag 'h:mm:ss)))) | ||
| 438 | (or hms-flag 'h:mm)))) | ||
| 439 | |||
| 440 | |||
| 441 | ;;; Initialization | ||
| 442 | |||
| 443 | (org-duration-set-regexps) | ||
| 444 | |||
| 445 | (provide 'org-duration) | ||
| 446 | ;;; org-duration.el ends here | ||