Package is now called git, not git-core.
[planner-el.git] / planner-notes-index.el
blob0bccc8fff67b6945ff13cc02c2c9b3176aed84a1
1 ;;; planner-notes-index.el --- Note indexing support for the Emacs planner
3 ;; Copyright (C) 2004, 2005, 2006, 2008 Free Software Foundation, Inc.
4 ;; Parts copyright (C) 2006, 2007 Software Freedom Law Center
6 ;;; Commentary:
8 ;;;_+ Package description
10 ;; Author: Sandra Jean Chua <sacha@free.net.ph>
11 ;; Filename: planner-notes-index.el
12 ;; URL: http://www.wjsullivan.net/PlannerMode.html
14 ;; This file is part of Planner. It is not part of GNU Emacs.
16 ;; Planner is free software; you can redistribute it and/or modify it
17 ;; under the terms of the GNU General Public License as published by
18 ;; the Free Software Foundation; either version 3, or (at your option)
19 ;; any later version.
21 ;; Planner is distributed in the hope that it will be useful, but
22 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
23 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 ;; General Public License for more details.
26 ;; You should have received a copy of the GNU General Public License
27 ;; along with Planner; see the file COPYING. If not, write to the
28 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
29 ;; Boston, MA 02110-1301, USA.
31 ;;;_+ Usage
33 ;; Place planner-notes-index.el in your load path and add this to your
34 ;; .emacs:
36 ;; (require 'planner-notes-index)
38 ;; Then you can use tags of the form
40 ;; <planner-notes-index page="2004.03.02">
41 ;; <planner-notes-index from="2004.03.01" to="2004.03.31">
42 ;; <planner-notes-index limit="10">
43 ;; <planner-notes-index page="PlanPage">
44 ;; <planner-notes-index-month-table month="2004.03" limit="5">
45 ;; <planner-notes-index-month-table month="2004.03">
47 ;; You can also use the following interactive functions:
49 ;; planner-notes-index
50 ;; planner-notes-index-days
51 ;; planner-notes-index-weeks
52 ;; planner-notes-index-months
53 ;; planner-notes-index-years (wow!)
55 ;; These work based on the current date (date of current buffer, or today).
57 ;; If a single page is specified, this page is scanned for headlines
58 ;; of the form:
60 ;; .#1 Headline
62 ;; and the results are presented as a bulleted list.
64 ;; If FROM and TO are specified, all date pages between them (inclusive)
65 ;; are scanned. If FROM is omitted, it is assumed to be the earliest entry.
66 ;; If TO is omitted, it is assumed to be the latest entry.
68 ;; If RECENT is specified, the list includes only that many recent headlines.
70 ;; and the results are presented as a bulleted list.
72 ;; To customize presentation, you can write a function that generates
73 ;; the appropriate <planner-notes-index> tags. You can also use
74 ;; planner-extract-note-headlines in your own functions.
76 ;;;_+ Contributors
78 ;; Yann Hodique helped port this to Muse.
80 (require 'planner)
81 (require 'calendar)
83 ;;; Code:
85 ;;;_* Internal functions
87 ;; Old names, but then planner-notes-get-headlines was created.
88 ;; These may disappear.
89 (defalias 'planner-notes-index-get-headlines
90 'planner-notes-index-headlines-on-page)
91 (defalias 'planner-notes-index-get-headlines-range
92 'planner-notes-index-headlines-in-range)
94 (defun planner-notes-index-headlines-on-page (page &optional limit)
95 "Return a list of headlines in PAGE.
96 If LIMIT is non-nil, only that many headlines are returned."
97 (when (stringp limit) (setq limit (string-to-number limit)))
98 (with-temp-buffer
99 (insert-file-contents (planner-page-file page))
100 (planner-notes-get-headlines limit)))
102 (defun planner-notes-index-headlines-in-range (&optional from to limit)
103 "Return a list of headlines over a span of day pages.
104 FROM (inclusive) and TO (inclusive) limit the dates. If FROM is
105 nil, start from the earliest entry. If TO is nil, include the
106 last entry. If LIMIT is non-nil, return at most LIMIT entries."
107 (with-planner
108 (when (and limit (stringp limit)) (setq limit (string-to-number limit)))
109 (let ((pages (mapcar 'car (planner-get-day-pages from to)))
110 data
111 headlines)
112 (while (and pages (if limit (> limit 0) t))
113 (setq data (planner-notes-index-headlines-on-page (car pages) limit))
114 (when limit
115 (setq limit (- limit (length data))))
116 (when data
117 (add-to-list 'headlines
118 (cons (car pages) data) t))
119 (setq pages (cdr pages)))
120 headlines)))
122 (defun planner-notes-index-insert-as-list (page-headlines &optional
123 limit prefix suffix)
124 "Link and format PAGE-HEADLINES.
125 PAGE-HEADLINES is a list of the form (page ((anchor headline) ...).
126 If LIMIT is non-nil, only display that number of recent items.
127 If PREFIX is non-nil, it is prepended to the item.
128 If SUFFIX is non-nil, it is appended to the item."
129 (when (and limit (stringp limit)) (setq limit (string-to-number limit)))
130 (let ((page (car page-headlines)))
131 (setq page-headlines (cdr page-headlines))
132 (while (and page-headlines (if limit (> limit 0) t))
133 (when prefix (insert prefix))
134 (insert (planner-make-link (concat page (caar page-headlines))
135 (cdr (car page-headlines))
137 (when suffix (insert suffix))
138 (setq page-headlines (cdr page-headlines))
139 (when limit (setq limit (1- limit))))))
141 ;;;_* Emacs-wiki tags
143 ;;;###autoload
144 (defun planner-notes-index-tag (tag-beg tag-end attrs)
145 "Mark up planner-notes-index tags.
147 Tags can be of the form:
149 <planner-notes-index page=\"2004.03.02\">
150 <planner-notes-index from=\"2004.03.01\" to=\"2004.03.31\">
151 <planner-notes-index limit=\"10\">"
152 (let (last-month last-year)
153 (mapc
154 (lambda (item)
155 (when (string-match planner-date-regexp (car item))
156 (unless (and last-year
157 (string= (match-string 1 (car item)) last-year))
158 (insert "* "
159 (setq last-year (match-string 1 (car item)))
160 "\n\n"))
161 (unless (and last-month
162 (string= (match-string 2 (car item)) last-month))
163 (insert "** " last-year "."
164 (setq last-month (match-string 2 (car item))) "\n\n")))
165 (insert "*** " (car item) "\n\n")
166 (planner-notes-index-insert-as-list item nil " - " "\n")
167 (insert "\n"))
168 (if (assoc "page" attrs)
169 (cons (cdr (assoc "page" attrs))
170 (planner-notes-index-headlines-on-page
171 (cdr (assoc "page" attrs))
172 (cdr (assoc "limit" attrs))))
173 (planner-notes-index-headlines-in-range
174 (cdr (assoc "from" attrs))
175 (cdr (assoc "to" attrs))
176 (cdr (assoc "limit" attrs)))))))
178 ;;;###autoload
179 (defun planner-notes-index-month-table-tag (beg end attrs)
180 "Mark up a month note index. Tag is from BEG to END.
181 ATTRS is a list of attributes. \"Month\" is a yyyy.mm
182 string (default: current month). \"Limit\" is the maximum number
183 of items per day (default: all).
185 Examples:
186 <planner-notes-index-month-table month=\"2004.02\">
187 <planner-notes-index-month-table month=\"2004.02\" limit=\"4\">"
188 (let ((month (cdr (assoc "month" attrs)))
189 (limit (cdr (assoc "limit" attrs)))
190 day headlines last dow)
191 (unless month
192 (setq month (substring (planner-get-current-date-filename) 0 7)))
193 (when limit (setq limit (string-to-number limit)))
194 (setq last (planner-filename-to-calendar-date (concat month ".31")))
195 (setq last (calendar-last-day-of-month (elt last 0) (elt last 2)))
196 ;; Chronologically sorted, at least by day
197 (setq headlines
198 (nreverse
199 (planner-notes-index-headlines-in-range
200 (concat month ".01") (concat month ".31")))) ;; works
201 ;; Offset to negative if month does not start on Sunday
202 (setq day (- 1 (calendar-day-of-week
203 (planner-filename-to-calendar-date (concat month ".01")))))
204 (setq dow 0)
205 (insert
206 "<table class=\"month_notes\">"
207 "<tr><th>Sun</th><th>Mon</th><th>Tue</th><th>Wed</th>"
208 "<th>Thu</th><th>Fri</th><th>Sat</th></tr>")
209 (while (or (< day 31) (< day 6))
210 (when (= dow 0) (insert "<tr>"))
211 (insert
212 "<td><div class=\"month_day\">"
213 (if (and (>= day 1) (<= day last))
214 (format (concat "[[%s" planner-date-separator "%02d]]") month day)
216 "</div><div class=\"month_details\">\n")
217 (let ((data (assoc (format (concat "%s" planner-date-separator "%02d")
218 month day)
219 headlines))
220 extra)
221 (planner-notes-index-insert-as-list
222 data limit nil "<br>")
224 (when (and limit data (> (length data) limit))
225 (setq extra (- (length data) limit))
226 (insert (format "%d more entr%s" extra
227 (if (= extra 1) "y" "ies")))))
228 (insert "</div></td>")
229 (when (= dow 6)
230 (insert "</tr>"))
231 (setq day (1+ day))
232 (setq dow (% (1+ dow) 7)))
233 (insert "</table>")))
235 ;;;_* Other user functions
237 (defvar planner-notes-index-buffer "*Notes Index*"
238 "Buffer for planner note index.")
240 ;;;###autoload
241 (defun planner-notes-index (&optional from to limit)
242 "Display a clickable notes index.
243 If called from a Lisp program, display only dates between FROM
244 and TO. With a prefix arg LIMIT, display only that number of
245 entries."
246 (interactive "i
249 (when limit (setq limit (prefix-numeric-value limit)))
250 (let ((headlines
251 (planner-notes-index-headlines-in-range from to limit))
252 last-month last-year)
253 (with-current-buffer (get-buffer-create planner-notes-index-buffer)
254 (setq buffer-read-only nil)
255 (erase-buffer)
256 (cd (planner-directory))
257 (mapcar (lambda (item)
258 (when (string-match planner-date-regexp (car item))
259 (unless (and last-year
260 (string= (match-string 1 (car item)) last-year))
261 (insert "* "
262 (setq last-year (match-string 1 (car item)))
263 "\n\n"))
264 (unless (and last-month
265 (string= (match-string 2 (car item)) last-month))
266 (insert "** " last-year "."
267 (setq last-month
268 (match-string 2 (car item))) "\n\n")))
269 (insert "*** " (car item) "\n\n")
270 (planner-notes-index-insert-as-list item nil " - " "\n")
271 (insert "\n"))
272 headlines)
273 (planner-mode)
274 (goto-char (point-min))
275 (pop-to-buffer (current-buffer)))))
277 ;;;###autoload
278 (defun planner-notes-index-days (days)
279 "Display an index of notes posted over the past few DAYS.
280 The list ends with the day of the current buffer or `planner-today'."
281 (interactive (list (read-string "Number of days (1): " nil nil "1")))
282 (when (stringp days) (setq days (string-to-number days)))
283 (planner-notes-index
284 (planner-calculate-date-from-day-offset
285 (planner-get-current-date-filename) (- 1 days))
286 (planner-get-current-date-filename)))
288 ;;;###autoload
289 (defun planner-notes-index-weeks (weeks)
290 "Display an index of notes posted over the past few WEEKS.
291 The list ends with the week of the current buffer or `planner-today'.
292 Weeks start from Sunday."
293 (interactive (list (read-string "Number of weeks (1): " nil nil "1")))
294 (when (stringp weeks) (setq weeks (string-to-number weeks)))
295 (let ((date (planner-filename-to-calendar-date
296 (planner-get-current-date-filename))))
297 (planner-notes-index
298 (planner-date-to-filename
299 (encode-time 0 0 0 (- (elt date 1)
300 (calendar-day-of-week date)
301 (* (- weeks 1) 7))
302 (elt date 0) (elt date 2)))
303 (planner-get-current-date-filename))))
305 ;;;###autoload
306 (defun planner-notes-index-months (months)
307 "Display an index of notes posted over the past few MONTHS.
308 The list ends with the month of the current buffer or `planner-today'."
309 (interactive (list (read-string "Number of months (1): " nil nil "1")))
310 (when (stringp months) (setq months (string-to-number months)))
311 (let ((date (planner-filename-to-calendar-date
312 (planner-get-current-date-filename))))
313 (planner-notes-index
314 (planner-date-to-filename
315 (encode-time 0 0 0 1
316 (- (elt date 0)
317 months -1) (elt date 2)))
318 (planner-get-current-date-filename))))
320 ;;;###autoload
321 (defun planner-notes-index-years (years)
322 "Display an index of notes posted over the past few YEARS.
323 The current year is included."
324 (interactive (list (read-string "Number of years (1): " nil nil "1")))
325 (when (stringp years) (setq years (string-to-number years)))
326 (let ((date (planner-filename-to-calendar-date (planner-today))))
327 (planner-notes-index
328 (planner-date-to-filename
329 (encode-time 0 0 0 1
331 (- (elt date 2)
332 years -1)))
333 (planner-get-current-date-filename))))
335 ;;;_* Initialization
337 (add-hook 'muse-publish-markup-tags
338 (if (featurep 'muse-nestable-tags)
339 '("planner-notes-index" nil t nil planner-notes-index-tag)
340 '("planner-notes-index" nil t planner-notes-index-tag)))
341 (add-hook 'muse-publish-markup-tags
342 (if (featurep 'muse-nestable-tags)
343 '("planner-notes-index-month-table" nil t nil
344 planner-notes-index-month-table-tag)
345 '("planner-notes-index-month-table" nil t
346 planner-notes-index-month-table-tag)))
348 (provide 'planner-notes-index)
350 ;;;_* Local emacs vars.
352 ;; Local variables:
353 ;; allout-layout: (* : )
354 ;; End:
356 ;;; planner.el ends here