org-pcomplete.el (16523B)
1 ;;; org-pcomplete.el --- In-buffer Completion Code -*- lexical-binding: t; -*- 2 3 ;; Copyright (C) 2004-2023 Free Software Foundation, Inc. 4 ;; 5 ;; Author: Carsten Dominik <carsten.dominik@gmail.com> 6 ;; John Wiegley <johnw at gnu dot org> 7 ;; Keywords: outlines, hypermedia, calendar, wp 8 ;; URL: https://orgmode.org 9 ;; 10 ;; This file is part of GNU Emacs. 11 ;; 12 ;; GNU Emacs is free software: you can redistribute it and/or modify 13 ;; it under the terms of the GNU General Public License as published by 14 ;; the Free Software Foundation, either version 3 of the License, or 15 ;; (at your option) any later version. 16 17 ;; GNU Emacs is distributed in the hope that it will be useful, 18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 ;; GNU General Public License for more details. 21 22 ;; You should have received a copy of the GNU General Public License 23 ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. 24 25 ;;; Code: 26 27 ;;;; Require other packages 28 29 (require 'org-macs) 30 (org-assert-version) 31 32 (require 'org-macs) 33 (require 'org-compat) 34 (require 'pcomplete) 35 36 (declare-function org-at-heading-p "org" (&optional ignored)) 37 (declare-function org-babel-combine-header-arg-lists "ob-core" (original &rest others)) 38 (declare-function org-babel-get-src-block-info "ob-core" (&optional no-eval datum)) 39 (declare-function org-before-first-heading-p "org" ()) 40 (declare-function org-buffer-property-keys "org" (&optional specials defaults columns)) 41 (declare-function org-element-at-point "org-element" (&optional pom cached-only)) 42 (declare-function org-element-property "org-element" property element) 43 (declare-function org-element-type "org-element" (element)) 44 (declare-function org-end-of-meta-data "org" (&optional full)) 45 (declare-function org-entry-properties "org" (&optional pom which)) 46 (declare-function org-export-backend-options "ox" (cl-x) t) 47 (declare-function org-get-buffer-tags "org" ()) 48 (declare-function org-get-export-keywords "org" ()) 49 (declare-function org-get-heading "org" (&optional no-tags no-todo no-priority no-comment)) 50 (declare-function org-get-tags "org" (&optional pos local)) 51 (declare-function org-link-heading-search-string "ol" (&optional string)) 52 (declare-function org-tag-alist-to-string "org" (alist &optional skip-key)) 53 (declare-function org-time-stamp-format "org" (&optional with-time inactive custom)) 54 55 (defvar org-babel-common-header-args-w-values) 56 (defvar org-current-tag-alist) 57 (defvar org-priority-default) 58 (defvar org-drawer-regexp) 59 (defvar org-element-affiliated-keywords) 60 (defvar org-entities) 61 (defvar org-export-default-language) 62 (defvar org-export-exclude-tags) 63 (defvar org-export-select-tags) 64 (defvar org-file-tags) 65 (defvar org-priority-highest) 66 (defvar org-link-abbrev-alist) 67 (defvar org-link-abbrev-alist-local) 68 (defvar org-priority-lowest) 69 (defvar org-options-keywords) 70 (defvar org-outline-regexp) 71 (defvar org-property-re) 72 (defvar org-startup-options) 73 (defvar org-tag-re) 74 (defvar org-todo-keywords-1) 75 (defvar org-todo-line-regexp) 76 77 78 ;;; Internal Functions 79 80 (defun org-thing-at-point () 81 "Examine the thing at point and let the caller know what it is. 82 The return value is a string naming the thing at point." 83 (let ((line-to-here (org-current-line-string t)) 84 (case-fold-search t)) 85 (cond 86 ;; Parameters on a clock table opening line. 87 ((org-match-line "[ \t]*#\\+BEGIN: clocktable[ \t]") 88 (cons "block-option" "clocktable")) 89 ;; Flags and parameters on a source block opening line. 90 ((org-match-line "[ \t]*#\\+BEGIN_SRC[ \t]") 91 (cons "block-option" "src")) 92 ;; Value for a known keyword. 93 ((org-match-line "[ \t]*#\\+\\(\\S-+\\):") 94 (cons "file-option" (match-string-no-properties 1))) 95 ;; Keyword name. 96 ((and (org-match-line "[ \t]*#\\+[a-zA-Z_]*$") 97 (looking-at-p "[ \t]*$")) 98 (cons "file-option" nil)) 99 ;; Link abbreviation. 100 ((save-excursion 101 (skip-chars-backward "-A-Za-z0-9_") 102 (and (eq ?\[ (char-before)) 103 (eq ?\[ (char-before (1- (point)))))) 104 (cons "link" nil)) 105 ;; Entities. Some of them accept numbers, but only at their end. 106 ;; So, we first skip numbers, then letters. 107 ((eq ?\\ (save-excursion 108 (skip-chars-backward "0-9") 109 (skip-chars-backward "a-zA-Z") 110 (char-before))) 111 (cons "tex" nil)) 112 ;; Tags on a headline. 113 ((and (org-match-line 114 (format "\\*+ \\(?:.+? \\)?\\(:\\)\\(\\(?::\\|%s\\)+\\)?[ \t]*$" 115 org-tag-re)) 116 (or (org-point-in-group (point) 2) 117 (= (point) (match-end 1)))) 118 (cons "tag" nil)) 119 ;; TODO keywords on an empty headline. 120 ((and (string-match "^\\*+ +\\S-*$" line-to-here) 121 (looking-at-p "[ \t]*$")) 122 (cons "todo" nil)) 123 ;; Heading after a star for search strings or links. 124 ((save-excursion 125 (skip-chars-backward "^*" (line-beginning-position)) 126 (and (eq ?* (char-before)) 127 (eq (char-before (1- (point))) '?\[) 128 (eq (char-before (- (point) 2)) '?\[))) 129 (cons "searchhead" nil)) 130 ;; Property or drawer name, depending on point. If point is at 131 ;; a valid location for a node property, offer completion on all 132 ;; node properties in the buffer. Otherwise, offer completion on 133 ;; all drawer names, including "PROPERTIES". 134 ((and (string-match "^[ \t]*:\\S-*$" line-to-here) 135 (looking-at-p "[ \t]*$")) 136 (let ((origin (line-beginning-position))) 137 (if (org-before-first-heading-p) (cons "drawer" nil) 138 (save-excursion 139 (org-end-of-meta-data) 140 (if (or (= origin (point)) 141 (not (org-match-line "[ \t]*:PROPERTIES:[ \t]*$"))) 142 (cons "drawer" nil) 143 (while (org-match-line org-property-re) 144 (forward-line)) 145 (if (= origin (point)) (cons "prop" nil) 146 (cons "drawer" nil))))))) 147 (t nil)))) 148 149 (defun org-pcomplete-case-double (list) 150 "Return list with both upcase and downcase version of all strings in LIST." 151 (let (e res) 152 (while (setq e (pop list)) 153 (setq res (cons (downcase e) (cons (upcase e) res)))) 154 (nreverse res))) 155 156 157 ;;; Completion API 158 159 (defun org-command-at-point () 160 "Return the qualified name of the Org completion entity at point. 161 When completing for #+STARTUP, for example, this function returns 162 \"file-option/startup\"." 163 (let ((thing (org-thing-at-point))) 164 (cond 165 ((string= "file-option" (car thing)) 166 (concat (car thing) 167 (and (cdr thing) (concat "/" (downcase (cdr thing)))))) 168 ((string= "block-option" (car thing)) 169 (concat (car thing) "/" (downcase (cdr thing)))) 170 (t (car thing))))) 171 172 (defun org-parse-arguments () 173 "Parse whitespace separated arguments in the current region." 174 (let ((begin (line-beginning-position)) 175 (end (line-end-position)) 176 begins args) 177 (save-restriction 178 (narrow-to-region begin end) 179 (save-excursion 180 (goto-char (point-min)) 181 (while (not (eobp)) 182 (skip-chars-forward " \t\n[") 183 (setq begins (cons (point) begins)) 184 (skip-chars-forward "^ \t\n[") 185 (setq args (cons (buffer-substring-no-properties 186 (car begins) (point)) 187 args))) 188 (cons (reverse args) (reverse begins)))))) 189 190 (defun org-pcomplete-initial () 191 "Call the right completion function for first argument completions." 192 (ignore 193 (funcall (or (pcomplete-find-completion-function 194 (car (org-thing-at-point))) 195 pcomplete-default-completion-function)))) 196 197 198 ;;; Completion functions 199 200 (defun pcomplete/org-mode/file-option () 201 "Complete against all valid file options." 202 (require 'org-element) 203 (pcomplete-here 204 (org-pcomplete-case-double 205 (append (mapcar (lambda (keyword) (concat keyword " ")) 206 org-options-keywords) 207 (mapcar (lambda (keyword) (concat keyword ": ")) 208 org-element-affiliated-keywords) 209 (let (block-names) 210 (dolist (name 211 '("CENTER" "COMMENT" "EXAMPLE" "EXPORT" "QUOTE" "SRC" 212 "VERSE") 213 block-names) 214 (push (format "END_%s" name) block-names) 215 (push (concat "BEGIN_" 216 name 217 ;; Since language is compulsory in 218 ;; export blocks source blocks, add 219 ;; a space. 220 (and (member name '("EXPORT" "SRC")) " ")) 221 block-names) 222 (push (format "ATTR_%s: " name) block-names))) 223 (mapcar (lambda (keyword) (concat keyword ": ")) 224 (org-get-export-keywords)))) 225 (substring pcomplete-stub 2))) 226 227 (defun pcomplete/org-mode/file-option/author () 228 "Complete arguments for the #+AUTHOR file option." 229 (pcomplete-here (list user-full-name))) 230 231 (defun pcomplete/org-mode/file-option/date () 232 "Complete arguments for the #+DATE file option." 233 (pcomplete-here (list (format-time-string (org-time-stamp-format))))) 234 235 (defun pcomplete/org-mode/file-option/email () 236 "Complete arguments for the #+EMAIL file option." 237 (pcomplete-here (list user-mail-address))) 238 239 (defun pcomplete/org-mode/file-option/exclude_tags () 240 "Complete arguments for the #+EXCLUDE_TAGS file option." 241 (require 'ox) 242 (pcomplete-here 243 (and org-export-exclude-tags 244 (list (mapconcat #'identity org-export-exclude-tags " "))))) 245 246 (defun pcomplete/org-mode/file-option/filetags () 247 "Complete arguments for the #+FILETAGS file option." 248 (pcomplete-here (and org-file-tags (mapconcat #'identity org-file-tags " ")))) 249 250 (defun pcomplete/org-mode/file-option/language () 251 "Complete arguments for the #+LANGUAGE file option." 252 (require 'ox) 253 (pcomplete-here 254 (pcomplete-uniquify-list 255 (list org-export-default-language "en")))) 256 257 (defun pcomplete/org-mode/file-option/priorities () 258 "Complete arguments for the #+PRIORITIES file option." 259 (pcomplete-here (list (format "%c %c %c" 260 org-priority-highest 261 org-priority-lowest 262 org-priority-default)))) 263 264 (defun pcomplete/org-mode/file-option/select_tags () 265 "Complete arguments for the #+SELECT_TAGS file option." 266 (require 'ox) 267 (pcomplete-here 268 (and org-export-select-tags 269 (list (mapconcat #'identity org-export-select-tags " "))))) 270 271 (defun pcomplete/org-mode/file-option/startup () 272 "Complete arguments for the #+STARTUP file option." 273 (while (pcomplete-here 274 (let ((opts (pcomplete-uniquify-list 275 (mapcar #'car org-startup-options)))) 276 ;; Some options are mutually exclusive, and shouldn't be completed 277 ;; against if certain other options have already been seen. 278 (dolist (arg pcomplete-args) 279 (cond 280 ((string= arg "hidestars") 281 (setq opts (delete "showstars" opts))))) 282 opts)))) 283 284 (defun pcomplete/org-mode/file-option/tags () 285 "Complete arguments for the #+TAGS file option." 286 (pcomplete-here 287 (list (org-tag-alist-to-string org-current-tag-alist)))) 288 289 (defun pcomplete/org-mode/file-option/title () 290 "Complete arguments for the #+TITLE file option." 291 (pcomplete-here 292 (let ((visited-file (buffer-file-name (buffer-base-buffer)))) 293 (list (or (and visited-file 294 (file-name-sans-extension 295 (file-name-nondirectory visited-file))) 296 (buffer-name (buffer-base-buffer))))))) 297 298 299 (defun pcomplete/org-mode/file-option/options () 300 "Complete arguments for the #+OPTIONS file option." 301 (while (pcomplete-here 302 (pcomplete-uniquify-list 303 (append 304 ;; Hard-coded OPTION items always available. 305 '("H:" "\\n:" "num:" "timestamp:" "arch:" "author:" "c:" 306 "creator:" "date:" "d:" "email:" "*:" "e:" "::" "f:" 307 "inline:" "tex:" "p:" "pri:" "':" "-:" "stat:" "^:" "toc:" 308 "|:" "tags:" "tasks:" "<:" "todo:") 309 ;; OPTION items from registered back-ends. 310 (let (items) 311 (dolist (backend (bound-and-true-p 312 org-export-registered-backends)) 313 (dolist (option (org-export-backend-options backend)) 314 (let ((item (nth 2 option))) 315 (when item (push (concat item ":") items))))) 316 items)))))) 317 318 (defun pcomplete/org-mode/file-option/infojs_opt () 319 "Complete arguments for the #+INFOJS_OPT file option." 320 (while (pcomplete-here 321 (pcomplete-uniquify-list 322 (mapcar (lambda (item) (format "%s:" (car item))) 323 (bound-and-true-p org-html-infojs-opts-table)))))) 324 325 (defun pcomplete/org-mode/file-option/bind () 326 "Complete arguments for the #+BIND file option, which are variable names." 327 (let (vars) 328 (mapatoms 329 (lambda (a) (when (boundp a) (setq vars (cons (symbol-name a) vars))))) 330 (pcomplete-here vars))) 331 332 (defun pcomplete/org-mode/link () 333 "Complete against defined #+LINK patterns." 334 (pcomplete-here 335 (pcomplete-uniquify-list 336 (copy-sequence 337 (mapcar (lambda (e) (concat (car e) ":")) 338 (append org-link-abbrev-alist-local 339 org-link-abbrev-alist)))))) 340 341 (defun pcomplete/org-mode/tex () 342 "Complete against TeX-style HTML entity names." 343 (require 'org-entities) 344 (while (pcomplete-here 345 (pcomplete-uniquify-list 346 (remove nil (mapcar #'car-safe org-entities))) 347 (substring pcomplete-stub 1)))) 348 349 (defun pcomplete/org-mode/todo () 350 "Complete against known TODO keywords." 351 (pcomplete-here (pcomplete-uniquify-list (copy-sequence org-todo-keywords-1)))) 352 353 (defun pcomplete/org-mode/searchhead () 354 "Complete against all headings. 355 This needs more work, to handle headings with lots of spaces in them." 356 (while (pcomplete-here 357 (save-excursion 358 (goto-char (point-min)) 359 (let (tbl) 360 (while (re-search-forward org-outline-regexp nil t) 361 ;; Remove the leading asterisk from 362 ;; `org-link-heading-search-string' result. 363 (push (substring (org-link-heading-search-string) 1) tbl)) 364 (pcomplete-uniquify-list tbl))) 365 ;; When completing a bracketed link, i.e., "[[*", argument 366 ;; starts at the star, so remove this character. 367 ;; Also, if the completion is done inside [[*head<point>]], 368 ;; drop the closing parentheses. 369 (replace-regexp-in-string 370 "\\]+$" "" 371 (substring pcomplete-stub 1))))) 372 373 (defun pcomplete/org-mode/tag () 374 "Complete a tag name. Omit tags already set." 375 (while (pcomplete-here 376 (mapcar (lambda (x) (concat x ":")) 377 (let ((lst (pcomplete-uniquify-list 378 (or (remq 379 nil 380 (mapcar (lambda (x) (org-string-nw-p (car x))) 381 org-current-tag-alist)) 382 (mapcar #'car (org-get-buffer-tags)))))) 383 (dolist (tag (org-get-tags nil t)) 384 (setq lst (delete tag lst))) 385 lst)) 386 (and (string-match ".*:" pcomplete-stub) 387 (substring pcomplete-stub (match-end 0))) 388 t))) 389 390 (defun pcomplete/org-mode/drawer () 391 "Complete a drawer name, including \"PROPERTIES\"." 392 (pcomplete-here 393 (org-pcomplete-case-double 394 (mapcar (lambda (x) (concat x ":")) 395 (let ((names (list "PROPERTIES"))) 396 (save-excursion 397 (goto-char (point-min)) 398 (while (re-search-forward org-drawer-regexp nil t) 399 (let ((drawer (org-element-at-point))) 400 (when (memq (org-element-type drawer) 401 '(drawer property-drawer)) 402 (push (org-element-property :drawer-name drawer) names) 403 (goto-char (org-element-property :end drawer)))))) 404 (pcomplete-uniquify-list names)))) 405 (substring pcomplete-stub 1))) ;remove initial colon 406 407 (defun pcomplete/org-mode/prop () 408 "Complete a property name. Omit properties already set." 409 (pcomplete-here 410 (org-pcomplete-case-double 411 (mapcar (lambda (x) 412 (concat x ": ")) 413 (let ((lst (pcomplete-uniquify-list 414 (copy-sequence (org-buffer-property-keys nil t t))))) 415 (dolist (prop (org-entry-properties)) 416 (setq lst (delete (car prop) lst))) 417 lst))) 418 (substring pcomplete-stub 1))) 419 420 (defun pcomplete/org-mode/block-option/src () 421 "Complete the arguments of a source block. 422 Complete a language in the first field, the header arguments and 423 switches." 424 (pcomplete-here 425 (mapcar 426 (lambda(x) (symbol-name (nth 3 x))) 427 (cdr (car (cdr (memq :key-type (plist-get 428 (symbol-plist 429 'org-babel-load-languages) 430 'custom-type))))))) 431 (let* ((info (org-babel-get-src-block-info 'no-eval)) 432 (lang (car info)) 433 (lang-headers (intern (concat "org-babel-header-args:" lang))) 434 (headers (org-babel-combine-header-arg-lists 435 org-babel-common-header-args-w-values 436 (and (boundp lang-headers) (eval lang-headers t))))) 437 (while (pcomplete-here 438 (append (mapcar 439 (lambda (arg) (format ":%s" (symbol-name (car arg)))) 440 headers) 441 '("-n" "-r" "-l")))))) 442 443 (defun pcomplete/org-mode/block-option/clocktable () 444 "Complete keywords in a clocktable line." 445 (while (pcomplete-here '(":maxlevel" ":scope" ":lang" 446 ":tstart" ":tend" ":block" ":step" 447 ":stepskip0" ":fileskip0" 448 ":emphasize" ":link" ":narrow" ":indent" 449 ":hidefiles" ":tcolumns" ":level" ":compact" 450 ":timestamp" ":formula" ":formatter" 451 ":wstart" ":mstart")))) 452 453 454 ;;; Finish up 455 456 (provide 'org-pcomplete) 457 458 ;;; org-pcomplete.el ends here