summaryrefslogtreecommitdiff
path: root/contrib/lisp/org-favtable.el
blob: 3a6bb88092386925cbeec338707da74d2444862a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
;;; org-favtable.el --- Lookup table of favorite references and links

;; Copyright (C) 2011-2014 Free Software Foundation, Inc.

;; Author: Marc-Oliver Ihm <org-favtable@ferntreffer.de>
;; Keywords: hypermedia, matching
;; Requires: org
;; Download: http://orgmode.org/worg/code/elisp/org-favtable.el
;; Version: 2.2.0

;; This file is not part of GNU Emacs.

;;; License:

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.

;;; Commentary:

;; Purpose:
;;
;;  Mark and find your favorite things and locations in org easily: Create
;;  and update a lookup table of your references and links. Often used
;;  entries bubble to the top and entering some keywords displays only the
;;  matching entries. That way the right entry one can be picked easily.
;;
;;  References are essentially small numbers (e.g. "R237" or "-455-"),
;;  which are created by this package; they are well suited to be used
;;  outside of org. Links are just normal org-mode links.
;;
;;
;; Setup:
;;
;;  - Add these lines to your .emacs:
;;
;;    (require 'org-favtable)
;;    ;; Good enough to start, but later you should probably
;;    ;; change this id, as will be explained below
;;    (setq org-favtable-id "00e26bef-1929-4110-b8b4-7eb9c9ab1fd4")
;;    ;; Optionally assign a key. Pick your own favorite.
;;    (global-set-key (kbd "C-+") 'org-favtable)
;;
;;  - Just invoke `org-favtable', which will explain how to complete your
;;    setup by creating the necessary table of favorites.
;;
;;
;; Further reading:
;;
;;  Invoke `org-favtable' and pick one of its help options. You may also
;;  read the documentation of `org-favtable-id' for setup instructions, of
;;  `org-favtable' for regular usage and of `org-favtable--commands' for a
;;  list of available commands.
;;

;;; Change Log:

;;   [2013-02-28 Th] Version 2.2.0:
;;    - Allowed shortcuts like "h237" for command "head" with argument "237"
;;    - Integrated with org-mark-ring-goto
;;
;;   [2013-01-25 Fr] Version 2.1.0:
;;    - Added full support for links
;;    - New commands "missing" and "statistics"
;;    - Renamed the package from "org-reftable" to "org-favtable"
;;    - Additional columns are required (e.g. "link"). Error messages will
;;      guide you
;;
;;   [2012-12-07 Fr] Version 2.0.0:
;;    - The format of the table of favorites has changed ! You need to bring
;;      your existing table into the new format by hand (which however is
;;      easy and explained below)
;;    - Reference table can be sorted after usage count or date of last access
;;    - Ask user explicitly, which command to invoke
;;    - Renamed the package from "org-refer-by-number" to "org-reftable"

;;   [2012-09-22 Sa] Version 1.5.0:
;;    - New command "sort" to sort a buffer or region by reference number
;;    - New commands "highlight" and "unhighlight" to mark references

;;   [2012-07-13 Fr] Version 1.4.0:
;;    - New command "head" to find a headline with a reference number

;;   [2012-04-28 Sa] Version 1.3.0:
;;    - New commands occur and multi-occur
;;    - All commands can now be invoked explicitly
;;    - New documentation
;;    - Many bugfixes

;;   [2011-12-10 Sa] Version 1.2.0:
;;    - Fixed a bug, which lead to a loss of newly created reference numbers
;;    - Introduced single and double prefix arguments
;;    - Started this Change Log

;;; Code:

(require 'org-table)
(require 'cl)

(defvar org-favtable--version "2.2.0")
(defvar org-favtable--preferred-command nil)

(defvar org-favtable--commands '(occur head ref link enter leave goto + help reorder fill sort update highlight unhighlight missing statistics)
  "List of commands known to org-favtable:

Commands known:

  occur: If you supply a keyword (text): Apply emacs standard
    occur operation on the table of favorites; ask for a
    string (keyword) to select lines. Occur will only show you
    lines which contain the given keyword, so you can easily find
    the right one. You may supply a list of words seperated by
    comma (\",\"), to select lines that contain any or all of the
    given words.

    If you supply a reference number: Apply emacs standard
    multi-occur operation all org-mode buffers to search for a
    specific reference.

    You may also read the note at the end of this help on saving
    the keystroke RET to accept this frequent default command.

  head: If invoked outside the table of favorites, ask for a
    reference number and search for a heading containing it. If
    invoked within favtable dont ask; rather use the reference or
    link from the current line.

  ref: Create a new reference, copy any previously selected text.
    If already within reftable, fill in ref-column.

  link: Create a new line in reftable with a link to the current node.
    Do not populate the ref column; this can later be populated by
    calling the \"fill\" command from within the reftable.

  leave: Leave the table of favorites. If the last command has
    been \"ref\", the new reference is copied and ready to yank.
    This \"org-mark-ring-goto\" and can be called several times
    in succession.

  enter: Just enter the node with the table of favorites.

  goto: Search for a specific reference within the table of
    favorites.

  help: Show this list of commands.

  +: Show all commands including the less frequently used ones
    given below. If \"+\" is followd by enough letters of such a
    command (e.g. \"+fi\"), then this command is invoked
    directly.

  reorder: Temporarily reorder the table of favorites, e.g. by
    count, reference or last access.

  fill: If either ref or link is missing, fill it.

  sort: Sort a set of lines (either the active region or the
    whole buffer) by the references found in each line.

  update: For the given reference, update the line in the
    favtable.

  highlight: Highlight references in region or buffer.

  unhighlight: Remove highlights.

  missing : Search for missing reference numbers (which do not
    appear in the reference table). If requested, add additional
    lines for them, so that the command \"new\" is able to reuse
    them.

  statistics : Show some statistics (e.g. minimum and maximum
    reference) about favtable.



Two ways to save keystrokes:

When prompting for a command, org-favtable puts the most likely
one (e.g. \"occur\" or \"ref\") at the front of the list, so that
you may just type RET.

If this command needs additional input (like e.g. \"occur\"), you
may supply this input right away, although you are still beeing
prompted for the command. So do an occur for the string \"foo\",
you can just enter \"foo\" without even entering \"occur\".


Another way to save keystrokes applies if you want to choose a
command, that requrires a reference number (and would normally
prompt for it): In that case you may just enter enough characters
from your command, so that it appears first in the list of
matches; then immediately enter the number of the reference you
are searching for. So the input \"h237\" would execute the
command \"head\" for reference \"237\" right away.

")

(defvar org-favtable--commands-some '(occur head ref link leave enter goto + help))

(defvar org-favtable--columns nil)

(defvar org-favtable-id nil
  "Id of the Org-mode node, which contains the favorite table.

Read below, on how to set up things. See the help options
\"usage\" and \"commands\" for normal usage after setup.

Setup requires two steps:

 - Adjust your .emacs initialization file

 - Create a suitable org-mode node


Here are the lines, you need to add to your .emacs:

  (require 'org-favtable)
  ;; Good enough to start, but later you should probably
  ;; change this id, as will be explained below
  (setq org-favtable-id \"00e26bef-1929-4110-b8b4-7eb9c9ab1fd4\")
  ;; Optionally assign a key. Pick your own favorite.
  (global-set-key (kbd \"C-+\") 'org-favtable)

Do not forget to restart emacs to make these lines effective.


As a second step you need to create the org-mode node, where your
reference numbers and links will be stored. It may look like
this:

  * org-favtable
    :PROPERTIES:
    :ID:       00e26bef-1929-4110-b8b4-7eb9c9ab1fd4
    :END:


    |     |      | Comment, description, details  |         |         |               |
    | ref | link | ;c                             | count;s | created | last-accessed |
    |     | <4>  | <30>                           |         |         |               |
    |-----+------+--------------------------------+---------+---------+---------------|
    | R1  |      | My first reference             |         |         |               |


You may just copy this node into one of your org-files.  Many
things however can or should be adjusted:

 - The node needs not be a top level node.

 - Its name is completely at you choice. The node is found
   through its ID.

 - There are three lines of headings above the first hline. The
   first one is ignored by org-favtable, and you can use them to
   give meaningful names to columns; the second line contains
   configuration information for org-favtable; please read
   further below for its format. The third line is optional and
   may contain width-informations (e.g. <30>) only.

 - The sequence of columns does not matter. You may reorder them
   any way you like; e.g. make the comment-column the last
   columns within the table. Columns ar found by their name,
   which appears in the second heading-line.

 - You can add further columns or even remove the
   \"Comment\"-column. All other columns from the
   example (e.g. \"ref\", \"link\", \"count\", \"created\" and
   \"last-accessed\") are required.

 - Your references need not start at \"R1\"; However, having an
   initial row is required (it serves as a template for subsequent
   references).

 - Your reference need not have the form \"R1\"; you may just as
   well choose any text, that contains a single number,
   e.g. \"reference-{1}\" or \"#7\" or \"++17++\" or \"-344-\". The
   function `org-favtable' will inspect your first reference and
   create all subsequent references in the same way.

 - You may want to change the ID-Property of the node above and
   create a new one, which is unique (and not just a copy of
   mine). You need to change it in the lines copied to your .emacs
   too. However, this is not strictly required to make things
   work, so you may do this later, after trying out this package.


Optionally you may tweak the second header line to adjust
`org-favtable' a bit. In the example above it looks like this
 (with spaces collapsed):


    | ref | link | ;c | count;s | created | last-accessed |


The different fields have different meanings:

 - ref : This denotes the column which contains you references

 - link : Column for org-mode links, which can be used to access
   locations within your files.

 - ;c : The flag \"c\" (\"c\" for \"copy\") denotes this column
   as the one beeing copied on command \"leave\". In the example
   above, it is also the comment-column.

 - count;s : this is the column which counts, how many time this
   line has been accessed (which is the key-feature of this
   package). The flag \"s\" stands for \"sort\", so the table is
   sorted after this column. You may also sort after columns
   \"ref\" or \"last-accessed\".

 - created : Date when this line was created.

 - last-accessed : Date and time, when this line was last accessed.


After this two-step setup process you may invoke `org-favtable'
to create a new favorite. Read the help option \"usage\" for
instructions on normal usage, read the help option \"commands\"
for help on single commands.

")


(defvar org-favtable--text-to-yank nil)
(defvar org-favtable--last-action nil)
(defvar org-favtable--occur-buffer nil)
(defvar org-favtable--ref-regex nil)
(defvar org-favtable--ref-format nil)



(defun org-favtable  (&optional what search search-is-link)
  "Mark and find your favorite items and org-locations easily:
Create and update a lookup table of your favorite references and
links. Often used entries automatically bubble to the top of the
table; entering some keywords narrows it to just the matching
entries; that way the right one can be picked easily.

References are essentially small numbers (e.g. \"R237\" or
\"-455-\"), as created by this package; links are normal org-mode
links. Within org-favtable, both are denoted as favorites.


Read below for a detailed description of this function. See the
help option \"setup\" or read the documentation of
`org-favtable-id' for setup instructions.

The function `org-favtable' operates on a dedicated table (called
the table or favorites or favtable, for short) within a special
Org-mode node. The node has to be created as part of your initial
setup. Each line of the favorite table contains:

 - A reference (optional)

 - A link (optional)

 - A number; counting, how often each reference has been
   used. This number is updated automatically and the table can
   be sorted according to it, so that most frequently used
   references appear at the top of the table and can be spotted
   easily.

 - Its respective creation date

 - Date and time of last access. This column can alternatively be
   used to sort the table.

To be useful, your table of favorites should probably contain a
column with comments too, which allows lines to be selected by
keywords.

The table of favorites is found through the id of the containing
node; this id should be stored within `org-favtable-id' (see there
for details).


The function `org-favtable' is the only interactive function of
this package and its sole entry point; it offers several commands
to create, find and look up these favorites (references and
links). All of them are explained within org-favtable's help.


Finally, org-favtable can also be invoked from elisp; the two
optional arguments accepted are:

         search : string to search for
           what : symbol of the command to invoke
 search-is-link : t, if argument search is actually a link

An example would be:

 (org-favtable \"237\" 'head)   ;; find heading with ref 237

"

  (interactive "P")

  (let (within-node        ; True, if we are within node with favtable
        result-is-visible  ; True, if node or occur is visible in any window
        ref-node-buffer-and-point ; cons with buffer and point of favorites node
        below-cursor              ; word below cursor
        active-region             ; active region (if any)
        link-id                   ; link of starting node, if required
        guarded-search            ; with guard against additional digits
        search-is-ref             ; true, if search is a reference
        commands                ; currently active set of selectable commands
        what-adjusted           ; True, if we had to adjust what
        what-input    ; Input on what question (need not necessary be "what")
        reorder-once  ; Column to use for single time sorting
        parts         ; Parts of a typical reference number (which
                                                  ; need not be a plain number); these are:
        head               ; Any header before number (e.g. "R")
        maxref             ; Maximum number from reference table (e.g. "153")
        tail               ; Tail after number (e.g. "}" or "")
        ref-regex          ; Regular expression to match a reference
        has-reuse          ; True, if table contains a line for reuse
        numcols            ; Number of columns in favtable
        kill-new-text      ; Text that will be appended to kill ring
        message-text       ; Text that will be issued as an explanation,
                           ; what we have done
        initial-ref-or-link      ; Initial position in reftable
        )

    ;;
    ;; Examine current buffer and location, before turning to favtable
    ;;

    ;; Get the content of the active region or the word under cursor
    (if (and transient-mark-mode
             mark-active)
        (setq active-region (buffer-substring (region-beginning) (region-end))))
    (setq below-cursor (thing-at-point 'symbol))


    ;; Find out, if we are within favable or not
    (setq within-node (string= (org-id-get) org-favtable-id))

    ;; Find out, if point in any window is within node with favtable
    (mapc (lambda (x) (with-current-buffer (window-buffer x)
                        (when (or
                               (string= (org-id-get) org-favtable-id)
                               (eq (window-buffer x)
                                   org-favtable--occur-buffer))
                          (setq result-is-visible t))))
          (window-list))



    ;;
    ;; Get decoration of references and highest reference from favtable
    ;;


    ;; Save initial ref or link
    (if (and within-node
             (org-at-table-p))
        (setq initial-ref-or-link
              (or (org-favtable--get-field 'ref)
                  (org-favtable--get-field 'link))))

    ;; Find node
    (setq ref-node-buffer-and-point (org-favtable--id-find))
    (unless ref-node-buffer-and-point
      (org-favtable--report-setup-error
       (format "Cannot find node with id \"%s\"" org-favtable-id)))

    ;; Get configuration of reftable; catch errors
    (let ((error-message
           (catch 'content-error

             (with-current-buffer (car ref-node-buffer-and-point)
               (save-excursion
                 (unless (string= (org-id-get) org-favtable-id)
                   (goto-char (cdr ref-node-buffer-and-point)))

                 ;; parse table while still within buffer
                 (setq parts (org-favtable--parse-and-adjust-table)))

               nil))))
      (when error-message
        (org-pop-to-buffer-same-window (car ref-node-buffer-and-point))
        (org-reveal)
        (error error-message)))

    ;; Give names to parts of configuration
    (setq head (nth 0 parts))
    (setq maxref (nth 1 parts))
    (setq tail (nth 2 parts))
    (setq numcols (nth 3 parts))
    (setq ref-regex (nth 4 parts))
    (setq has-reuse (nth 5 parts))
    (setq org-favtable--ref-regex ref-regex)
    (setq org-favtable--ref-format (concat head "%d" tail))

    ;;
    ;; Find out, what we are supposed to do
    ;;

    (if (equal what '(4)) (setq what 'leave))

    ;; Set preferred action, that will be the default choice
    (setq org-favtable--preferred-command
          (if within-node
              (if (memq org-favtable--last-action '(ref link))
                  'leave
                'occur)
            (if active-region
                'ref
              (if (and below-cursor (string-match ref-regex below-cursor))
                  'occur
                nil))))

    ;; Ask user, what to do
    (unless what
      (setq commands (copy-list org-favtable--commands-some))
      (while (progn
               (setq what-input
                     (org-icompleting-read
                      "Please choose: "
                      (mapcar 'symbol-name
                              ;; Construct unique list of commands with
                              ;; preferred one at front
                              (delq nil (delete-dups
                                         (append
                                          (list org-favtable--preferred-command)
                                          commands))))
                      nil nil))


               ;; if input starts with "+", any command (not only some) may follow
               ;; this allows input like "+sort" to be accepted
               (when (string= (substring what-input 0 1) "+")
                 ;; make all commands available for selection
                 (setq commands (copy-list org-favtable--commands))
                 (unless (string= what-input "+")
                   ;; not just "+", use following string
                   (setq what-input (substring what-input 1))

                   (let ((completions
                          ;; get list of possible completions for what-input
                          (all-completions what-input (mapcar 'symbol-name commands))))
                     ;; use it, if unambigously
                     (if (= (length completions) 1)
                         (setq what-input (car completions))))))


               ;; if input ends in digits, save them away and do completions on head of input
               ;; this allows input like "h224" to be accepted
               (when (string-match "^\\([^0-9+]\\)\\([0-9]+\\)\\s *$" what-input)
                 ;; use first match as input, even if ambigously
                 (setq org-favtable--preferred-command
                       (intern (first (all-completions (match-string 1 what-input)
                                                       (mapcar 'symbol-name commands)))))
                 ;; use digits as argument to commands
                 (setq what-input (format org-favtable--ref-format
                                          (string-to-number (match-string 2 what-input)))))

               (setq what (intern what-input))

               ;; user is not required to input one of the commands; if
               ;; not, take the first one and use the original input for
               ;; next question
               (if (memq what commands)
                   ;; input matched one element of list, dont need original
                   ;; input any more
                   (setq what-input nil)
                 ;; what-input will be used for next question, use first
                 ;; command for what
                 (setq what (or org-favtable--preferred-command
                                (first commands)))
                 ;; remove any trailing dot, that user might have added to
                 ;; disambiguate his input
                 (if (equal (substring what-input -1) ".")
                     ;; but do this only, if dot was really necessary to
                     ;; disambiguate
                     (let ((shortened-what-input (substring what-input 0 -1)))
                       (unless (test-completion shortened-what-input
                                                (mapcar 'symbol-name
                                                        commands))
                         (setq what-input shortened-what-input)))))

               ;; ask for reorder in loop, because we have to ask for
               ;; what right again
               (if (eq what 'reorder)
                   (setq reorder-once
                         (intern
                          (org-icompleting-read
                           "Please choose column to reorder reftable once: "
                           (mapcar 'symbol-name '(ref count last-accessed))
                           nil t))))

               ;; maybe ask initial question again
               (memq what '(reorder +)))))


    ;;
    ;; Get search, if required
    ;;

    ;; These actions need a search string:
    (when (memq what '(goto occur head update))

      ;; Maybe we've got a search string from the arguments
      (unless search
        (let (search-from-table
              search-from-cursor)

          ;; Search string can come from several sources:
          ;; From ref column of table
          (when within-node
            (setq search-from-table (org-favtable--get-field 'ref)))
          ;; From string below cursor
          (when (and (not within-node)
                     below-cursor
                     (string-match (concat "\\(" ref-regex "\\)")
                                   below-cursor))
            (setq search-from-cursor (match-string 1 below-cursor)))

          ;; Depending on requested action, get search from one of the sources above
          (cond ((eq what 'goto)
                 (setq search (or what-input search-from-cursor)))
                ((memq what '(head occur))
                 (setq search (or what-input search-from-table search-from-cursor))))))


      ;; If we still do not have a search string, ask user explicitly
      (unless search

        (if what-input
            (setq search what-input)
          (setq search (read-from-minibuffer
                        (cond ((memq what '(occur head))
                               "Text or reference number to search for: ")
                              ((eq what 'goto)
                               "Reference number to search for, or enter \".\" for id of current node: ")
                              ((eq what 'update)
                               "Reference number to update: ")))))

        (if (string-match "^\\s *[0-9]+\\s *$" search)
            (setq search (format "%s%s%s" head (org-trim search) tail))))

      ;; Clean up and examine search string
      (if search (setq search (org-trim search)))
      (if (string= search "") (setq search nil))
      (setq search-is-ref (string-match ref-regex search))

      ;; Check for special case
      (when (and (memq what '(head goto))
                 (string= search "."))
        (setq search (org-id-get))
        (setq search-is-link t))

      (when search-is-ref
        (setq guarded-search (org-favtable--make-guarded-search search)))

      ;;
      ;; Do some sanity checking before really starting
      ;;

      ;; Correct requested action, if nothing to search
      (when (and (not search)
                 (memq what '(search occur head)))
        (setq what 'enter)
        (setq what-adjusted t))

      ;; For a proper reference as input, we do multi-occur
      (if (and (string-match ref-regex search)
               (eq what 'occur))
          (setq what 'multi-occur))

      ;; Check for invalid combinations of arguments; try to be helpful
      (when (and (memq what '(head goto))
                 (not search-is-link)
                 (not search-is-ref))
        (error "Can do '%s' only for a reference or link (not '%s'), try 'occur' to search for text" what search)))


    ;;
    ;; Prepare
    ;;

    ;; Get link if required before moving in
    (if (eq what 'link)
        (setq link-id (org-id-get-create)))

    ;; Move into table, if outside
    (when (memq what '(enter ref link goto occur multi-occur missing statistics))

      ;; Support orgmode-standard of going back (buffer and position)
      (org-mark-ring-push)

      ;; Switch to favtable
      (org-pop-to-buffer-same-window (car ref-node-buffer-and-point))
      (goto-char (cdr ref-node-buffer-and-point))
      (show-subtree)
      (org-show-context)

      ;; sort favtable
      (org-favtable--sort-table reorder-once))

    ;; Goto back to initial ref, because reformatting of table above might
    ;; have moved point
    (when initial-ref-or-link
      (while (and (org-at-table-p)
                  (not (or
                        (string= initial-ref-or-link (org-favtable--get-field 'ref))
                        (string= initial-ref-or-link (org-favtable--get-field 'link)))))
        (forward-line))
      ;; did not find ref, go back to top
      (if (not (org-at-table-p)) (goto-char top)))


    ;;
    ;; Actually do, what is requested
    ;;

    (cond


     ((eq what 'help)

      (let ((help-what
             ;; which sort of help ?
             (intern
              (concat
               "help-"
               (org-icompleting-read
                "Help on: "
                (mapcar 'symbol-name '(commands usage setup version example))
                nil t)))))

        ;; help is taken from docstring of functions or variables
        (cond ((eq help-what 'help-commands)
               (org-favtable--show-help 'org-favtable--commands))
              ((eq help-what 'help-usage)
               (org-favtable--show-help 'org-favtable))
              ((eq help-what 'help-setup)
               (org-favtable--show-help 'org-favtable-id))
              ((eq help-what 'help-version)
               (org-favtable-version)))))


     ((eq what 'multi-occur)

      ;; Conveniently position cursor on number to search for
      (org-favtable--goto-top)
      (let (found (initial (point)))
        (while (and (not found)
                    (forward-line)
                    (org-at-table-p))
          (save-excursion
            (setq found (string= search
                                 (org-favtable--get-field 'ref)))))
        (if found
            (org-favtable--update-line nil)
          (goto-char initial)))

      ;; Construct list of all org-buffers
      (let (buff org-buffers)
        (dolist (buff (buffer-list))
          (set-buffer buff)
          (if (string= major-mode "org-mode")
              (setq org-buffers (cons buff org-buffers))))

        ;; Do multi-occur
        (multi-occur org-buffers guarded-search)
        (if (get-buffer "*Occur*")
            (progn
              (setq message-text (format "multi-occur for '%s'" search))
              (setq org-favtable--occur-buffer (get-buffer "*Occur*"))
              (other-window 1)
              (toggle-truncate-lines 1))
          (setq message-text (format "Did not find '%s'" search)))))


     ((eq what 'head)

      (let (link)
        ;; link either from table or passed in as argument

        ;; try to get link
        (if search-is-link
            (setq link (org-trim search))
          (if (and within-node
                   (org-at-table-p))
              (setq link (org-favtable--get-field 'link))))

        ;; use link if available
        (if (and link
                 (not (string= link "")))
            (progn
              (org-id-goto link)
              (org-favtable--update-line search)
              (setq message-text "Followed link"))

          (message (format "Scanning headlines for '%s' ..." search))
          (let (buffer point)
            (if (catch 'found
                  (progn
                    ;; loop over all headlines, stop on first match
                    (org-map-entries
                     (lambda ()
                       (when (looking-at (concat ".*" guarded-search))
                         ;; remember location and bail out
                         (setq buffer (current-buffer))
                         (setq point (point))
                         (throw 'found t)))
                     nil 'agenda)
                    nil))

                (progn
                  (org-favtable--update-line search)
                  (setq message-text (format "Found '%s'" search))
                  (org-pop-to-buffer-same-window buffer)
                  (goto-char point)
                  (org-reveal))
              (setq message-text (format "Did not find '%s'" search)))))))


     ((eq what 'leave)

      (when result-is-visible

        ;; If we are within the occur-buffer, switch over to get current line
        (if (and (string= (buffer-name) "*Occur*")
                 (eq org-favtable--last-action 'occur))
            (occur-mode-goto-occurrence)))

      (setq kill-new-text org-favtable--text-to-yank)
      (setq org-favtable--text-to-yank nil)

      ;; If "leave" has been called two times in succession, make
      ;; org-mark-ring-goto believe it has been called two times too
      (if (eq org-favtable--last-action 'leave)
          (let ((this-command nil) (last-command nil))
            (org-mark-ring-goto 1))
        (org-mark-ring-goto 0)))


     ((eq what 'goto)

      ;; Go downward in table to requested reference
      (let (found (initial (point)))
        (org-favtable--goto-top)
        (while (and (not found)
                    (forward-line)
                    (org-at-table-p))
          (save-excursion
            (setq found
                  (string= search
                           (org-favtable--get-field
                            (if search-is-link 'link 'ref))))))
        (if found
            (progn
              (setq message-text (format "Found '%s'" search))
              (org-favtable--update-line nil)
              (org-table-goto-column (org-favtable--column-num 'ref))
              (if (looking-back " ") (backward-char))
              ;; remember string to copy
              (setq org-favtable--text-to-yank
                    (org-trim (org-table-get-field (org-favtable--column-num 'copy)))))
          (setq message-text (format "Did not find '%s'" search))
          (goto-char initial)
          (forward-line)
          (setq what 'missed))))


     ((eq what 'occur)

      ;; search for string: occur
      (let (search-regexp
            all-or-any
            (search-words (split-string search "," t)))

        (if (< (length search-words) 2)
            ;; only one word to search; use it as is
            (setq search-regexp search)
          ;; construct regexp to match any of the words (maybe throw out some matches later)
          (setq search-regexp
                (mapconcat (lambda (x) (concat "\\(" x "\\)")) search-words "\\|"))
          (setq all-or-any
                (intern
                 (org-icompleting-read
                  "Two or more words have been specified; show lines, that match: " '("all" "any")))))

        (save-restriction
          (org-narrow-to-subtree)
          (occur search-regexp)
          (widen)
          (if (get-buffer "*Occur*")
              (with-current-buffer "*Occur*"

                ;; install helpful keyboard-shortcuts within occur-buffer
                (let ((keymap (make-sparse-keymap)))
                  (set-keymap-parent keymap occur-mode-map)

                  (define-key keymap (kbd "RET")
                    (lambda () (interactive)
                      (org-favtable--occur-helper 'head)))

                  (define-key keymap (kbd "<C-return>")
                    (lambda () (interactive)
                      (org-favtable--occur-helper 'multi-occur)))

                  (define-key keymap (kbd "<M-return>")
                    (lambda () (interactive)
                      (org-favtable--occur-helper 'goto)))

                  (define-key keymap (kbd "<C-M-return>")
                    (lambda () (interactive)
                      (org-favtable--occur-helper 'update)))

                  (use-local-map keymap))

                ;; Brush up occur buffer
                (other-window 1)
                (toggle-truncate-lines 1)
                (let ((inhibit-read-only t))
                  ;; insert some help text
                  (insert (substitute-command-keys
                           "Type RET to find heading, C-RET for multi-occur, M-RET to go to occurence and C-M-RET to update line in reftable.\n\n"))
                  (forward-line 1)

                  ;; when matching all of multiple words, remove all lines that do not match one of the words
                  (when (eq all-or-any 'all)
                    (mapc (lambda (x) (keep-lines x)) search-words))

                  ;; replace description from occur
                  (when all-or-any
                    (forward-line -1)
                    (kill-line)
                    (let ((count (- (count-lines (point) (point-max)) 1)))
                      (insert (format "%d %s for %s of %s"
                                      count
                                      (if (= count 1) "match" "matches")
                                      all-or-any
                                      search)))
                    (forward-line)
                    (beginning-of-line))

                  ;; Record link or reference for each line in
                  ;; occur-buffer, that is linked into reftable. Because if
                  ;; we later realign the reftable and then reuse the occur
                  ;; buffer, the original links might point nowehere.
                  (save-excursion
                    (while (not (eq (point) (point-max)))
                      (let ((beg (line-beginning-position))
                            (end (line-end-position))
                            pos ref link)

                        ;; occur has saved the position into a special property
                        (setq pos (get-text-property (point) 'occur-target))
                        (when pos
                          ;; but this property might soon point nowhere; so retrieve ref-or-link instead
                          (with-current-buffer (marker-buffer pos)
                            (goto-char pos)
                            (setq ref (org-favtable--get-field 'ref))
                            (setq link (org-favtable--get-field 'link))))
                        ;; save as text property
                        (put-text-property beg end 'org-favtable--ref ref)
                        (put-text-property beg end 'org-favtable--link link))
                      (forward-line))))

                (setq message-text
                      (format  "Occur for '%s'" search)))
            (setq message-text
                  (format "Did not find any matches for '%s'" search))))))


     ((memq what '(ref link))

      ;; add a new row (or reuse existing one)
      (let (new)

        (when (eq what 'ref)
            ;; go through table to find first entry to be reused
          (when has-reuse
            (org-favtable--goto-top)
            ;; go through table
            (while (and (org-at-table-p)
                        (not new))
              (when (string=
                     (org-favtable--get-field 'count)
                     ":reuse:")
                (setq new (org-favtable--get-field 'ref))
                (if new (org-table-kill-row)))
              (forward-line)))

          ;; no ref to reuse; construct new reference
          (unless new
            (setq new (format "%s%d%s" head (1+ maxref) tail)))

          ;; remember for org-mark-ring-goto
          (setq org-favtable--text-to-yank new))

        ;; insert ref or link as very first row
        (org-favtable--goto-top)
        (org-table-insert-row)

        ;; fill special columns with standard values
        (when (eq what 'ref)
          (org-table-goto-column (org-favtable--column-num 'ref))
          (insert new))
        (when (eq what 'link)
          (org-table-goto-column (org-favtable--column-num 'link))
          (insert link-id))
        (org-table-goto-column (org-favtable--column-num 'created))
        (org-insert-time-stamp nil nil t)

        ;; goto first empty field
        (unless (catch 'empty
                  (dotimes (col numcols)
                    (org-table-goto-column (+ col 1))
                    (if (string= (org-trim (org-table-get-field)) "")
                        (throw 'empty t))))
          ;; none found, goto first
          (org-table-goto-column 1))

        (org-table-align)
        (if active-region (setq kill-new-text active-region))
        (if (eq what 'ref)
            (setq message-text (format "Adding a new row with ref '%s'" new))
          (setq message-text (format "Adding a new row linked to '%s'" link-id)))))


     ((eq what 'enter)

      ;; simply go into table
      (org-favtable--goto-top)
      (show-subtree)
      (recenter)
      (if what-adjusted
          (setq message-text "Nothing to search for; at favtable")
        (setq message-text "At favtable")))


     ((eq what 'fill)

      ;; check, if within reftable
      (unless (and within-node
                   (org-at-table-p))
        (error "Not within table of favorites"))

      ;; applies to missing refs and missing links alike
      (let ((ref (org-favtable--get-field 'ref))
            (link (org-favtable--get-field 'link)))

        (if (and (not ref)
                 (not link))
            ;; have already checked this during parse, check here anyway
            (error "Columns ref and link are both empty in this line"))

        ;; fill in new ref
        (if (not ref)
            (progn
              (setq kill-new-text (format "%s%d%s" head (1+ maxref) tail))
              (org-favtable--get-field 'ref kill-new-text)
              ;; remember for org-mark-ring-goto
              (setq org-favtable--text-to-yank kill-new-text)
              (org-id-goto link)
              (setq message-text "Filled reftable field with new reference"))

          ;; fill in new link
          (if (not link)
              (progn
                (setq guarded-search (org-favtable--make-guarded-search ref))
                (message (format "Scanning headlines for '%s' ..." ref))
                (let (link)
                  (if (catch 'found
                        (org-map-entries
                         (lambda ()
                           (when (looking-at (concat ".*" guarded-search))
                             (setq link (org-id-get-create))
                             (throw 'found t)))
                         nil 'agenda)
                        nil)

                      (progn
                        (org-favtable--get-field 'link link)
                        (setq message-text "Inserted link"))

                    (setq message-text (format "Did not find reference '%s'" ref)))))

            ;; nothing is missing
            (setq message-text "Columns 'ref' and 'link' are already filled; nothing to do")))))


     ((eq what 'sort)

      ;; sort lines according to contained reference
      (let (begin end where)
        (catch 'aborted
          ;; either active region or whole buffer
          (if (and transient-mark-mode
                   mark-active)
              ;; sort only region
              (progn
                (setq begin (region-beginning))
                (setq end (region-end))
                (setq where "region"))
            ;; sort whole buffer
            (setq begin (point-min))
            (setq end (point-max))
            (setq where "whole buffer")
            ;; make sure
            (unless (y-or-n-p "Sort whole buffer ")
              (setq message-text "Sort aborted")
              (throw 'aborted nil)))

          (save-excursion
            (save-restriction
              (goto-char (point-min))
              (narrow-to-region begin end)
              (sort-subr nil 'forward-line 'end-of-line
                         (lambda ()
                           (if (looking-at (concat ".*"
                                                   (org-favtable--make-guarded-search ref-regex 'dont-quote)))
                               (string-to-number (match-string 1))
                             0))))
            (highlight-regexp ref-regex)
            (setq message-text (format "Sorted %s from character %d to %d, %d lines"
                                       where begin end
                                       (count-lines begin end)))))))


     ((eq what 'update)

      ;; simply update line in reftable
      (save-excursion
        (let ((ref-or-link (if search-is-link "link" "reference")))
          (beginning-of-line)
          (if (org-favtable--update-line search)
              (setq message-text (format "Updated %s '%s'" ref-or-link search))
            (setq message-text (format "Did not find %s '%s'" ref-or-link search))))))


     ((eq what 'parse)

      ;; Just parse the reftable, which is already done, so nothing to do
      )


     ((memq what '(highlight unhighlight))

      (let ((where "buffer"))
        (save-excursion
          (save-restriction
            (when (and transient-mark-mode
                       mark-active)
              (narrow-to-region (region-beginning) (region-end))
              (setq where "region"))

            (if (eq what 'highlight)
                (progn
                  (highlight-regexp ref-regex)
                  (setq message-text (format "Highlighted references in %s" where)))
              (unhighlight-regexp ref-regex)
              (setq message-text (format "Removed highlights for references in %s" where)))))))


     ((memq what '(missing statistics))

      (org-favtable--goto-top)
      (let (missing
            ref-field
            ref
            min
            max
            (total 0))

        ;; start with list of all references
        (setq missing (mapcar (lambda (x) (format "%s%d%s" head x tail))
                              (number-sequence 1 maxref)))

        ;; go through table and remove all refs, that we see
        (while (and (forward-line)
                    (org-at-table-p))

          ;; get ref-field and number
          (setq ref-field (org-favtable--get-field 'ref))
          (if (and ref-field
                   (string-match ref-regex ref-field))
              (setq ref (string-to-number (match-string 1 ref-field))))

          ;; remove existing refs from list
          (if ref-field (setq missing (delete ref-field missing)))

          ;; record min and max
          (if (or (not min) (< ref min)) (setq min ref))
          (if (or (not max) (> ref max)) (setq max ref))

          ;; count
          (setq total (1+ total)))

        ;; insert them, if requested
        (forward-line -1)
        (if (eq what 'statistics)

            (setq message-text (format "Found %d references from %s to %s. %d references below highest do not appear in table. "
                                       total
                                       (format org-favtable--format min)
                                       (format org-favtable--format max)
                                       (length missing)))

          (if (y-or-n-p (format "Found %d missing references; do you wish to append them to the table of favorites"
                                (length missing)))
              (let (type)
                (setq type (org-icompleting-read
                            "Insert new lines for reuse by command \"new\" or just as missing ? " '("reuse" "missing")))
                (mapc (lambda (x)
                        (let (org-table-may-need-update) (org-table-insert-row t))
                        (org-favtable--get-field 'ref x)
                        (org-favtable--get-field 'count (format ":%s:" type)))
                      missing)
                (org-table-align)
                (setq message-text (format "Inserted %d new lines for missing refernces" (length missing))))
            (setq message-text (format "%d missing references." (length missing)))))))


     (t (error "This is a bug: unmatched case '%s'" what)))


    ;; remember what we have done for next time
    (setq org-favtable--last-action what)

    ;; tell, what we have done and what can be yanked
    (if kill-new-text (setq kill-new-text
                            (substring-no-properties kill-new-text)))
    (if (string= kill-new-text "") (setq kill-new-text nil))
    (let ((m (concat
              message-text
              (if (and message-text kill-new-text)
                  " and r"
                (if kill-new-text "R" ""))
              (if kill-new-text (format "eady to yank '%s'" kill-new-text) ""))))
      (unless (string= m "") (message m)))
    (if kill-new-text (kill-new kill-new-text))))



(defun org-favtable--parse-and-adjust-table ()

  (let ((maxref 0)
        top
        bottom
        ref-field
        link-field
        parts
        numcols
        head
        tail
        ref-regex
        has-reuse
        initial-point)

    (setq initial-point (point))
    (org-favtable--goto-top)
    (setq top (point))

    (goto-char top)

    ;; count columns
    (org-table-goto-column 100)
    (setq numcols (- (org-table-current-column) 1))

    ;; get contents of columns
    (forward-line -2)
    (unless (org-at-table-p)
      (org-favtable--report-setup-error
       "Table of favorites starts with a hline" t))

    ;; check for optional line consisting solely of width specifications
    (beginning-of-line)
    (if (looking-at "\\s *|\\(\\(\\s *|\\)\\|\\(\\s *<[0-9]+>\\s *|\\)\\)+\\s *$")
        (forward-line -1))
    (org-table-goto-column 1)

    (setq org-favtable--columns (org-favtable--parse-headings numcols))

    ;; Go beyond end of table
    (while (org-at-table-p) (forward-line 1))

    ;; Kill all empty rows at bottom
    (while (progn
             (forward-line -1)
             (org-table-goto-column 1)
             (and
              (not (org-favtable--get-field 'ref))
              (not (org-favtable--get-field 'link))))
      (org-table-kill-row))
    (forward-line)
    (setq bottom (point))
    (forward-line -1)

    ;; Retrieve any decorations around the number within the first nonempty ref-field
    (goto-char top)
    (while (and (org-at-table-p)
                (not (setq ref-field (org-favtable--get-field 'ref))))
      (forward-line))

    ;; Some Checking
    (unless ref-field
      (org-favtable--report-setup-error
       "No line of reference column contains a number" t))

    (unless (string-match "^\\([^0-9]*\\)\\([0-9]+\\)\\([^0-9]*\\)$" ref-field)
      (org-favtable--report-setup-error
       (format "First reference in table table of favorites ('%s') does not contain a number" ref-field) t))


    ;; These are the decorations used within the first ref of favtable
    (setq head (match-string 1 ref-field))
    (setq tail (match-string 3 ref-field))
    (setq ref-regex (concat (regexp-quote head)
                            "\\([0-9]+\\)"
                            (regexp-quote tail)))

    ;; Go through table to find maximum number and do some checking
    (let ((ref 0))

      (while (org-at-table-p)

        (setq ref-field (org-favtable--get-field 'ref))
        (setq link-field (org-favtable--get-field 'link))

        (if (and (not ref-field)
                 (not link-field))
            (throw 'content-error "Columns ref and link are both empty in this line"))

        (if ref-field
            (if (string-match ref-regex ref-field)
                ;; grab number
                (setq ref (string-to-number (match-string 1 ref-field)))
              (throw 'content-error "Column ref does not contain a number")))

        ;; check, if higher ref
        (if (> ref maxref) (setq maxref ref))

        ;; check if ref is ment for reuse
        (if (string= (org-favtable--get-field 'count) ":reuse:")
            (setq has-reuse 1))

        (forward-line 1)))

    ;; sort used to be here

    (setq parts (list head maxref tail numcols ref-regex has-reuse))

    ;; go back to top of table
    (goto-char top)

    parts))



(defun org-favtable--sort-table (sort-column)

  (unless sort-column (setq sort-column (org-favtable--column-num 'sort)))

  (let (top
        bottom
        ref-field
        count-field
        count-special)


    ;; get boundaries of table
    (org-favtable--goto-top)
    (forward-line 0)
    (setq top (point))
    (while (org-at-table-p) (forward-line))
    (setq bottom (point))

    (save-restriction
      (narrow-to-region top bottom)
      (goto-char top)
      (sort-subr t
                 'forward-line
                 'end-of-line
                 (lambda ()
                   (let (ref
                         (ref-field (or (org-favtable--get-field 'ref) ""))
                         (count-field (or (org-favtable--get-field 'count) ""))
                         (count-special 0))

                     ;; get reference with leading zeroes, so it can be
                     ;; sorted as text
                     (string-match org-favtable--ref-regex ref-field)
                     (setq ref (format
                                "%06d"
                                (string-to-number
                                 (or (match-string 1 ref-field)
                                     "0"))))

                     ;; find out, if special token in count-column
                     (setq count-special (format "%d"
                                                 (- 2
                                                    (length (member count-field '(":missing:" ":reuse:"))))))

                     ;; Construct different sort-keys according to
                     ;; requested sort column; prepend count-special to
                     ;; sort special entries at bottom of table, append ref
                     ;; as a secondary sort key
                     (cond

                      ((eq sort-column 'count)
                       (concat count-special
                               (format
                                "%08d"
                                (string-to-number (or (org-favtable--get-field 'count)
                                                      "")))
                               ref))

                      ((eq sort-column 'last-accessed)
                       (concat count-special
                               (org-favtable--get-field 'last-accessed)
                               " "
                               ref))

                      ((eq sort-column 'ref)
                       (concat count-special
                               ref))

                      (t (error "This is a bug: unmatched case '%s'" sort-column)))))

                 nil 'string<)))

  ;; align table
  (org-table-align))


(defun org-favtable--goto-top ()

  ;; go to heading of node
  (while (not (org-at-heading-p)) (forward-line -1))
  (forward-line 1)
  ;; go to table within node, but make sure we do not get into another node
  (while (and (not (org-at-heading-p))
              (not (org-at-table-p))
              (not (eq (point) (point-max))))
    (forward-line 1))

  ;; check, if there really is a table
  (unless (org-at-table-p)
    (org-favtable--report-setup-error
     (format "Cannot find favtable within node %s" org-favtable-id) t))

  ;; go to first hline
  (while (and (not (org-at-table-hline-p))
              (org-at-table-p))
    (forward-line 1))

  ;; and check
  (unless (org-at-table-hline-p)
    (org-favtable--report-setup-error
     "Cannot find hline within table of favorites" t))

  (forward-line 1)
  (org-table-goto-column 1))



(defun org-favtable--id-find ()
  "Find org-favtable-id"
  (let ((marker (org-id-find org-favtable-id 'marker))
        marker-and-buffer)

    (if marker
        (progn
          (setq marker-and-buffer (cons (marker-buffer marker) (marker-position marker)))
          (move-marker marker nil)
          marker-and-buffer)
      nil)))



(defun org-favtable--parse-headings (numcols)

  (let (columns)

    ;; Associate names of special columns with column-numbers
    (setq columns (copy-tree '((ref . 0) (link . 0) (created . 0) (last-accessed . 0)
                               (count . 0) (sort . nil) (copy . nil))))

    ;; For each column
    (dotimes (col numcols)
      (let* (field-flags ;; raw heading, consisting of file name and maybe
                         ;; flags (seperated by ";")
             field       ;; field name only
             field-symbol ;; and as a symbol
             flags       ;; flags from field-flags
             found)

        ;; parse field-flags into field and flags
        (setq field-flags (org-trim (org-table-get-field (+ col 1))))
        (if (string-match "^\\([^;]*\\);\\([a-z]+\\)$" field-flags)
            (progn
              (setq field (downcase (or (match-string 1 field-flags) "")))
              ;; get flags as list of characters
              (setq flags (mapcar 'string-to-char
                                  (split-string
                                   (downcase (match-string 2 field-flags))
                                   "" t))))
          ;; no flags
          (setq field field-flags))

        (unless (string= field "") (setq field-symbol (intern (downcase field))))

        ;; Check, that no flags appear twice
        (mapc (lambda (x)
                (when (memq (car x) flags)
                  (if (cdr (assoc (cdr x) columns))
                      (org-favtable--report-setup-error
                       (format "More than one heading is marked with flag '%c'" (car x)) t))))
              '((?s . sort)
                (?c . copy)))

        ;; Process flags
        (if (memq ?s flags)
            (setcdr (assoc 'sort columns) field-symbol))
        (if (memq ?c flags)
            (setcdr (assoc 'copy columns) (+ col 1)))

        ;; Store columns in alist
        (setq found (assoc field-symbol columns))
        (when found
          (if (> (cdr found) 0)
              (org-favtable--report-setup-error
               (format "'%s' appears two times as column heading" (downcase field)) t))
          (setcdr found (+ col 1)))))

    ;; check if all necessary informations have been specified
    (mapc (lambda (col)
            (unless (> (cdr (assoc col columns)) 0)
              (org-favtable--report-setup-error
               (format "column '%s' has not been set" col) t)))
          '(ref link count created last-accessed))

    ;; use ref as a default sort-column
    (unless (cdr (assoc 'sort columns))
      (setcdr (assoc 'sort columns) 'ref))
    columns))



(defun org-favtable--report-setup-error (text &optional switch-to-node)

  (when switch-to-node
    (org-id-goto org-favtable-id)
    (delete-other-windows))

  (when (y-or-n-p (concat
                   text
                   ";\n"
                   "the correct setup is explained in the documentation of 'org-favtable-id'.\n"
                   "Do you want to read it ? "))
    (org-favtable--show-help 'org-favtable-id))

  (error "")
  (setq org-favtable--last-action 'leave))



(defun org-favtable--show-help (function-or-variable)

  (let ((isfun (functionp function-or-variable)))
    ;; bring up help-buffer for function or variable
    (if isfun
        (describe-function function-or-variable)
      (describe-variable function-or-variable))


    ;; clean up help-buffer
    (pop-to-buffer "*Help*")
    (let ((inhibit-read-only t))
      (goto-char (point-min))
      (while (progn
               (kill-line 1)
               (not (looking-at
                     (if isfun
                         "("
                       "Documentation:")))))
      (kill-line (if isfun 2 3))
      (goto-char (point-max))
      (kill-line -2)
      (goto-char (point-min)))))



(defun org-favtable--update-line (ref-or-link)

  (let (initial
        found
        count-field
        (ref-node-buffer-and-point (org-favtable--id-find)))

    (with-current-buffer (car ref-node-buffer-and-point)

      ;; search reference or link, if given (or assume, that we are already positioned right)
      (when ref-or-link
        (setq initial (point))
        (goto-char (cdr ref-node-buffer-and-point))
        (org-favtable--goto-top)
        (while (and (org-at-table-p)
                    (not (or (string= ref-or-link (org-favtable--get-field 'ref))
                             (string= ref-or-link (org-favtable--get-field 'link)))))
          (forward-line)))

      (if (not (org-at-table-p))
          (error "Did not find reference or link '%s'" ref-or-link)
        (setq count-field (org-favtable--get-field 'count))

        ;; update count field only if number or empty; leave :missing: and :reuse: as is
        (if (or (not count-field)
                (string-match "^[0-9]+$" count-field))
            (org-favtable--get-field 'count
                                    (number-to-string
                                     (+ 1 (string-to-number (or count-field "0"))))))

        ;; update timestamp
        (org-table-goto-column (org-favtable--column-num 'last-accessed))
        (org-table-blank-field)
        (org-insert-time-stamp nil t t)

        (setq found t))

      (if initial (goto-char initial))

      found)))



(defun org-favtable--occur-helper (action)
  (let ((line-beg (line-beginning-position))
        key search link ref)

    ;; extract reference or link from text property (as put there before)
    (setq ref (get-text-property line-beg 'org-favtable--ref))
    (if (string= ref "") (setq ref nil))
    (setq link (get-text-property line-beg 'org-favtable--link))
    (if (string= link "") (setq link nil))

    (org-favtable action
                  (or link ref) ;; prefer link
                  (if link t nil))))


(defun org-favtable--get-field (key &optional value)
  (let (field)
    (setq field (org-trim (org-table-get-field (cdr (assoc key org-favtable--columns)) value)))
    (if (string= field "") (setq field nil))

    field))


(defun org-favtable--column-num (key)
  (cdr (assoc key org-favtable--columns)))


(defun org-favtable-version ()
  "Show version of org-favtable" (interactive)
  (message "org-favtable %s" org-favtable--version))


(defun org-favtable--make-guarded-search (ref &optional dont-quote)
  (concat "\\b" (if dont-quote ref (regexp-quote ref)) "\\b"))


(defun org-favtable-get-ref-regex-format ()
  "return cons-cell with regular expression and format for references"
  (unless org-favtable--ref-regex
    (org-favtable 'parse))
  (cons (org-favtable--make-guarded-search org-favtable--ref-regex 'dont-quote) org-favtable--ref-format))


(defadvice org-mark-ring-goto (after org-favtable--advice-text-to-yank activate)
  "Make text from the favtable available for yank."
  (when org-favtable--text-to-yank
      (kill-new org-favtable--text-to-yank)
      (message (format "Ready to yank '%s'" org-favtable--text-to-yank))
      (setq org-favtable--text-to-yank nil)))


(provide 'org-favtable)

;; Local Variables:
;; fill-column: 75
;; comment-column: 50
;; End:

;;; org-favtable.el ends here