-
Notifications
You must be signed in to change notification settings - Fork 268
/
Copy pathAgenda.py
319 lines (274 loc) · 11.6 KB
/
Agenda.py
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
# -*- coding: utf-8 -*-
from datetime import date
import os
import glob
import vim
from orgmode._vim import ORGMODE, get_bufnumber, get_bufname, echoe
from orgmode import settings
from orgmode.keybinding import Keybinding, Plug, Command
from orgmode.menu import Submenu, ActionEntry, add_cmd_mapping_menu
from orgmode.py3compat.encode_compatibility import *
from orgmode.py3compat.unicode_compatibility import *
from orgmode.py3compat.py_py3_string import *
class Agenda(object):
u"""
The Agenda Plugin uses liborgmode.agenda to display the agenda views.
The main task is to format the agenda from liborgmode.agenda.
Also all the mappings: jump from agenda to todo, etc are realized here.
"""
def __init__(self):
u""" Initialize plugin """
object.__init__(self)
# menu entries this plugin should create
self.menu = ORGMODE.orgmenu + Submenu(u'Agenda')
# key bindings for this plugin
# key bindings are also registered through the menu so only additional
# bindings should be put in this variable
self.keybindings = []
# commands for this plugin
self.commands = []
@classmethod
def _switch_to(cls, bufname, vim_commands=None):
u"""
Swicht to the buffer with bufname.
A list of vim.commands (if given) gets executed as well.
TODO: this should be extracted and imporved to create an easy to use
way to create buffers/jump to buffers. Otherwise there are going to be
quite a few ways to open buffers in vimorgmode.
"""
cmds = [
u'botright split org:%s' % bufname,
u'setlocal buftype=nofile',
u'setlocal modifiable',
u'setlocal nonumber',
# call opendoc() on enter the original todo item
u'nnoremap <silent> <buffer> <CR> :exec "%s ORGMODE.plugins[u\'Agenda\'].opendoc()"<CR>' % VIM_PY_CALL,
u'nnoremap <silent> <buffer> <TAB> :exec "%s ORGMODE.plugins[u\'Agenda\'].opendoc(switch=True)"<CR>' % VIM_PY_CALL,
u'nnoremap <silent> <buffer> <S-CR> :exec "%s ORGMODE.plugins[u\'Agenda\'].opendoc(split=True)"<CR>' % VIM_PY_CALL,
# statusline
u'setlocal statusline=Org\\ %s' % bufname]
if vim_commands:
cmds.extend(vim_commands)
for cmd in cmds:
vim.command(u_encode(cmd))
@classmethod
def _get_agendadocuments(self):
u"""
Return the org documents of the agenda files; return None if no
agenda documents are defined.
TODO: maybe turn this into an decorator?
"""
# load org files of agenda
agenda_files = settings.get(u'org_agenda_files', u',')
if not agenda_files or agenda_files == ',':
echoe(
u"No org_agenda_files defined. Use :let "
u"g:org_agenda_files=['~/org/index.org'] to add "
u"files to the agenda view.")
return
return self._load_agendafiles(agenda_files)
@classmethod
def _load_agendafiles(self, agenda_files):
# glob for files in agenda_files
resolved_files = []
for f in agenda_files:
f = glob.glob(os.path.join(
os.path.expanduser(os.path.dirname(f)),
os.path.basename(f)))
resolved_files.extend(f)
agenda_files = [os.path.realpath(f) for f in resolved_files]
# load the agenda files into buffers
for agenda_file in agenda_files:
vim.command(u_encode(u'badd %s' % agenda_file.replace(" ", "\\ ")))
# determine the buffer nr of the agenda files
agenda_nums = [get_bufnumber(fn) for fn in agenda_files]
# collect all documents of the agenda files and create the agenda
return [ORGMODE.get_document(i) for i in agenda_nums if i is not None]
@classmethod
def opendoc(cls, split=False, switch=False):
u"""
If you are in the agenda view jump to the document the item in the
current line belongs to. cls.line2doc is used for that.
:split: if True, open the document in a new split window.
:switch: if True, switch to another window and open the the document
there.
"""
row, _ = vim.current.window.cursor
try:
bufname, bufnr, destrow = cls.line2doc[row]
except:
return
# reload source file if it is not loaded
if get_bufname(bufnr) is None:
vim.command(u_encode(u'badd %s' % bufname))
bufnr = get_bufnumber(bufname)
tmp = cls.line2doc[row]
cls.line2doc[bufnr] = tmp
# delete old endry
del cls.line2doc[row]
if split:
vim.command(u_encode(u"sbuffer %s" % bufnr))
elif switch:
vim.command(u_encode(u"wincmd w"))
vim.command(u_encode(u"buffer %d" % bufnr))
else:
vim.command(u_encode(u"buffer %s" % bufnr))
vim.command(u_encode(u"normal! %dgg <CR>" % (destrow + 1)))
@classmethod
def list_next_week(cls):
agenda_documents = cls._get_agendadocuments()
if not agenda_documents:
return
cls.list_next_week_for(agenda_documents)
@classmethod
def list_next_week_for_buffer(cls):
agenda_documents = vim.current.buffer.name
loaded_agendafiles = cls._load_agendafiles([agenda_documents])
cls.list_next_week_for(loaded_agendafiles)
@classmethod
def list_next_week_for(cls, agenda_documents):
raw_agenda = ORGMODE.agenda_manager.get_next_week_and_active_todo(
agenda_documents)
# if raw_agenda is empty, return directly
if not raw_agenda:
vim.command('echom "All caught-up. No agenda or active todo next week."')
return
# create buffer at bottom
cmd = [u'setlocal filetype=orgagenda', ]
cls._switch_to(u'AGENDA', cmd)
# line2doc is a dic with the mapping:
# line in agenda buffer --> source document
# It's easy to jump to the right document this way
cls.line2doc = {}
# format text for agenda
last_date = None
final_agenda = [u'Week Agenda:']
for i, h in enumerate(raw_agenda):
# insert date information for every new date (not datetime)
active_date_no_time = h.active_date.date()
if active_date_no_time != last_date:
today = date.today()
# insert additional "TODAY" string
if active_date_no_time == today:
section = unicode(active_date_no_time) + u" TODAY"
today_row = len(final_agenda) + 1
else:
section = unicode(active_date_no_time)
final_agenda.append(section)
# update last_date
last_date = active_date_no_time
p = h
tags = []
while p is not None:
tags += p.tags
p = p.parent
bufname = os.path.basename(vim.buffers[h.document.bufnr].name)
bufname = bufname[:-4] if bufname.endswith(u'.org') else bufname
formatted = u" %(bufname)s (%(bufnr)d) %(todo)s %(timestr)s %(title)s %(tags)s" % {
'bufname': bufname,
'bufnr': h.document.bufnr,
'todo': h.todo,
'timestr': h.active_date.timestr(),
'title': h.title,
'tags': ':' + ':'.join(tags) + ':' if tags else ''
}
final_agenda.append(formatted)
cls.line2doc[len(final_agenda)] = (get_bufname(h.document.bufnr), h.document.bufnr, h.start)
# show agenda
vim.current.buffer[:] = [u_encode(i) for i in final_agenda]
vim.command(u_encode(u'setlocal nomodifiable conceallevel=2 concealcursor=nc'))
# try to jump to the position of today
try:
vim.command(u_encode(u'normal! %sgg<CR>' % today_row))
except:
pass
@classmethod
def list_all_todos(cls, current_buffer=False):
u""" List all todos in one buffer.
Args:
current_buffer (bool):
False: all agenda files
True: current org_file
"""
if current_buffer:
agenda_documents = vim.current.buffer.name
loaded_agendafiles = cls._load_agendafiles([agenda_documents])
else:
loaded_agendafiles = cls._get_agendadocuments()
if not loaded_agendafiles:
return
raw_agenda = ORGMODE.agenda_manager.get_todo(loaded_agendafiles)
cls.line2doc = {}
# create buffer at bottom
cmd = [u'setlocal filetype=orgagenda']
cls._switch_to(u'AGENDA', cmd)
# format text of agenda
final_agenda = []
for i, h in enumerate(raw_agenda):
tmp = u"%s %s" % (h.todo, h.title)
final_agenda.append(tmp)
cls.line2doc[len(final_agenda)] = (get_bufname(h.document.bufnr), h.document.bufnr, h.start)
# show agenda
vim.current.buffer[:] = [u_encode(i) for i in final_agenda]
vim.command(u_encode(u'setlocal nomodifiable conceallevel=2 concealcursor=nc'))
@classmethod
def list_timeline(cls):
"""
List a timeline of the current buffer to get an overview of the
current file.
"""
raw_agenda = ORGMODE.agenda_manager.get_timestamped_items(
[ORGMODE.get_document()])
# create buffer at bottom
cmd = [u'setlocal filetype=orgagenda']
cls._switch_to(u'AGENDA', cmd)
cls.line2doc = {}
# format text of agenda
final_agenda = []
for i, h in enumerate(raw_agenda):
tmp = fmt.format('{} {}', h.todo, h.title).lstrip().rstrip()
final_agenda.append(tmp)
cls.line2doc[len(final_agenda)] = (get_bufname(h.document.bufnr), h.document.bufnr, h.start)
# show agenda
vim.current.buffer[:] = [u_encode(i) for i in final_agenda]
vim.command(u_encode(u'setlocal nomodifiable conceallevel=2 concealcursor=nc'))
def register(self):
u"""
Registration of the plugin.
Key bindings and other initialization should be done here.
"""
add_cmd_mapping_menu(
self,
name=u"OrgAgendaTodo",
function=u'%s ORGMODE.plugins[u"Agenda"].list_all_todos()' % VIM_PY_CALL,
key_mapping=u'<localleader>cat',
menu_desrc=u'Agenda for all TODOs'
)
add_cmd_mapping_menu(
self,
name=u"OrgBufferAgendaTodo",
function=u'%s ORGMODE.plugins[u"Agenda"].list_all_todos(current_buffer=True)' % VIM_PY_CALL,
key_mapping=u'<localleader>caT',
menu_desrc=u'Agenda for all TODOs based on current buffer'
)
add_cmd_mapping_menu(
self,
name=u"OrgAgendaWeek",
function=u'%s ORGMODE.plugins[u"Agenda"].list_next_week()' % VIM_PY_CALL,
key_mapping=u'<localleader>caa',
menu_desrc=u'Agenda for the week'
)
add_cmd_mapping_menu(
self,
name=u"OrgBufferAgendaWeek",
function=u'%s ORGMODE.plugins[u"Agenda"].list_next_week_for_buffer()' % VIM_PY_CALL,
key_mapping=u'<localleader>caA',
menu_desrc=u'Agenda for the week based on current buffer'
)
add_cmd_mapping_menu(
self,
name=u'OrgAgendaTimeline',
function=u'%s ORGMODE.plugins[u"Agenda"].list_timeline()' % VIM_PY_CALL,
key_mapping=u'<localleader>caL',
menu_desrc=u'Timeline for this buffer'
)