File: gdbmrecent.sl

package info (click to toggle)
jed-extra 2.5.7-2
  • links: PTS, VCS
  • area: main
  • in suites: bullseye, buster, sid, stretch
  • size: 2,972 kB
  • ctags: 2,490
  • sloc: makefile: 75; ruby: 43; sed: 38; sh: 31
file content (273 lines) | stat: -rw-r--r-- 7,805 bytes parent folder | download | duplicates (5)
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
% gdbmrecent.sl
% 
% $Id: gdbmrecent.sl,v 1.8 2008/03/22 14:44:00 paul Exp paul $
% Keywords: convenience
%
% Copyright (c) 2004-2008 Paul Boekholt.
% Released under the terms of the GNU GPL (version 2 or later).
% 
% Yet another recent mode. This one was written to test my gdbm module.
% You should get slgdbm version 1.7 or later from
% http://www.cheesit.com/downloads/slang/slgdbm.html
% or if you're on Debian install the slang-gdbm package.
% 
% Features:
% -delayed synchronization of the database. It should still be possible
%  to work in multiple instances at the same time
% -stores line and column information
% -goes to last line and column even when file is not opened from recent menu
% -recent menu shows only the most recent `Recent_Max_Cached_Files' files, but line and
%  column information is remembered for Recent_Files_Expire_Time days.
% -Make buffer-local variables persistent.  For now this can be used to make
%  a buffer's ispell dictionary setting persist.

require("gdbm");

provide("recent");
provide("gdbmrecent");
require ("sl_utils");

% customvariables.

% where to store the list
custom_variable("Recent_Db", dircat(Jed_Home_Directory, "recent_db"));
% length of the recent popup menu
custom_variable("Recent_Max_Cached_Files", 15);
% regexp for files not to be added to the recent list (/tmp/mutt-1234)
custom_variable("Recent_Files_Exclude_Pattern", "/tmp");
% Time before entries expire (in days)
custom_variable("Recent_Files_Expire_Time", 7);

% This is a comma-separated list of blocal variables that are stored in the
% recent files database.  The entry is still purged if a file is not opened
% for a week.  The blocal's values have to be strings, and for now `=' and `:'
% are not allowed in them.
custom_variable("Gdbm_Pvars", "ispell_dictionary");

% the idea of the recent_flag is that when a script opens a file that should
% not be added to the recent list it should say
% public variable recent_flag;
% ...
% recent_flag = 0;
% () = find_file("/some/file");
public variable recent_flag=1;

private variable recent_files=Assoc_Type[String_Type];

% Return a cache entry for the current buffer (see getbuf_info)
% (NULL if file matches Exclude Pattern or no file 
% associated with buffer)
private define getbuf_filemark()
{
   if(blocal_var_exists("no_recent")) return NULL;
   variable entry = buffer_filename();
   if (not strlen(entry) || string_match(entry, Recent_Files_Exclude_Pattern, 1))
     return NULL;
   variable varname, val=sprintf("%d:%d:%d",_time(), what_line_if_wide(), what_column());
   foreach varname (strchop(Gdbm_Pvars, ',', 0))
     {
	if (blocal_var_exists(varname))
	  val = sprintf("%s:%s=%s", val, varname, get_blocal_var(varname));
     }
   return [entry, val];
}

% find_file_after_hook
% goto line, column
% update date in assoc
private define open_hook ()
{
   ifnot (recent_flag)
     {
	create_blocal_var("no_recent");
	recent_flag = 1;
	return;
     }
   variable date, line=1, column=1, pvars="", pvar;
   variable filename = buffer_filename();
   if (not strlen(filename) || string_match(filename, Recent_Files_Exclude_Pattern, 1))
     return;
   
   variable val = NULL, db=gdbm_open(Recent_Db, GDBM_READER, 0600);
   if (db != NULL)
     val = db[filename];
   if (val != NULL && 3 <= sscanf(val, "%d:%d:%d%s", &date, &line, &column, &pvars))
     {
	% goto saved position
	goto_line(line);
	() = goto_column_best_try(column);
	% open folds
	loop(10) % while (is_line_hidden) might cause an infinite loop!
	  {
	     ifnot(is_line_hidden) break;
	     runhooks("fold_open_fold");
	  }
	if (strlen(pvars))
	  {
	     foreach pvar (strchop(pvars, ':', 0))
	       {
		  pvar=strchop(pvar, '=', 0);
		  if (2==length(pvar))
		    define_blocal_var(pvar[0], pvar[1]);
	       }
	  }
     }
   recent_files[filename] = sprintf("%d:%d:%d:%s", _time(), what_line(), 
				    what_column(), pvars);
}

% save hook
% update assoc
private define save_hook()
{
   _pop_n (_NARGS); % remove spurious arguments from stack
   variable filemark = getbuf_filemark();
   if (filemark == NULL) return;
   recent_files[filemark[0]] = filemark[1];
}

% save the recent files assoc to the database
private define update_db(db)
{
   variable k, v;
   foreach k, v (recent_files) using ("keys", "values")
     {
	% if write fails, stop trying
	if (gdbm_store(db, k, v, GDBM_REPLACE))
	  break;
     }
   recent_files=Assoc_Type[String_Type];
}


private define _get_files(db)
{
   variable keys, values, dates;
   (keys, values) = gdbm_get_keys_and_values(db);
   dates = array_map(Integer_Type, &atoi, values);
   keys = keys[array_sort(-dates)];
   return keys;
}

public define recent_get_files()
{
   variable db=gdbm_open(Recent_Db, GDBM_READER, 0600);
   if (db != NULL)
     _get_files(db);
}


% build the menu
private define menu_callback (popup)
{
   variable db=gdbm_open(Recent_Db, GDBM_WRCREAT, 0600);
   if (db == NULL) return vmessage("gdbm: %s", gdbm_error());
   update_db(db);
   variable keys, l, i;
   
   keys = _get_files(db);
   l = length(keys);
   
   if (l > Recent_Max_Cached_Files)
     l = Recent_Max_Cached_Files;
   
   _for i (0, l - 1, 1)
     {
	menu_append_item (popup, keys[i], sprintf ("() = find_file (\"%s\")", keys[i]));
     }  
}


% this shows a menu of at most Recent_Max_Cached_Files that were opened in the
% current buffer's directory or a subdirectory thereof.
private define recent_here_callback (popup)
{
   variable dir;
   ( , dir, , ) = getbuf_info();
   variable dirlen = strlen(dir);
   ifnot(dirlen) return;
   variable db=gdbm_open(Recent_Db, GDBM_WRCREAT, 0600);
   if (db == NULL) return vmessage("gdbm: %s", gdbm_error());
   update_db(db);
   variable keys, l, i;
   
   keys = _get_files(db);
   keys = keys[where(not array_map(Integer_Type, &strncmp, keys, dir, dirlen))];
   l = length(keys);
   
   if (l > Recent_Max_Cached_Files)
     l = Recent_Max_Cached_Files;
   
   _for i (0, l - 1, 1)
     {
	menu_append_item (popup, keys[i], sprintf ("() = find_file (\"%s\")", keys[i]));
     }  
}

private define purge_not_so_recent(db)
{
   variable key, keys, values, dates;
   (keys, values) = gdbm_get_keys_and_values(db);
   variable cutoff_date = _time() - 86400 * Recent_Files_Expire_Time;
   dates = array_map(Integer_Type, &atoi, values);
   keys = keys[where(dates < cutoff_date)];
   % it's not possible to use foreach(db) here
   % see info:(gdbm)Sequential
   foreach key (keys)
     {
	()=gdbm_delete(db, key);
     }
}

% update the cache's line/col info
% purge entries older than a week
private define exit_hook()
{
   try
     {
	variable db=gdbm_open(Recent_Db, GDBM_WRCREAT, 0600);
	variable filemark, buf;
	if (db != NULL)
	  {
	     % update line/col info
	     loop (buffer_list())
	       {
		  buf = ();
		  ifnot(strlen(buf)) continue;
		  sw2buf(buf);
		  filemark = getbuf_filemark();
		  if (filemark != NULL)
		    recent_files[filemark[0]] = filemark[1];
	       }
	     update_db(db);
	     purge_not_so_recent(db);
	     variable st=stat_file(Recent_Db);
	     if (st.st_size > 50000)
	       ()=gdbm_reorganize(db);
	  }
     }
   catch AnyError;
   return 1;   % tell _jed_exit_hooks to continue
}

private define add_recent_files_popup_hook (menubar)
{
   variable menu = "Global.&File";

   menu_insert_popup (0, menu, "&Recent Files");
   menu_set_select_popup_callback (strcat (menu, ".&Recent Files"),
				   &menu_callback);
   menu_insert_popup (1, menu, "&Recent Here");
   menu_set_select_popup_callback (strcat (menu, ".&Recent Here"),
				   &recent_here_callback);

}

append_to_hook ("_jed_find_file_after_hooks", &open_hook);
append_to_hook("_jed_save_buffer_after_hooks", &save_hook);

% update list at exit
append_to_hook("_jed_exit_hooks", &exit_hook);

append_to_hook ("load_popup_hooks", &add_recent_files_popup_hook);