org-index.el 72 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944
  1. ;;; org-index.el --- A personal index for org and beyond
  2. ;; Copyright (C) 2011-2013 Free Software Foundation, Inc.
  3. ;; Author: Marc Ihm <org-index@ferntreffer.de>
  4. ;; Keywords: hypermedia, matching
  5. ;; Requires: org
  6. ;; Download: http://orgmode.org/worg/code/elisp/org-index.el
  7. ;; Version: 2.3.2
  8. ;; This file is not part of GNU Emacs.
  9. ;;; License:
  10. ;; This program is free software; you can redistribute it and/or modify
  11. ;; it under the terms of the GNU General Public License as published by
  12. ;; the Free Software Foundation; either version 3, or (at your option)
  13. ;; any later version.
  14. ;;
  15. ;; This program is distributed in the hope that it will be useful,
  16. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. ;; GNU General Public License for more details.
  19. ;;
  20. ;; You should have received a copy of the GNU General Public License
  21. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  22. ;;; Commentary:
  23. ;; Purpose:
  24. ;;
  25. ;; Mark and find your favorite org-locations and other points of interest
  26. ;; easily; create and update a lookup table of references and links. When
  27. ;; searching, frequently used entries appear at the the top and entering
  28. ;; some keywords narrows down to matching entries only; so the right one
  29. ;; can be spotted easily.
  30. ;;
  31. ;; References are essentially small numbers (e.g. "R237" or "-455-"),
  32. ;; which are created by this package; they are well suited to be used
  33. ;; outside org. Links are normal org-mode links.
  34. ;;
  35. ;; Setup:
  36. ;;
  37. ;; - Add these lines to your .emacs:
  38. ;;
  39. ;; (require 'org-index)
  40. ;;
  41. ;; ;; Optionally assign a key. Pick your own.
  42. ;; (global-set-key (kbd "C-+") 'org-index)
  43. ;;
  44. ;; - Invoke `org-index', which will assist you to create your
  45. ;; index table.
  46. ;;
  47. ;; - Do not forget to restart emacs to make these lines effective.
  48. ;;
  49. ;;
  50. ;; Further reading:
  51. ;;
  52. ;; See the documentation of `org-index', which can also be read
  53. ;; by invoking `org-index' and and choosing the help-command.
  54. ;;
  55. ;; For more documentation and working examples, see:
  56. ;;
  57. ;; http://orgmode.org/worg/org-contrib/org-index.html
  58. ;;
  59. ;;; Change Log:
  60. ;; [2013-10-04 Fr] Version 2.3.2:
  61. ;; - Bugfix: index-table created by assistant is found after
  62. ;; restart of emacs instead of invoking assistent again
  63. ;;
  64. ;; [2013-07-20 Sa] Version 2.3.0:
  65. ;; - Renamed from "org-favtable" to "org-index"
  66. ;; - Added an assistent to set up the index table
  67. ;; - occur is now incremental, searching as you type
  68. ;; - simplified the documentation and help-system
  69. ;; - Saving keystrokes, as "+g237" is now valid input
  70. ;; - Many bugfixes
  71. ;;
  72. ;; [2013-02-28 Th] Version 2.2.0:
  73. ;; - Allowed shortcuts like "h237" for command "head" with argument "237"
  74. ;; - Integrated with org-mark-ring-goto
  75. ;;
  76. ;; [2013-01-25 Fr] Version 2.1.0:
  77. ;; - Added full support for links
  78. ;; - New commands "missing" and "statistics"
  79. ;; - Renamed the package from "org-reftable" to "org-favtable"
  80. ;; - Additional columns are required (e.g. "link"). Error messages will
  81. ;; guide you
  82. ;;
  83. ;; [2012-12-07 Fr] Version 2.0.0:
  84. ;; - The format of the table of favorites has changed ! You need to bring
  85. ;; your existing table into the new format by hand (which however is
  86. ;; easy and explained below)
  87. ;; - Reference table can be sorted after usage count or date of last access
  88. ;; - Ask user explicitly, which command to invoke
  89. ;; - Renamed the package from "org-refer-by-number" to "org-reftable"
  90. ;; [2012-09-22 Sa] Version 1.5.0:
  91. ;; - New command "sort" to sort a buffer or region by reference number
  92. ;; - New commands "highlight" and "unhighlight" to mark references
  93. ;; [2012-07-13 Fr] Version 1.4.0:
  94. ;; - New command "head" to find a headline with a reference number
  95. ;; [2012-04-28 Sa] Version 1.3.0:
  96. ;; - New commands occur and multi-occur
  97. ;; - All commands can now be invoked explicitly
  98. ;; - New documentation
  99. ;; - Many bugfixes
  100. ;; [2011-12-10 Sa] Version 1.2.0:
  101. ;; - Fixed a bug, which lead to a loss of newly created reference numbers
  102. ;; - Introduced single and double prefix arguments
  103. ;; - Started this Change Log
  104. ;;; Code:
  105. (require 'org-table)
  106. (require 'cl)
  107. (defvar org-index--preferred-command nil)
  108. (defvar org-index--commands
  109. '(occur head ref link leave enter goto help + reorder fill sort update highlight unhighlight missing statistics)
  110. "List of commands known to org-index.")
  111. (defvar org-index--commands-some '(occur head ref link leave enter goto help +))
  112. (defvar org-index--columns nil)
  113. (defcustom org-index-id nil
  114. "Id of the Org-mode node, which contains the index table."
  115. :group 'org
  116. :group 'org-index)
  117. (defvar org-index--text-to-yank nil)
  118. (defvar org-index--last-action nil)
  119. (defvar org-index--ref-regex nil)
  120. (defvar org-index--ref-format nil)
  121. (defvar org-index--buffer nil "buffer of index table")
  122. (defvar org-index--point nil "position at start of headline of index table")
  123. (defvar org-index--below-hline nil "position of first cell in first line below hline")
  124. (defvar org-index--point-before nil "point in buffer with index table")
  125. (defun org-index (&optional ARG)
  126. "Mark and find your favorite things and org-locations easily:
  127. Create and update a lookup table of references and links. Often
  128. used entries bubble to the top; entering some keywords narrows
  129. down to matching entries only, so that the right one can be
  130. spotted easily.
  131. References are essentially small numbers (e.g. \"R237\" or \"-455-\"),
  132. which are created by this package; they are well suited to be used
  133. outside of org. Links are normal org-mode links.
  134. This is version 2.3.2 of org-index.
  135. The function `org-index' operates on a dedicated table, the index
  136. table, which lives within its own Org-mode node. The table and
  137. its node will be created, when you first invoke org-index.
  138. Each line in the index table contains:
  139. - A reference
  140. - A link
  141. - A number; counting, how often each reference has been
  142. used. This number is updated automatically and the table can
  143. be sorted after it, so that most frequently used references
  144. appear at the top of the table and can be spotted easily.
  145. - The creation date of the line.
  146. - Date and time of last access. This column can alternatively be
  147. used to sort the table.
  148. - A column for your own comments, which allows lines to be selected by
  149. keywords.
  150. The index table is found through the id of the containing
  151. node; this id is stored within `org-index-id'.
  152. The function `org-index' is the only interactive function of this
  153. package and its sole entry point; it offers several commands to
  154. create, find and look up these favorites (references and links).
  155. Commands known:
  156. occur: Incremental search, that after each keystroke shows
  157. matching lines from index table. You may enter a list of words
  158. seperated by comma (\",\"), to select lines that contain all
  159. of the given words.
  160. If you supply a number (e.g. \"237\"): Apply emacs standard
  161. multi-occur operation on all org-mode buffers to search for
  162. this specific reference.
  163. You may also read the note at the end of this help on saving
  164. the keystroke RET with this frequent default command.
  165. head: If invoked outside the index table, ask for a
  166. reference number and search for a heading containing it. If
  167. invoked within index table dont ask; rather use the reference or
  168. link from the current line.
  169. ref: Create a new reference, copy any previously selected text.
  170. If already within index table, fill in ref-column.
  171. link: Create a new line in index table with a link to the
  172. current node. Do not populate the ref column; this can later
  173. be populated by calling the \"fill\" command from within the
  174. index table.
  175. leave: Leave the index table. If the last command has
  176. been \"ref\", the new reference is copied and ready to yank.
  177. This \"org-mark-ring-goto\" and can be called several times
  178. in succession. If you invoke org-index with a prefix argument,
  179. this command \"leave\" is executed without further questions.
  180. enter: Just enter the node with the index table.
  181. goto: Search for a specific reference within the index table.
  182. help: Show this text.
  183. +: Show all commands including the less frequently used ones
  184. given below. If \"+\" is followd by enough letters of such a
  185. command (e.g. \"+fi\"), then this command is invoked
  186. directly.
  187. reorder: Temporarily reorder the index table, e.g. by
  188. count, reference or last access.
  189. fill: If either ref or link is missing, fill it.
  190. sort: Sort a set of lines (either the active region or the
  191. whole buffer) by the references found in each line.
  192. update: For the given reference, update the line in the
  193. index table.
  194. highlight: Highlight references in region or buffer.
  195. unhighlight: Remove highlights.
  196. missing : Search for missing reference numbers (which do not
  197. appear in the reference table). If requested, add additional
  198. lines for them, so that the command \"ref\" is able to reuse
  199. them.
  200. statistics : Show some statistics (e.g. minimum and maximum
  201. reference) about index table.
  202. Two ways to save keystrokes:
  203. When prompting for a command, org-index puts the most likely
  204. one (e.g. \"occur\" or \"ref\") in front of the list, so that
  205. you may just type RET.
  206. If this command needs additional input (like e.g. \"occur\"), you
  207. may supply this input right away, although you are still beeing
  208. prompted for the command. So, to do an occur for the string
  209. \"foo\", you can just enter \"foo\" RET, without even typing
  210. \"occur\".
  211. Another way to save keystrokes applies if you want to choose a
  212. command, that requrires a reference number (and would normally
  213. prompt for it): In that case you may just enter enough characters
  214. from your command, so that it appears first in the list of
  215. matches; then immediately enter the number of the reference you
  216. are searching for. So the input \"h237\" would execute the
  217. command \"head\" for reference \"237\" right away.
  218. "
  219. (interactive "P")
  220. (org-index-1 (if (equal ARG '(4)) 'leave nil) )
  221. )
  222. (defun org-index-1 (&optional what search search-is-link)
  223. "Do the actual worg for org-index; its optional arguments are:
  224. search : string to search for
  225. what : symbol of the command to invoke
  226. search-is-link : t, if argument search is actually a link
  227. An example would be:
  228. (org-index \"237\" 'head) ;; find heading with ref 237
  229. "
  230. (let (within-node ; True, if we are within node of the index table
  231. active-window-index ; active window with index table (if any)
  232. below-cursor ; word below cursor
  233. active-region ; active region (if any)
  234. link-id ; link of starting node, if required
  235. guarded-search ; with guard against additional digits
  236. search-is-ref ; true, if search is a reference
  237. commands ; currently active set of selectable commands
  238. what-adjusted ; True, if we had to adjust what
  239. what-input ; Input on what question (need not necessary be "what")
  240. trailing-digits ; any digits, that are are appended to what-input
  241. reorder-once ; Column to use for single time sorting
  242. parts ; Parts of a typical reference number (which
  243. ; need not be a plain number); these are:
  244. head ; Any header before number (e.g. "R")
  245. maxref ; Maximum number from reference table (e.g. "153")
  246. tail ; Tail after number (e.g. "}" or "")
  247. ref-regex ; Regular expression to match a reference
  248. has-reuse ; True, if table contains a line for reuse
  249. numcols ; Number of columns in index table
  250. kill-new-text ; Text that will be appended to kill ring
  251. message-text ; Text that will be issued as an explanation,
  252. ; what we have done
  253. initial-ref-or-link ; Initial position in index table
  254. )
  255. ;;
  256. ;; Examine current buffer and location, before turning to index table
  257. ;;
  258. (unless (boundp 'org-index-id)
  259. (setq org-index-id nil)
  260. (org-index--create-new-index
  261. t
  262. (format "No index table has been created yet." org-index-id)))
  263. ;; Bail out, if new index has been created
  264. (catch 'created-new-index
  265. ;; Get the content of the active region or the word under cursor
  266. (if (and transient-mark-mode
  267. mark-active)
  268. (setq active-region (buffer-substring (region-beginning) (region-end))))
  269. (setq below-cursor (thing-at-point 'symbol))
  270. ;; Find out, if we are within favable or not
  271. (setq within-node (string= (org-id-get) org-index-id))
  272. ;;
  273. ;; Get decoration of references and highest reference from index table
  274. ;;
  275. ;; Save initial ref or link
  276. (if (and within-node
  277. (org-at-table-p))
  278. (setq initial-ref-or-link
  279. (or (org-index--get-field 'ref)
  280. (org-index--get-field 'link))))
  281. ;; Find node
  282. (let ((marker (org-id-find org-index-id 'marker)) initial)
  283. (if marker
  284. (progn
  285. (setq org-index--buffer (marker-buffer marker)
  286. org-index--point (marker-position marker))
  287. (move-marker marker nil))
  288. (org-index--create-new-index
  289. t
  290. (format "Cannot find node with id \"%s\"" org-index-id))))
  291. ;; Check and remember, if active window contains buffer with index table
  292. (if (eq (window-buffer) org-index--buffer)
  293. (setq active-window-index (selected-window)))
  294. ;; Get configuration of index table; catch errors
  295. (let ((error-message
  296. (catch 'content-error
  297. (with-current-buffer org-index--buffer
  298. (unless org-index--point-before
  299. (setq org-index--point-before (point)))
  300. (unless (string= (org-id-get) org-index-id)
  301. (goto-char org-index--point))
  302. ;; parse table while still within buffer
  303. (setq parts (org-index--parse-and-adjust-table))
  304. ;; go back
  305. (goto-char org-index--point-before)
  306. nil))))
  307. (when error-message
  308. (org-pop-to-buffer-same-window org-index--buffer)
  309. (org-reveal)
  310. (error error-message)))
  311. ;; Give names to parts of configuration
  312. (setq head (nth 0 parts))
  313. (setq maxref (nth 1 parts))
  314. (setq tail (nth 2 parts))
  315. (setq numcols (nth 3 parts))
  316. (setq ref-regex (nth 4 parts))
  317. (setq has-reuse (nth 5 parts))
  318. (setq org-index--ref-regex ref-regex)
  319. (setq org-index--ref-format (concat head "%d" tail))
  320. ;;
  321. ;; Find out, what we are supposed to do
  322. ;;
  323. ;; Set preferred action, that will be the default choice
  324. (setq org-index--preferred-command
  325. (if within-node
  326. (if (memq org-index--last-action '(ref link))
  327. 'leave
  328. 'goto)
  329. (if active-region
  330. 'ref
  331. (if (and below-cursor (string-match ref-regex below-cursor))
  332. 'occur
  333. nil))))
  334. ;; Ask user, what to do
  335. (unless what
  336. (setq commands (copy-list org-index--commands-some))
  337. (while (let (completions starts-with-plus is-only-plus)
  338. (setq what-input
  339. (org-completing-read
  340. "Please choose: "
  341. (mapcar 'symbol-name
  342. ;; Construct unique list of commands with
  343. ;; preferred one at front
  344. (delq nil (delete-dups
  345. (append
  346. (list org-index--preferred-command)
  347. (copy-list commands)))))
  348. nil nil))
  349. ;; if input ends in digits, save them away and do completions on head of input
  350. ;; this allows input like "h224" to be accepted
  351. (when (string-match "^\\([^0-9]+\\)\\([0-9]+\\)\\s *$" what-input)
  352. ;; remember digits
  353. (setq trailing-digits (string-to-number (match-string 2 what-input)))
  354. ;; and use non-digits-part to find match
  355. (setq what-input (match-string 1 what-input)))
  356. ;; if input starts with "+", any command (not only some) may follow
  357. ;; this allows input like "+sort" to be accepted
  358. (when (string= (substring what-input 0 1) "+")
  359. ;; make all commands available for selection
  360. (setq commands (copy-list org-index--commands))
  361. (setq what-input (substring what-input 1))
  362. (setq starts-with-plus (> (length what-input) 0))
  363. (setq is-only-plus (not starts-with-plus)))
  364. ;; get list of possible completions for what-input; i.e.
  365. ;; all commands, that start with what-input
  366. (setq completions (delq nil (mapcar
  367. (lambda (x)
  368. (let ((where (search what-input (symbol-name x))))
  369. (if (and where
  370. (= where 0))
  371. x
  372. nil))) commands)))
  373. ;; if input starts with "+" and not just "+"
  374. (when starts-with-plus
  375. ;; use first completion, if unambigously
  376. (if (= (length completions) 1)
  377. (setq what-input (symbol-name (car completions)))
  378. (if completions
  379. (error "Input \"+%s\" matches multiple commands: %s"
  380. what-input
  381. (mapconcat 'symbol-name completions ", "))
  382. (error "Input \"+%s\" matches no commands" what-input))))
  383. ;; if input ends in digits, use first completion, even if ambigous
  384. ;; this allows input like "h224" to be accepted
  385. (when (and trailing-digits completions)
  386. ;; use first match as input, even if ambigously
  387. (setq org-index--preferred-command (first completions))
  388. (setq what-input (number-to-string trailing-digits)))
  389. ;; convert to symbol
  390. (setq what (intern what-input))
  391. (if is-only-plus (setq what '+))
  392. ;; user is not required to input one of the commands; if
  393. ;; not, take the first one and use the original input for
  394. ;; next question
  395. (if (memq what commands)
  396. ;; input matched one element of list, dont need original
  397. ;; input any more
  398. (setq what-input nil)
  399. ;; what-input will be used for next question, use first
  400. ;; command for what
  401. (setq what (or org-index--preferred-command
  402. (first commands)))
  403. ;; remove any trailing dot, that user might have added to
  404. ;; disambiguate his input
  405. (if (and (> (length what-input) 0)
  406. (equal (substring what-input -1) "."))
  407. ;; but do this only, if dot was really necessary to
  408. ;; disambiguate
  409. (let ((shortened-what-input (substring what-input 0 -1)))
  410. (unless (test-completion shortened-what-input
  411. (mapcar 'symbol-name
  412. commands))
  413. (setq what-input shortened-what-input)))))
  414. ;; ask for reorder in loop, because we have to ask for
  415. ;; what right again
  416. (if (eq what 'reorder)
  417. (setq reorder-once
  418. (intern
  419. (org-icompleting-read
  420. "Please choose column to reorder index table once: "
  421. (mapcar 'symbol-name '(ref count last-accessed))
  422. nil t))))
  423. ;; maybe ask initial question again
  424. (memq what '(reorder +)))))
  425. ;;
  426. ;; Get search, if required
  427. ;;
  428. ;; These actions need a search string:
  429. (when (memq what '(goto occur head update))
  430. ;; Maybe we've got a search string from the arguments
  431. (unless search
  432. (let (search-from-table
  433. search-from-cursor)
  434. ;; Search string can come from several sources:
  435. ;; From link or ref columns of table
  436. (when within-node
  437. (setq search-from-table (org-index--get-field 'link))
  438. (if search-from-table
  439. (setq search-is-link t)
  440. (setq search-from-table (org-index--get-field 'ref))))
  441. ;; From string below cursor
  442. (when (and (not within-node)
  443. below-cursor
  444. (string-match (concat "\\(" ref-regex "\\)")
  445. below-cursor))
  446. (setq search-from-cursor (match-string 1 below-cursor)))
  447. ;; Depending on requested action, get search from one of the sources above
  448. (cond ((eq what 'goto)
  449. (setq search (or what-input search-from-cursor)))
  450. ((memq what '(head occur))
  451. (setq search (or what-input search-from-table search-from-cursor))))))
  452. ;; If we still do not have a search string, ask user explicitly
  453. (unless search
  454. (unless (eq what 'occur)
  455. (if what-input
  456. (setq search what-input)
  457. (setq search (read-from-minibuffer
  458. (cond ((eq what 'head)
  459. "Text or reference number to search for: ")
  460. ((eq what 'goto)
  461. "Reference number to search for, or enter \".\" for id of current node: ")
  462. ((eq what 'update)
  463. "Reference number to update: ")))))
  464. (if (string-match "^\\s *[0-9]+\\s *$" search)
  465. (setq search (format "%s%s%s" head (org-trim search) tail))))))
  466. ;; Clean up and examine search string
  467. (when search
  468. (setq search (org-trim search))
  469. (if (string= search "") (setq search nil))
  470. (when search
  471. (if (string-match "^[0-9]+$" search)
  472. (setq search (concat head search tail)))
  473. (setq search-is-ref (string-match ref-regex search))))
  474. ;; Check for special case
  475. (when (and (memq what '(head goto))
  476. (string= search "."))
  477. (setq search (org-id-get))
  478. (setq search-is-link t))
  479. (when search-is-ref
  480. (setq guarded-search (org-index--make-guarded-search search)))
  481. ;;
  482. ;; Do some sanity checking before really starting
  483. ;;
  484. ;; Correct requested action, if nothing to search
  485. (when (and (not search)
  486. (memq what '(search head)))
  487. (setq what 'enter)
  488. (setq what-adjusted t))
  489. ;; For a proper reference as input, we do multi-occur
  490. (if (and search
  491. (string-match ref-regex search)
  492. (eq what 'occur))
  493. (setq what 'multi-occur))
  494. ;; Check for invalid combinations of arguments; try to be helpful
  495. (when (and (memq what '(head goto))
  496. (not search-is-link)
  497. (not search-is-ref))
  498. (error "Can do '%s' only for a reference or link (not '%s'), try 'occur' to search for text" what search))
  499. ;;
  500. ;; Prepare
  501. ;;
  502. ;; Get link if required before moving in
  503. (if (eq what 'link)
  504. (let ((org-id-link-to-org-use-id t))
  505. (setq link-id (org-id-get-create))))
  506. ;; Move into table, if outside
  507. ;; These commands enter index table only temporarily
  508. (when (memq what '(occur multi-occur statistics))
  509. ;; Switch to index table
  510. (set-buffer org-index--buffer)
  511. (goto-char org-index--point)
  512. ;; sort index table
  513. (org-index--sort-table reorder-once))
  514. ;; These commands will leave user in index table after they are finished
  515. (when (memq what '(enter ref link goto missing))
  516. ;; Support orgmode-standard of going back (buffer and position)
  517. (org-mark-ring-push)
  518. ;; Switch to index table
  519. (org-pop-to-buffer-same-window org-index--buffer)
  520. (goto-char org-index--point)
  521. (show-subtree)
  522. (org-show-context)
  523. (setq org-index--point-before nil) ;; dont want to go back
  524. ;; sort index table
  525. (org-index--sort-table reorder-once))
  526. ;; Goto back to initial ref, because reformatting of table above might
  527. ;; have moved point
  528. (when initial-ref-or-link
  529. (while (and (org-at-table-p)
  530. (not (or
  531. (string= initial-ref-or-link (org-index--get-field 'ref))
  532. (string= initial-ref-or-link (org-index--get-field 'link)))))
  533. (forward-line))
  534. ;; did not find ref, go back to top
  535. (if (not (org-at-table-p)) (goto-char org-index--point)))
  536. ;;
  537. ;; Actually do, what is requested
  538. ;;
  539. (cond
  540. ((eq what 'help)
  541. ;; bring up help-buffer for this function
  542. (describe-function 'org-index))
  543. ((eq what 'multi-occur)
  544. ;; Conveniently position cursor on number to search for
  545. (goto-char org-index--below-hline)
  546. (let (found (initial (point)))
  547. (while (and (not found)
  548. (forward-line)
  549. (org-at-table-p))
  550. (save-excursion
  551. (setq found (string= search
  552. (org-index--get-field 'ref)))))
  553. (if found
  554. (org-index--update-line nil)
  555. (goto-char initial)))
  556. ;; Construct list of all org-buffers
  557. (let (buff org-buffers)
  558. (dolist (buff (buffer-list))
  559. (set-buffer buff)
  560. (if (string= major-mode "org-mode")
  561. (setq org-buffers (cons buff org-buffers))))
  562. ;; Do multi-occur
  563. (multi-occur org-buffers guarded-search)
  564. (if (get-buffer "*Occur*")
  565. (progn
  566. (setq message-text (format "multi-occur for '%s'" search))
  567. (other-window 1)
  568. (toggle-truncate-lines 1))
  569. (setq message-text (format "Did not find '%s'" search)))))
  570. ((eq what 'head)
  571. (let (link)
  572. ;; link either from table or passed in as argument
  573. ;; try to get link
  574. (if search-is-link
  575. (setq link (org-trim search))
  576. (if (and within-node
  577. (org-at-table-p))
  578. (setq link (org-index--get-field 'link))))
  579. ;; use link if available
  580. (if (and link
  581. (not (string= link "")))
  582. (progn
  583. (org-index--update-line search)
  584. (org-id-goto link)
  585. (org-reveal)
  586. (if (eq (current-buffer) org-index--buffer)
  587. (setq org-index--point-before nil))
  588. (setq message-text "Followed link"))
  589. (message (format "Scanning headlines for '%s' ..." search))
  590. (org-index--update-line search)
  591. (let (buffer point)
  592. (if (catch 'found
  593. (progn
  594. ;; loop over all headlines, stop on first match
  595. (org-map-entries
  596. (lambda ()
  597. (when (looking-at (concat ".*" guarded-search))
  598. ;; If this is not an inlinetask ...
  599. (when (< (org-element-property :level (org-element-at-point))
  600. org-inlinetask-min-level)
  601. ;; ... remember location and bail out
  602. (setq buffer (current-buffer))
  603. (setq point (point))
  604. (throw 'found t))))
  605. nil 'agenda)
  606. nil))
  607. (progn
  608. (if (eq buffer org-index--buffer)
  609. (setq org-index--point-before nil))
  610. (setq message-text (format "Found '%s'" search))
  611. (org-pop-to-buffer-same-window buffer)
  612. (goto-char point)
  613. (org-reveal))
  614. (setq message-text (format "Did not find '%s'" search)))))))
  615. ((eq what 'leave)
  616. (setq kill-new-text org-index--text-to-yank)
  617. (setq org-index--text-to-yank nil)
  618. ;; If "leave" has been called two times in succession, make
  619. ;; org-mark-ring-goto believe it has been called two times too
  620. (if (eq org-index--last-action 'leave)
  621. (let ((this-command nil) (last-command nil))
  622. (org-mark-ring-goto 1))
  623. (org-mark-ring-goto)))
  624. ((eq what 'goto)
  625. ;; Go downward in table to requested reference
  626. (let (found (initial (point)))
  627. (goto-char org-index--below-hline)
  628. (while (and (not found)
  629. (forward-line)
  630. (org-at-table-p))
  631. (save-excursion
  632. (setq found
  633. (string= search
  634. (org-index--get-field
  635. (if search-is-link 'link 'ref))))))
  636. (if found
  637. (progn
  638. (setq message-text (format "Found '%s'" search))
  639. (org-index--update-line nil)
  640. (org-table-goto-column (org-index--column-num 'ref))
  641. (if (looking-back " ") (backward-char))
  642. ;; remember string to copy
  643. (setq org-index--text-to-yank
  644. (org-trim (org-table-get-field (org-index--column-num 'copy)))))
  645. (setq message-text (format "Did not find '%s'" search))
  646. (goto-char initial)
  647. (forward-line)
  648. (setq what 'missed))))
  649. ((eq what 'occur)
  650. (org-index--do-occur what-input))
  651. ((memq what '(ref link))
  652. ;; add a new row (or reuse existing one)
  653. (let (new)
  654. (when (eq what 'ref)
  655. ;; go through table to find first entry to be reused
  656. (when has-reuse
  657. (goto-char org-index--below-hline)
  658. ;; go through table
  659. (while (and (org-at-table-p)
  660. (not new))
  661. (when (string=
  662. (org-index--get-field 'count)
  663. ":reuse:")
  664. (setq new (org-index--get-field 'ref))
  665. (if new (org-table-kill-row)))
  666. (forward-line)))
  667. ;; no ref to reuse; construct new reference
  668. (unless new
  669. (setq new (format "%s%d%s" head (1+ maxref) tail)))
  670. ;; remember for org-mark-ring-goto
  671. (setq org-index--text-to-yank new))
  672. ;; insert ref or link as very first row
  673. (goto-char org-index--below-hline)
  674. (org-table-insert-row)
  675. ;; fill special columns with standard values
  676. (when (eq what 'ref)
  677. (org-table-goto-column (org-index--column-num 'ref))
  678. (insert new))
  679. (when (eq what 'link)
  680. (org-table-goto-column (org-index--column-num 'link))
  681. (insert link-id))
  682. (org-table-goto-column (org-index--column-num 'created))
  683. (org-insert-time-stamp nil nil t)
  684. (org-table-goto-column (org-index--column-num 'count))
  685. (insert "1")
  686. ;; goto copy-field or first empty one
  687. (if (org-index--column-num 'copy)
  688. (org-table-goto-column (org-index--column-num 'copy))
  689. (unless (catch 'empty
  690. (dotimes (col numcols)
  691. (org-table-goto-column (+ col 1))
  692. (if (string= (org-trim (org-table-get-field)) "")
  693. (throw 'empty t))))
  694. ;; none found, goto first
  695. (org-table-goto-column 1)))
  696. (org-table-align)
  697. (if active-region (setq kill-new-text active-region))
  698. (if (eq what 'ref)
  699. (setq message-text (format "Adding a new row with ref '%s'" new))
  700. (setq message-text (format "Adding a new row linked to '%s'" link-id)))))
  701. ((eq what 'enter)
  702. ;; simply go into table
  703. (goto-char org-index--below-hline)
  704. (show-subtree)
  705. (recenter)
  706. (if what-adjusted
  707. (setq message-text "Nothing to search for; at index table")
  708. (setq message-text "At index table")))
  709. ((eq what 'fill)
  710. ;; check, if within index table
  711. (unless (and within-node
  712. (org-at-table-p))
  713. (error "Not within index table"))
  714. ;; applies to missing refs and missing links alike
  715. (let ((ref (org-index--get-field 'ref))
  716. (link (org-index--get-field 'link)))
  717. (if (and (not ref)
  718. (not link))
  719. ;; have already checked this during parse, check here anyway
  720. (error "Columns ref and link are both empty in this line"))
  721. ;; fill in new ref
  722. (if (not ref)
  723. (progn
  724. (setq kill-new-text (format "%s%d%s" head (1+ maxref) tail))
  725. (org-index--get-field 'ref kill-new-text)
  726. ;; remember for org-mark-ring-goto
  727. (setq org-index--text-to-yank kill-new-text)
  728. (org-id-goto link)
  729. (setq message-text "Filled field of index table with new reference"))
  730. ;; fill in new link
  731. (if (not link)
  732. (progn
  733. (setq guarded-search (org-index--make-guarded-search ref))
  734. (message (format "Scanning headlines for '%s' ..." ref))
  735. (let (link)
  736. (if (catch 'found
  737. (org-map-entries
  738. (lambda ()
  739. (when (looking-at (concat ".*" guarded-search))
  740. (setq link (org-id-get-create))
  741. (throw 'found t)))
  742. nil 'agenda)
  743. nil)
  744. (progn
  745. (org-index--get-field 'link link)
  746. (setq message-text "Inserted link"))
  747. (setq message-text (format "Did not find reference '%s'" ref)))))
  748. ;; nothing is missing
  749. (setq message-text "Columns 'ref' and 'link' are already filled; nothing to do")))))
  750. ((eq what 'sort)
  751. ;; sort lines according to contained reference
  752. (let (begin end where)
  753. (catch 'aborted
  754. ;; either active region or whole buffer
  755. (if (and transient-mark-mode
  756. mark-active)
  757. ;; sort only region
  758. (progn
  759. (setq begin (region-beginning))
  760. (setq end (region-end))
  761. (setq where "region"))
  762. ;; sort whole buffer
  763. (setq begin (point-min))
  764. (setq end (point-max))
  765. (setq where "whole buffer")
  766. ;; make sure
  767. (unless (y-or-n-p "Sort whole buffer ")
  768. (setq message-text "Sort aborted")
  769. (throw 'aborted nil)))
  770. (save-excursion
  771. (save-restriction
  772. (goto-char (point-min))
  773. (narrow-to-region begin end)
  774. (sort-subr nil 'forward-line 'end-of-line
  775. (lambda ()
  776. (if (looking-at (concat ".*"
  777. (org-index--make-guarded-search ref-regex 'dont-quote)))
  778. (string-to-number (match-string 1))
  779. 0))))
  780. (highlight-regexp ref-regex 'isearch)
  781. (setq message-text (format "Sorted %s from character %d to %d, %d lines"
  782. where begin end
  783. (count-lines begin end)))))))
  784. ((eq what 'update)
  785. ;; simply update line in index table
  786. (save-excursion
  787. (let ((ref-or-link (if search-is-link "link" "reference")))
  788. (beginning-of-line)
  789. (if (org-index--update-line search)
  790. (setq message-text (format "Updated %s '%s'" ref-or-link search))
  791. (setq message-text (format "Did not find %s '%s'" ref-or-link search))))))
  792. ((eq what 'parse)
  793. ;; Just parse the index table, which is already done, so nothing to do
  794. )
  795. ((memq what '(highlight unhighlight))
  796. (let ((where "buffer"))
  797. (save-excursion
  798. (save-restriction
  799. (when (and transient-mark-mode
  800. mark-active)
  801. (narrow-to-region (region-beginning) (region-end))
  802. (setq where "region"))
  803. (if (eq what 'highlight)
  804. (progn
  805. (highlight-regexp ref-regex 'isearch)
  806. (setq message-text (format "Highlighted references in %s" where)))
  807. (unhighlight-regexp ref-regex)
  808. (setq message-text (format "Removed highlights for references in %s" where)))))))
  809. ((memq what '(missing statistics))
  810. (goto-char org-index--below-hline)
  811. (let (missing
  812. ref-field
  813. ref
  814. min
  815. max
  816. (total 0))
  817. ;; start with list of all references
  818. (setq missing (mapcar (lambda (x) (format "%s%d%s" head x tail))
  819. (number-sequence 1 maxref)))
  820. ;; go through table and remove all refs, that we see
  821. (while (and (forward-line)
  822. (org-at-table-p))
  823. ;; get ref-field and number
  824. (setq ref-field (org-index--get-field 'ref))
  825. (if (and ref-field
  826. (string-match ref-regex ref-field))
  827. (setq ref (string-to-number (match-string 1 ref-field))))
  828. ;; remove existing refs from list
  829. (if ref-field (setq missing (delete ref-field missing)))
  830. ;; record min and max
  831. (if (or (not min) (< ref min)) (setq min ref))
  832. (if (or (not max) (> ref max)) (setq max ref))
  833. ;; count
  834. (setq total (1+ total)))
  835. ;; insert them, if requested
  836. (forward-line -1)
  837. (if (eq what 'statistics)
  838. (setq message-text (format "Found %d references from %s to %s. %d references below highest do not appear in table. "
  839. total
  840. (format org-index--ref-format min)
  841. (format org-index--ref-format max)
  842. (length missing)))
  843. (if (y-or-n-p (format "Found %d missing references; do you wish to append them to the index table"
  844. (length missing)))
  845. (let (type)
  846. (setq type (org-icompleting-read
  847. "Insert new lines for reuse by command \"new\" or just as missing ? " '("reuse" "missing")))
  848. (mapc (lambda (x)
  849. (let (org-table-may-need-update) (org-table-insert-row t))
  850. (org-index--get-field 'ref x)
  851. (org-index--get-field 'count (format ":%s:" type)))
  852. missing)
  853. (org-table-align)
  854. (setq message-text (format "Inserted %d new lines for missing refernces" (length missing))))
  855. (setq message-text (format "%d missing references." (length missing)))))))
  856. (t (error "This is a bug: unmatched case '%s'" what)))
  857. ;; restore point in buffer or window with index table
  858. (if org-index--point-before
  859. ;; buffer displayed in window need to set point there first
  860. (if (eq (window-buffer active-window-index)
  861. org-index--buffer)
  862. (set-window-point active-window-index org-index--point-before)
  863. ;; set position in buffer in any case and second
  864. (with-current-buffer org-index--buffer
  865. (goto-char org-index--point-before)
  866. (setq org-index--point-before nil))))
  867. ;; remember what we have done for next time
  868. (setq org-index--last-action what)
  869. ;; tell, what we have done and what can be yanked
  870. (if kill-new-text (setq kill-new-text
  871. (substring-no-properties kill-new-text)))
  872. (if (string= kill-new-text "") (setq kill-new-text nil))
  873. (let ((m (concat
  874. message-text
  875. (if (and message-text kill-new-text)
  876. " and r"
  877. (if kill-new-text "R" ""))
  878. (if kill-new-text (format "eady to yank '%s'" kill-new-text) ""))))
  879. (unless (string= m "") (message m)))
  880. (if kill-new-text (kill-new kill-new-text)))))
  881. (defun org-index--parse-and-adjust-table ()
  882. (let ((maxref 0)
  883. top
  884. bottom
  885. ref-field
  886. link-field
  887. parts
  888. numcols
  889. head
  890. tail
  891. ref-regex
  892. has-reuse
  893. initial-point)
  894. (setq initial-point (point))
  895. (org-index--go-below-hline)
  896. (setq org-index--below-hline (point))
  897. (setq top (point))
  898. ;; count columns
  899. (org-table-goto-column 100)
  900. (setq numcols (- (org-table-current-column) 1))
  901. ;; get contents of columns
  902. (forward-line -2)
  903. (unless (org-at-table-p)
  904. (org-index--create-new-index
  905. nil
  906. "Index table starts with a hline"))
  907. ;; check for optional line consisting solely of width specifications
  908. (beginning-of-line)
  909. (if (looking-at "\\s *|\\(\\(\\s *|\\)\\|\\(\\s *<[0-9]+>\\s *|\\)\\)+\\s *$")
  910. (forward-line -1))
  911. (org-table-goto-column 1)
  912. (setq org-index--columns (org-index--parse-headings numcols))
  913. ;; Go beyond end of table
  914. (while (org-at-table-p) (forward-line 1))
  915. ;; Kill all empty rows at bottom
  916. (while (progn
  917. (forward-line -1)
  918. (org-table-goto-column 1)
  919. (and
  920. (not (org-index--get-field 'ref))
  921. (not (org-index--get-field 'link))))
  922. (org-table-kill-row))
  923. (forward-line)
  924. (setq bottom (point))
  925. (forward-line -1)
  926. ;; Retrieve any decorations around the number within the first nonempty ref-field
  927. (goto-char top)
  928. (while (and (org-at-table-p)
  929. (not (setq ref-field (org-index--get-field 'ref))))
  930. (forward-line))
  931. ;; Some Checking
  932. (unless ref-field
  933. (org-index--create-new-index
  934. nil
  935. "Reference column is empty"))
  936. (unless (string-match "^\\([^0-9]*\\)\\([0-9]+\\)\\([^0-9]*\\)$" ref-field)
  937. (org-index--create-new-index
  938. nil
  939. (format "First reference in index table ('%s') does not contain a number" ref-field)))
  940. ;; These are the decorations used within the first ref of index
  941. (setq head (match-string 1 ref-field))
  942. (setq tail (match-string 3 ref-field))
  943. (setq ref-regex (concat (regexp-quote head)
  944. "\\([0-9]+\\)"
  945. (regexp-quote tail)))
  946. ;; Go through table to find maximum number and do some checking
  947. (let ((ref 0))
  948. (while (org-at-table-p)
  949. (setq ref-field (org-index--get-field 'ref))
  950. (setq link-field (org-index--get-field 'link))
  951. (if (and (not ref-field)
  952. (not link-field))
  953. (throw 'content-error "Columns ref and link are both empty in this line"))
  954. (if ref-field
  955. (if (string-match ref-regex ref-field)
  956. ;; grab number
  957. (setq ref (string-to-number (match-string 1 ref-field)))
  958. (throw 'content-error "Column ref does not contain a number")))
  959. ;; check, if higher ref
  960. (if (> ref maxref) (setq maxref ref))
  961. ;; check if ref is ment for reuse
  962. (if (string= (org-index--get-field 'count) ":reuse:")
  963. (setq has-reuse 1))
  964. (forward-line 1)))
  965. ;; sort used to be here
  966. (setq parts (list head maxref tail numcols ref-regex has-reuse))
  967. ;; go back to top of table
  968. (goto-char top)
  969. parts))
  970. (defun org-index--sort-table (sort-column)
  971. (unless sort-column (setq sort-column (org-index--column-num 'sort)))
  972. (let (top
  973. bottom
  974. ref-field
  975. count-field
  976. count-special)
  977. ;; get boundaries of table
  978. (goto-char org-index--below-hline)
  979. (forward-line 0)
  980. (setq top (point))
  981. (while (org-at-table-p) (forward-line))
  982. (setq bottom (point))
  983. (save-restriction
  984. (narrow-to-region top bottom)
  985. (goto-char top)
  986. (sort-subr t
  987. 'forward-line
  988. 'end-of-line
  989. (lambda ()
  990. (let (ref
  991. (ref-field (or (org-index--get-field 'ref) ""))
  992. (count-field (or (org-index--get-field 'count) ""))
  993. (count-special 0))
  994. ;; get reference with leading zeroes, so it can be
  995. ;; sorted as text
  996. (string-match org-index--ref-regex ref-field)
  997. (setq ref (format
  998. "%06d"
  999. (string-to-number
  1000. (or (match-string 1 ref-field)
  1001. "0"))))
  1002. ;; find out, if special token in count-column
  1003. (setq count-special (format "%d"
  1004. (- 2
  1005. (length (member count-field '(":missing:" ":reuse:"))))))
  1006. ;; Construct different sort-keys according to
  1007. ;; requested sort column; prepend count-special to
  1008. ;; sort special entries at bottom of table, append ref
  1009. ;; as a secondary sort key
  1010. (cond
  1011. ((eq sort-column 'count)
  1012. (concat count-special
  1013. (format
  1014. "%08d"
  1015. (string-to-number (or (org-index--get-field 'count)
  1016. "")))
  1017. ref))
  1018. ((eq sort-column 'last-accessed)
  1019. (concat count-special
  1020. (org-index--get-field 'last-accessed)
  1021. " "
  1022. ref))
  1023. ((eq sort-column 'ref)
  1024. (concat count-special
  1025. ref))
  1026. (t (error "This is a bug: unmatched case '%s'" sort-column)))))
  1027. nil 'string<)))
  1028. ;; align table
  1029. (org-table-align))
  1030. (defun org-index--go-below-hline ()
  1031. ;; go to heading of node
  1032. (while (not (org-at-heading-p)) (forward-line -1))
  1033. (forward-line 1)
  1034. ;; go to table within node, but make sure we do not get into another node
  1035. (while (and (not (org-at-heading-p))
  1036. (not (org-at-table-p))
  1037. (not (eq (point) (point-max))))
  1038. (forward-line 1))
  1039. ;; check, if there really is a table
  1040. (unless (org-at-table-p)
  1041. (org-index--create-new-index
  1042. t
  1043. (format "Cannot find index table within node %s" org-index-id)))
  1044. ;; go to first hline
  1045. (while (and (not (org-at-table-hline-p))
  1046. (org-at-table-p))
  1047. (forward-line 1))
  1048. ;; and check
  1049. (unless (org-at-table-hline-p)
  1050. (org-index--create-new-index
  1051. nil
  1052. "Cannot find hline within index table"))
  1053. (forward-line 1)
  1054. (org-table-goto-column 1))
  1055. (defun org-index--parse-headings (numcols)
  1056. (let (columns)
  1057. ;; Associate names of special columns with column-numbers
  1058. (setq columns (copy-tree '((ref . 0) (link . 0) (created . 0) (last-accessed . 0)
  1059. (count . 0) (sort . nil) (copy . nil))))
  1060. ;; For each column
  1061. (dotimes (col numcols)
  1062. (let* (field-flags ;; raw heading, consisting of file name and maybe
  1063. ;; flags (seperated by ";")
  1064. field ;; field name only
  1065. field-symbol ;; and as a symbol
  1066. flags ;; flags from field-flags
  1067. found)
  1068. ;; parse field-flags into field and flags
  1069. (setq field-flags (org-trim (org-table-get-field (+ col 1))))
  1070. (if (string-match "^\\([^;]*\\);\\([a-z]+\\)$" field-flags)
  1071. (progn
  1072. (setq field (downcase (or (match-string 1 field-flags) "")))
  1073. ;; get flags as list of characters
  1074. (setq flags (mapcar 'string-to-char
  1075. (split-string
  1076. (downcase (match-string 2 field-flags))
  1077. "" t))))
  1078. ;; no flags
  1079. (setq field field-flags))
  1080. (unless (string= field "") (setq field-symbol (intern (downcase field))))
  1081. ;; Check, that no flags appear twice
  1082. (mapc (lambda (x)
  1083. (when (memq (car x) flags)
  1084. (if (cdr (assoc (cdr x) columns))
  1085. (org-index--create-new-index
  1086. nil
  1087. (format "More than one heading is marked with flag '%c'" (car x))))))
  1088. '((?s . sort)
  1089. (?c . copy)))
  1090. ;; Process flags
  1091. (if (memq ?s flags)
  1092. (setcdr (assoc 'sort columns) field-symbol))
  1093. (if (memq ?c flags)
  1094. (setcdr (assoc 'copy columns) (+ col 1)))
  1095. ;; Store columns in alist
  1096. (setq found (assoc field-symbol columns))
  1097. (when found
  1098. (if (> (cdr found) 0)
  1099. (org-index--create-new-index
  1100. nil
  1101. (format "'%s' appears two times as column heading" (downcase field))))
  1102. (setcdr found (+ col 1)))))
  1103. ;; check if all necessary informations have been specified
  1104. (mapc (lambda (col)
  1105. (unless (> (cdr (assoc col columns)) 0)
  1106. (org-index--create-new-index
  1107. nil
  1108. (format "column '%s' has not been set" col))))
  1109. '(ref link count created last-accessed))
  1110. ;; use ref as a default sort-column
  1111. (unless (cdr (assoc 'sort columns))
  1112. (setcdr (assoc 'sort columns) 'ref))
  1113. columns))
  1114. (defun org-index--create-new-index (create-new-index reason)
  1115. "Create a new empty index table with detailed explanation."
  1116. (let (prompt buffer-name title firstref id)
  1117. (setq prompt
  1118. (if create-new-index
  1119. (concat "There is this problem with the existing index table:\n\n " reason "\n\nThis assistant will guide you to create a new one.\n\nDo you want to proceed ?")
  1120. (concat "The existing index table contains this error:\n\n " reason "\n\nYou need to correct this error manually before proceeding. However, this assistant will help you to create an new initial index table with detailed comments, so that you may fix the errors in your existing table more easily.\n\nDo you want to proceed ?")))
  1121. (unless (y-or-n-p prompt)
  1122. (message "Cannot proceed without a valid index table: %s" reason)
  1123. ;; show existing index
  1124. (when (and org-index--buffer
  1125. org-index--point)
  1126. (org-pop-to-buffer-same-window org-index--buffer)
  1127. (goto-char org-index--point)
  1128. (org-show-context)
  1129. (show-subtree)
  1130. (recenter 1)
  1131. (delete-other-windows))
  1132. (throw 'created-new-index nil))
  1133. (setq buffer-name (org-completing-read "Please choose the buffer, where the new node for the index table should be created; the new node will be inserted at its end.\n\nBuffer: " (mapcar 'buffer-name (org-buffer-list)) nil nil))
  1134. (setq title (read-from-minibuffer "Please enter the title of the index node: "))
  1135. (while (progn
  1136. (setq firstref (read-from-minibuffer "Please enter your first reference-number. This is a number preceeded by some non-digit chars and optionally followed by some more non-digit chars, e.g. 'R1', '-1-' or '#1#' (and your initial number does not need to be '1'). The format of your reference-numbers only needs to make sense for yourself, so that you can spot it easily in your texts or write it on a piece of paper; it should however not already appear to frequently within your existing notes, to avoid too many false hits when searching.\n\nPlease choose: "))
  1137. (if (string-match "^[^0-9]+[0-9]+[^0-9]*$" firstref)
  1138. nil
  1139. (let (desc)
  1140. ;; firstref not okay, report details
  1141. (setq desc
  1142. (cond ((string= firstref "") "is empty")
  1143. ((not (string-match "^[^0-9]+" firstref)) "starts with a digit")
  1144. ((not (string-match "^[^0-9]+[0-9]+" firstref)) "does not contain a number")
  1145. ((not (string-match "^[^0-9]+[0-9]+[^0-9]*$" firstref)) "contains more than one sequence of digits")))
  1146. (read-from-minibuffer (format "Your input '%s' does not meet the requirements because it %s. Please hit RET and try again " firstref desc)))
  1147. t)))
  1148. (with-current-buffer buffer-name
  1149. (goto-char (point-max))
  1150. (insert (format "\n\n* %s %s\n" firstref title))
  1151. (insert "\n\n Below you find your initial index table, which will grow over time.\n"
  1152. " Following that your may read its detailed explanation, which will help you,\n"
  1153. " to adopt org-index to your needs. This however is optional reading and not\n"
  1154. " required to start using org-index.\n\n")
  1155. (setq id (org-id-get-create))
  1156. (insert (format "
  1157. | | | | | | comment |
  1158. | ref | link | created | count;s | last-accessed | ;c |
  1159. | | <4> | | | | |
  1160. |-----+------+---------+---------+---------------+---------|
  1161. | %s | %s | %s | | | %s |
  1162. "
  1163. firstref
  1164. id
  1165. (with-temp-buffer (org-insert-time-stamp nil nil t))
  1166. "This node"))
  1167. (insert "
  1168. Detailed explanation:
  1169. The index table above has three lines of headings above the first
  1170. hline:
  1171. - The first one is ignored by org-index, and you can use it to
  1172. give meaningful names to columns. In the table above only one
  1173. column has a name (\"comment\"). This line is optional.
  1174. - The second line is the most important one, because it
  1175. contains the configuration information for org-index; please
  1176. read further below for its format.
  1177. - The third line is again optional; it may only specify the
  1178. widths of the individual columns (e.g. <4>).
  1179. The columns get their meaning by the second line of headings;
  1180. specifically by one of the keywords (e.g. \"ref\") or a flag
  1181. seperated by a semicolon (e.g. \";s\").
  1182. The keywords and flags are:
  1183. - ref: This contains the reference, which consists of a decorated
  1184. number, which is incremented for each new line. References are
  1185. meant to be used in org-mode headlines or outside of org´,
  1186. e.g. within folder names.
  1187. - link: org-mode link pointing to the matching location within org.
  1188. - created: When has this line been created ?
  1189. - count: How many times has this line accessed ? The trailing
  1190. flag \"s\" makes the table beeing sorted after
  1191. this column, so that often used entries appear at the top of
  1192. the table.
  1193. - last-accessed: When has this line ben accessed
  1194. - The last column above has no keyword, only the flag \"c\",
  1195. which makes its content beeing copied under certain
  1196. conditions. It is typically used for comments.
  1197. The sequence of columns does not matter. You may reorder them any
  1198. way you like. Columns are found by their name, which appears in
  1199. the second line of headings.
  1200. You can add further columns or even remove the last column. All
  1201. other columns are required.
  1202. Finally: This node needs not be a top level node; its name is
  1203. completely at you choice; it is found through its ID only.
  1204. ")
  1205. (while (not (org-at-table-p)) (forward-line -1))
  1206. (org-table-align)
  1207. (while (not (org-at-heading-p)) (forward-line -1))
  1208. ;; present results to user
  1209. (if (and (not create-new-index)
  1210. org-index--buffer
  1211. org-index--point)
  1212. ;; we had an error with the existing table, so present old and new one
  1213. (progn
  1214. ;; show existing index
  1215. (org-pop-to-buffer-same-window org-index--buffer)
  1216. (goto-char org-index--point)
  1217. (org-show-context)
  1218. (show-subtree)
  1219. (recenter 1)
  1220. (delete-other-windows)
  1221. ;; show new index
  1222. (select-window (split-window-vertically))
  1223. (org-pop-to-buffer-same-window buffer-name)
  1224. (org-id-goto id)
  1225. (org-show-context)
  1226. (show-subtree)
  1227. (recenter 1)
  1228. (message "Please compare your existing index (upper window) and a temporary new one (lower window) to correct the previous error (\"%s\"); the explanations following the new index table should help." reason))
  1229. ;; Only show the new index
  1230. (org-pop-to-buffer-same-window buffer-name)
  1231. (delete-other-windows)
  1232. (org-id-goto id)
  1233. (org-show-context)
  1234. (show-subtree)
  1235. (recenter 1)
  1236. (setq org-index-id id)
  1237. (if (y-or-n-p "This is your new index table; Do you want to save its id to make it permanent ? ")
  1238. (progn
  1239. (customize-save-variable 'org-index-id id)
  1240. (message "Saved org-index-id '%s' to %s" org-index-id custom-file))
  1241. (let (sq)
  1242. (setq sq (format "(setq org-index-id \"%s\")" org-index-id))
  1243. (kill-new sq)
  1244. (message "Did not make the id of the new index permamanent; you may want to put\n\n %s\n\ninto your own initialization; it is copied already, just yank it." sq)))))
  1245. ;; cannot handle this situation in higher code, but do not want to finish with an error
  1246. (throw 'created-new-index nil)))
  1247. (defun org-index--update-line (ref-or-link)
  1248. (let (initial
  1249. found
  1250. count-field)
  1251. (with-current-buffer org-index--buffer
  1252. ;; search reference or link, if given (or assume, that we are already positioned right)
  1253. (when ref-or-link
  1254. (setq initial (point))
  1255. (goto-char org-index--below-hline)
  1256. (while (and (org-at-table-p)
  1257. (not (or (string= ref-or-link (org-index--get-field 'ref))
  1258. (string= ref-or-link (org-index--get-field 'link)))))
  1259. (forward-line)))
  1260. (if (not (org-at-table-p))
  1261. (error "Did not find reference or link '%s'" ref-or-link)
  1262. (setq count-field (org-index--get-field 'count))
  1263. ;; update count field only if number or empty; leave :missing: and :reuse: as is
  1264. (if (or (not count-field)
  1265. (string-match "^[0-9]+$" count-field))
  1266. (org-index--get-field 'count
  1267. (number-to-string
  1268. (+ 1 (string-to-number (or count-field "0"))))))
  1269. ;; update timestamp
  1270. (org-table-goto-column (org-index--column-num 'last-accessed))
  1271. (org-table-blank-field)
  1272. (org-insert-time-stamp nil t t)
  1273. (setq found t))
  1274. (if initial (goto-char initial))
  1275. found)))
  1276. (defun org-index--get-field (key &optional value)
  1277. (let (field)
  1278. (setq field (org-trim (org-table-get-field (cdr (assoc key org-index--columns)) value)))
  1279. (if (string= field "") (setq field nil))
  1280. field))
  1281. (defun org-index--column-num (key)
  1282. (cdr (assoc key org-index--columns)))
  1283. (defun org-index--make-guarded-search (ref &optional dont-quote)
  1284. (concat "\\b" (if dont-quote ref (regexp-quote ref)) "\\b"))
  1285. (defun org-index-get-ref-regex-format ()
  1286. "return cons-cell with regular expression and format for references"
  1287. (unless org-index--ref-regex
  1288. (org-index-1 'parse))
  1289. (cons (org-index--make-guarded-search org-index--ref-regex 'dont-quote) org-index--ref-format))
  1290. (defun org-index--do-occur (initial-search)
  1291. (let (
  1292. (occur-buffer-name "*org-index-occur*")
  1293. (word "") ; last word to search for growing and shrinking on keystrokes
  1294. (prompt "Search for: ")
  1295. words ; list of other words that must match too
  1296. occur-buffer
  1297. lines-to-show ; number of lines to show in window
  1298. start-of-lines ; position, where lines begin
  1299. left-off-at ; stack of last positions in index table
  1300. after-inserted ; in occur-buffer
  1301. lines-visible ; in occur-buffer
  1302. below-hline-bol ; below-hline and at bol
  1303. exit-gracefully ; true if normal exit
  1304. in-c-backspace ; true while processing C-backspace
  1305. ret from to key)
  1306. ;; clear buffer
  1307. (if (get-buffer "*org-index-occur*")
  1308. (kill-buffer occur-buffer-name))
  1309. (setq occur-buffer (get-buffer-create "*org-index-occur*"))
  1310. (with-current-buffer org-index--buffer
  1311. (let ((initial (point)))
  1312. (goto-char org-index--below-hline)
  1313. (forward-line 0)
  1314. (setq below-hline-bol (point))
  1315. (goto-char initial)))
  1316. (org-pop-to-buffer-same-window occur-buffer)
  1317. (toggle-truncate-lines 1)
  1318. (unwind-protect ; to reset cursor-shape even in case of errors
  1319. (progn
  1320. ;; fill in header
  1321. (erase-buffer)
  1322. (insert (concat "Incremental search, showing one window of matches.\n"
  1323. "Use DEL and C-DEL to erase, cursor keys to move, RET to find heading.\n\n"))
  1324. (setq start-of-lines (point))
  1325. (setq cursor-type 'hollow)
  1326. ;; get window size of occur-buffer as number of lines to be searched
  1327. (setq lines-to-show (+ (- (window-body-height) (line-number-at-pos)) 1))
  1328. ;; fill initially
  1329. (setq ret (org-index--get-matching-lines nil lines-to-show below-hline-bol))
  1330. (when (car ret)
  1331. (insert (cdr ret))
  1332. (setq left-off-at (cons (car ret) nil))
  1333. (setq after-inserted (cons (point) nil)))
  1334. ;; read keys
  1335. (while
  1336. (progn
  1337. (goto-char start-of-lines)
  1338. (setq lines-visible 0)
  1339. ;; use initial-search (if present) to simulate keyboard input
  1340. (if (and initial-search
  1341. (> (length initial-search) 0))
  1342. (progn
  1343. (setq key (string-to-char (substring initial-search 0 1)))
  1344. (if (length initial-search)
  1345. (setq initial-search (substring initial-search 1))))
  1346. (if in-c-backspace
  1347. (setq key 'backspace)
  1348. (setq key (read-event
  1349. (format "%s %s"
  1350. prompt
  1351. (mapconcat 'identity (reverse (cons word words)) ","))))
  1352. (setq exit-gracefully (memq key (list 'return 'up 'down 'left 'right)))))
  1353. (not exit-gracefully))
  1354. (cond
  1355. ((eq key 'C-backspace)
  1356. (setq in-c-backspace t))
  1357. ((eq key 'backspace) ; erase last char
  1358. (if (= (length word) 0)
  1359. ;; nothing more to delete
  1360. (setq in-c-backspace nil)
  1361. ;; unhighlight longer match
  1362. (let ((case-fold-search t))
  1363. (unhighlight-regexp (regexp-quote word)))
  1364. ;; chars left shorten word
  1365. (setq word (substring word 0 -1))
  1366. (when (= (length word) 0) ; when nothing left, use next word from list
  1367. (setq word (car words))
  1368. (setq words (cdr words))
  1369. (setq in-c-backspace nil))
  1370. ;; remove everything, that has been added for char just deleted
  1371. (when (cdr after-inserted)
  1372. (setq after-inserted (cdr after-inserted))
  1373. (goto-char (car after-inserted))
  1374. (delete-region (point) (point-max)))
  1375. ;; back up last position in index table too
  1376. (when (cdr left-off-at)
  1377. (setq left-off-at (cdr left-off-at)))
  1378. ;; go through buffer and check, if any invisible line should now be shown
  1379. (goto-char start-of-lines)
  1380. (while (< (point) (point-max))
  1381. (if (outline-invisible-p)
  1382. (progn
  1383. (setq from (line-beginning-position)
  1384. to (line-beginning-position 2))
  1385. ;; check for matches
  1386. (when (org-index--test-words (cons word words) (buffer-substring from to))
  1387. (when (<= lines-visible lines-to-show) ; show, if more lines required
  1388. (outline-flag-region from to nil)
  1389. (incf lines-visible))))
  1390. ;; already visible, just count
  1391. (incf lines-visible))
  1392. (forward-line 1))
  1393. ;; highlight shorter word
  1394. (unless (= (length word) 0)
  1395. (let ((case-fold-search t))
  1396. (highlight-regexp (regexp-quote word) 'isearch)))))
  1397. ((eq key ?,) ; comma: enter an additional search word
  1398. ;; push current word and clear, no need to change display
  1399. (setq words (cons word words))
  1400. (setq word ""))
  1401. ((and (characterp key)
  1402. (aref printable-chars key)) ; any other char: add to current search word
  1403. ;; unhighlight short word
  1404. (unless (= (length word) 0)
  1405. (let ((case-fold-search t))
  1406. (unhighlight-regexp (regexp-quote word))))
  1407. ;; add to word
  1408. (setq word (concat word (downcase (string key))))
  1409. ;; hide lines, that do not match longer word any more
  1410. (while (< (point) (point-max))
  1411. (unless (outline-invisible-p)
  1412. (setq from (line-beginning-position)
  1413. to (line-beginning-position 2))
  1414. ;; check for matches
  1415. (if (org-index--test-words (list word) (buffer-substring from to))
  1416. (incf lines-visible) ; count as visible
  1417. (outline-flag-region from to t))) ; hide
  1418. (forward-line 1))
  1419. ;; duplicate top of stacks; eventually overwritten below
  1420. (setq left-off-at (cons (car left-off-at) left-off-at))
  1421. (setq after-inserted (cons (car after-inserted) after-inserted))
  1422. ;; get new lines from index table
  1423. (when (< lines-visible lines-to-show)
  1424. (setq ret (org-index--get-matching-lines (cons word words)
  1425. (- lines-to-show lines-visible)
  1426. (car left-off-at)))
  1427. (when (car ret)
  1428. (insert (cdr ret))
  1429. (setcar left-off-at (car ret))
  1430. (setcar after-inserted (point))))
  1431. ;; highlight longer word
  1432. (let ((case-fold-search t))
  1433. (highlight-regexp (regexp-quote word) 'isearch)))))
  1434. ;; search is done collect and brush up results
  1435. ;; remove any lines, that are still invisible
  1436. (goto-char start-of-lines)
  1437. (while (< (point) (point-max))
  1438. (if (outline-invisible-p)
  1439. (delete-region (line-beginning-position) (line-beginning-position 2))
  1440. (forward-line 1)))
  1441. ;; get all the rest
  1442. (message "Getting all matches ...")
  1443. (setq ret (org-index--get-matching-lines (cons word words) 0 (car left-off-at)))
  1444. (message "done.")
  1445. (insert (cdr ret)))
  1446. ;; postprocessing even for non graceful exit
  1447. (setq cursor-type t)
  1448. ;; replace previous heading
  1449. (let ((numlines (count-lines (point) start-of-lines)))
  1450. (goto-char start-of-lines)
  1451. (forward-line -1)
  1452. (delete-region (point-min) (point))
  1453. (insert (format (concat (if exit-gracefully
  1454. "Search is done; showing all %d matches.\n"
  1455. "Search aborted; showing only some matches.\n")
  1456. "Use cursor keys to move, press RET to find heading.\n")
  1457. numlines)))
  1458. (forward-line))
  1459. ;; install keyboard-shortcuts within occur-buffer
  1460. (let ((keymap (make-sparse-keymap))
  1461. fun-on-ret)
  1462. (set-keymap-parent keymap text-mode-map)
  1463. (setq fun-on-ret (lambda () (interactive)
  1464. (let ((ref (org-index--get-field 'ref))
  1465. (link (org-index--get-field 'link)))
  1466. (org-index-1 'head
  1467. (or link ref) ;; prefer link
  1468. (if link t nil)))))
  1469. (define-key keymap (kbd "RET") fun-on-ret)
  1470. (use-local-map keymap)
  1471. ;; perform action according to last char
  1472. (cond
  1473. ((eq key 'return)
  1474. (funcall fun-on-ret))
  1475. ((eq key 'up)
  1476. (forward-line -1))
  1477. ((eq key 'down)
  1478. (forward-line 1))
  1479. ((eq key 'left)
  1480. (forward-char -1))
  1481. ((eq key 'right)
  1482. (forward-char 1))))))
  1483. (defun org-index--get-matching-lines (words numlines start-from)
  1484. (let ((numfound 0)
  1485. pos
  1486. initial line lines)
  1487. (with-current-buffer org-index--buffer
  1488. ;; remember initial pos and start at requested
  1489. (setq initial (point))
  1490. (goto-char start-from)
  1491. ;; loop over buffer until we have found enough lines
  1492. (while (and (or (< numfound numlines)
  1493. (= numlines 0))
  1494. (org-at-table-p))
  1495. ;; check each word
  1496. (setq line (buffer-substring (line-beginning-position) (line-beginning-position 2)))
  1497. (when (org-index--test-words words line)
  1498. (setq lines (concat lines line))
  1499. (incf numfound))
  1500. (forward-line 1)
  1501. (setq pos (point)))
  1502. ;; return to initial position
  1503. (goto-char initial))
  1504. (unless lines (setq lines ""))
  1505. (cons pos lines)))
  1506. (defun org-index--test-words (words line)
  1507. (let ((found-all t))
  1508. (setq line (downcase line))
  1509. (catch 'not-found
  1510. (dolist (w words)
  1511. (or (search w line)
  1512. (throw 'not-found nil)))
  1513. t)))
  1514. (defadvice org-mark-ring-goto (after org-index--advice-text-to-yank activate)
  1515. "Make text from org-index available for yank."
  1516. (when org-index--text-to-yank
  1517. (kill-new org-index--text-to-yank)
  1518. (message (format "Ready to yank '%s'" org-index--text-to-yank))
  1519. (setq org-index--text-to-yank nil)))
  1520. (provide 'org-index)
  1521. ;; Local Variables:
  1522. ;; fill-column: 75
  1523. ;; comment-column: 50
  1524. ;; End:
  1525. ;;; org-index.el ends here