]> git.donarmstrong.com Git - lib.git/blob - emacs_el/tiny-tools/tiny/tinyload.el
add tiny-tools
[lib.git] / emacs_el / tiny-tools / tiny / tinyload.el
1 ;;; tinyload.el --- Load set of packages when Emacs is idle (lazy load)
2
3 ;; This file is not part of Emacs
4
5 ;;{{{ Id
6
7 ;; Copyright (C)    1997-2007 Jari Aalto
8 ;; Keywords:        extensions
9 ;; Author:          Jari Aalto
10 ;; Maintainer:      Jari Aalto
11 ;;
12 ;; To get information on this program, call M-x tinyload-version.
13 ;; Look at the code with folding.el
14
15 ;; COPYRIGHT NOTICE
16 ;;
17 ;; This program is free software; you can redistribute it and/or modify it
18 ;; under the terms of the GNU General Public License as published by the Free
19 ;; Software Foundation; either version 2 of the License, or (at your option)
20 ;; any later version.
21 ;;
22 ;; This program is distributed in the hope that it will be useful, but
23 ;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24 ;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
25 ;; for more details.
26 ;;
27 ;; You should have received a copy of the GNU General Public License
28 ;; along with program; see the file COPYING. If not, write to the
29 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
30 ;; Boston, MA 02110-1301, USA.
31 ;;
32 ;; Visit <http://www.gnu.org/copyleft/gpl.html> for more information
33
34 ;;}}}
35 ;;{{{ Install
36
37 ;; ....................................................... &t-install ...
38 ;; Put this file on your Emacs-Lisp load path, add following into your
39 ;; ~/.emacs startup file. Move all your `require' commands into
40 ;; the load list.
41 ;;
42 ;;     (setq tinyload-:load-list '("package" "package" ...))
43 ;;     (require 'tinyload)
44 ;;
45 ;; TinyLoad can't be autoloaded, because it installs an idle-timer
46 ;; function.
47 ;;
48 ;; See examples at the end of file how do I utilize this package in full.
49 ;; If you have any questions, use 'submit' function. In case of error
50 ;; or misbehavior, turn on the debug and send the debug results
51 ;; From the *Messages* buffer and describe what was happening
52 ;;
53 ;;      M-x tinyload-debug-toggle
54 ;;      M-x tinyload-submit-bug-report
55
56 ;;}}}
57
58 ;;{{{ Documentation
59
60 ;; ..................................................... &t-commentary ...
61
62 ;;; Commentary:
63
64 ;;  Preface, Jul 1997
65 ;;
66 ;;      While it is possible to arrange Emacs `rc' (start-up) files to use
67 ;;      all possible and imaginable autoloads, there are still packages
68 ;;      that can't be autoloaded due to their setup nature or other
69 ;;      behavior: `require' commands are necessary in `.emacs' in order
70 ;;      to use those modules. This means that for every `require' command,
71 ;;      the Emacs startup slows remarkably. Experienced Emacs users have
72 ;;      very complex boot configurations, so waiting minutes for Emacs
73 ;;      startup screen to appear is quite frustrating.
74 ;;
75 ;;      The described situation gave birth to this package. Now the emacs
76 ;;      is ready to use within few seconds.
77 ;;
78 ;;      What this package does, is, that it caches the load requests and
79 ;;      executes them when it thinks there is free time. Instead of setting
80 ;;      up all at once on startup, the emacs configuration is built piece
81 ;;      by piece, until the whole 100% configuration is there.
82 ;;
83 ;;      The benefit is that Emacs starts instantly, and when it is
84 ;;      idle, the remaining packages, that you wanted to be
85 ;;      available in your daily Emacs session, are loaded.
86 ;;
87 ;;  Overview of features
88 ;;
89 ;;      o   Delayed (Lazy) loading of packages (at some later time); after
90 ;;          15 seconds of idle time, remaining files are loaded one by one.
91 ;;      o   You no longer have to use `require' in your .emacs, instead,
92 ;;          you define `tinyload-:load-list' where you put the requests.
93 ;;      o   Your .emacs starts faster when the extra `require' and
94 ;;          `load' commands be moved to load list.
95 ;;
96 ;;      If you're a first time Emacs user or if you consider lisp
97 ;;      difficult, have a look at simpler setup than what is described
98 ;;      below from C-h v `tinyload-:load-file'. The idea is that you
99 ;;      tell the configuration file which lists packages that you
100 ;;      want to load in format:
101 ;;
102 ;;          PACKAGE CONFIG-WORD
103 ;;
104 ;;      The CONFIG-WORD should be self explanatory: it instructs in which
105 ;;      OS and in which Emacs flavor the package is loaded. Here isa
106 ;;      sample: reportmail is only loaded under win32 and XEmacs.
107 ;;
108 ;;          paren
109 ;;          autorevert win32
110 ;;          gnus.el
111 ;;          reportmail  win32-xemacs
112 ;;
113 ;;      Another easy interface is to use functions:
114 ;;
115 ;;          `tinyload-load-list-add-function'
116 ;;          `tinyload-load-list-add-package'
117 ;;          `tinyload-load-list-delete-function'
118 ;;          `tinyload-load-list-delete-package'
119 ;;
120 ;;  First user notice
121 ;;
122 ;;      When you use this package for the first time, you may feel
123 ;;      uncomfortable with the amount of messages you see displayed on
124 ;;      the echo area. And if you're in echo-area prompt (e.g. after `C-x'
125 ;;      `C-f') those messages may disturb the echo area prompt.
126 ;;
127 ;;      Just don't panic. Move your cursor key to the left (C-a)
128 ;;      or start typing and the load will be interrupted. As long as
129 ;;      there is activity in your Emacs the load will not happen.
130 ;;
131 ;;      The messages that are displayed in the echo area are important,
132 ;;      because they get stored in *Messages* buffers and you can take a
133 ;;      look if anything strange happened. Like if some package couldn't be
134 ;;      loaded at all. Pay attention to *fatal* messages.
135 ;;
136 ;;  Messages in *Message* buffer
137 ;;
138 ;;      There are different types of messages
139 ;;
140 ;;          TinyLoad: fdb ok      (10% 1/10)                         [1]
141 ;;          TinyLoad: elp 'noerr! (20% 2/10)                         [2]
142 ;;          TinyLoad: [ERROR] loading  ~/elisp/rc/emacs-rc-init.el   [3]
143 ;;
144 ;;      o   [1] Package was loaded and the display shows some remaining
145 ;;          statistics.
146 ;;      o   [2] There was 'noerr parameter defined and the
147 ;;          recent load of the package failed: perhaps it didn't exist along
148 ;;          `load-path' or there was other problem in the package itself.
149 ;;      o   [3] When file was loaded, some error happened. You
150 ;;          should study this file by hand and spot the problem manually.
151 ;;          Be sure that the syntax of the file is correct.
152 ;;
153 ;;      In addition to these basic messages, there are some internal
154 ;;      messages that do not concern regular user, only the maintainer.
155 ;;      When TinyLoad wakes up, you might see following message
156 ;;
157 ;;          Tinyload: timer expired; invoking load process..[busy;stop;N]
158 ;;                                                           |    |    |
159 ;;                                        user activity status    |    |
160 ;;                                                  Continue status    |
161 ;;                                    Busy count; and deadlock indicator
162 ;;
163 ;;      Which simply means that Emacs called the loader function and
164 ;;      because *Continue* *status* was nil, user did nothing at the time
165 ;;      of invocation. If the message [busy;stop;N] then user was doing
166 ;;      something that weren't allowed to be interrupted. Usually this
167 ;;      happens when cursor is in echo area e.g. after `C-x' `C-f'.
168 ;;      If the cursor never leaves the echo area or if the busy situation
169 ;;      continues for a certain period of time, the program automatically
170 ;;      clears the busy signal and continues loading. You should not see
171 ;;      infinite [busy;stop.N] messages. If you really see 10 such messages,
172 ;;      then contact the author: there must be an unresolved deadlock and
173 ;;      a bug in the program.
174 ;;
175 ;;      When the `tinyload-:load-list' has been handled, the loader process
176 ;;      terminates itself. The following message tells that the process has
177 ;;      ceased to exist. If you want to start reading the list again,
178 ;;      call `M-x' `tinyload-install'.
179 ;;
180 ;;          TinyLoad: Loader process terminated.
181 ;;
182 ;;  Tutorial
183 ;;
184 ;;      Let's supposes your emacs startup consists of following `rc' files
185 ;;      The name `rc' comes from Unix resource files, like
186 ;;      $HOME/.bashrc, $HOME/.cshrc ...
187 ;;
188 ;;          emacs-rc-main.el     -- the main load controller
189 ;;          emacs-rc-path.el     -- settings `auto-mode-alist' etc.
190 ;;          emacs-rc-bup.el      -- Backup settings
191 ;;          emacs-rc-set.el      -- Emacs variable settings
192 ;;
193 ;;          emacs-rc-keys.el     -- Keyboard customizations
194 ;;          emacs-rc-font.el     -- Fonts and Font lock; face settings
195 ;;          emacs-rc-hooks.el    -- All add-hook commands and mode settings.
196 ;;          emacs-rc-ding.el     -- Gnus customizations (symlink to ~/.gnus)
197 ;;          emacs-rc-pkg-std.el  -- Loading std Emacs packages and their setup
198 ;;          emacs-rc-pkg-misc.el -- Non-std distrib, additional packages
199 ;;          emacs-rc-tips.el     -- Tips (code samples) from the Usenet
200 ;;          emacs-rc-mail.el     -- mail agent, Rmail, VM, message.el etc. setup
201 ;;
202 ;;      Now suppose your .emacs loads all these files like this
203 ;;
204 ;;          ;; $HOME/.emacs -- Emacs startup controller
205 ;;
206 ;;          (require 'cl)   ;; Tell location of startup files
207 ;;          (pushnew "~/elisp/rc" load-path :test 'string=)
208 ;;
209 ;;          (require 'emacs-rc-path)
210 ;;          (require 'emacs-rc-bup)
211 ;;          (require 'emacs-rc-set)
212 ;;          (load "emacs-rc-keys.el")
213 ;;          (require 'emacs-rc-font)
214 ;;          (load "emacs-rc-hooks")
215 ;;          (load "emacs-rc-ding")
216 ;;          (load "emacs-rc-pkg-std")
217 ;;          (load "emacs-rc-pkg-misc")
218 ;;          (load "emacs-rc-tips")
219 ;;          (add-hook 'mail-mode-hook '(lambda () (require 'emacs-rc-mail)))
220 ;;
221 ;;          ;; End of file $HOME/.emacs
222 ;;
223 ;;      The reason why there may be both `load' and `require' commands
224 ;;      may be that you frequently make updates and changes to some of your
225 ;;      start-up files. Like if you frequently update Setting for Gnus,
226 ;;      and you want to reload your settings, the (load "emacs-rc-ding")
227 ;;      is executed again. If you used `require' the new settings would not
228 ;;      have been loaded. (See explanation of `load' and `require' from the
229 ;;      Emacs info manual). So, to re-cap, if you would call:
230 ;;
231 ;;          M-x load-file ~/.emacs
232 ;;
233 ;;      Only the `load' commands' files would be loaded again. All the
234 ;;      `require' files would have been skipped, because the `rc' resource
235 ;;      features had already been defined.
236 ;;
237 ;;      Now, loading all these files, either with `require' or `load',
238 ;;      takes too much time when you start Emacs. After some rearrangements
239 ;;      you can put the delayed loading into use:
240 ;;
241 ;;          ;; $HOME/.emacs -- Emacs startup controller
242 ;;
243 ;;          (require 'cl)   ;; Tell location of startup files
244 ;;          (pushnew "~/elisp/rc" load-path :test 'string=)
245 ;;
246 ;;          ;; Have these minimum features immediately available
247 ;;
248 ;;          (require 'emacs-rc-path)
249 ;;          (require 'emacs-rc-bup)
250 ;;          (require 'emacs-rc-set)
251 ;;          (load "emacs-rc-keys.el")
252 ;;
253 ;;          ;;  Load this setup only when mail composing is started
254 ;;
255 ;;          (add-hook 'mail-mode-hook '(lambda () (require 'emacs-rc-mail)))
256 ;;
257 ;;          ;;  ........................................... lazy loading ....
258 ;;          ;;  We can afford to load these later
259 ;;
260 ;;          (setq tinyload-:load-list
261 ;;            '(("emacs-rc-font")
262 ;;              ("emacs-rc-hooks")
263 ;;              ("emacs-rc-ding")
264 ;;              ("emacs-rc-pkg-std")
265 ;;              ("emacs-rc-pkg-misc")
266 ;;              ("emacs-rc-tips")
267 ;;              ("emacs-rc-mail")))
268 ;;
269 ;;          (require 'tinyload)
270 ;;          ;; End of file $HOME/.emacs
271 ;;
272 ;;      When Emacs load this startup, only the most important files are
273 ;;      loaded saving the start time considerably. After `tinyload' finds
274 ;;      that your Emacs is idle it starts loading all the rest of the
275 ;;      packages you defined in the `tinyload-:load-list'. For more complex
276 ;;      setup, refer to end of tinyload.el source file, where you can
277 ;;      find a complete example setup.
278 ;;
279 ;;        NOTE: Please pay attention to one detail above. The `emacs-rc-mail'
280 ;;        will be loaded from load list _and_ it will be loaded when
281 ;;        you call M-x `mail'. Do you believe there is redundancy? The
282 ;;        idea is that you may call M-x `mail' way before the TinyLoad
283 ;;        reaches that file in its load list and the hook guarantees that
284 ;;        you get the setup at mail invoke time.
285 ;;
286 ;;        But it may be the other way round: TinyLoad has already loaded
287 ;;        the mail setup for you and thus invoking M-x `mail' is fast,
288 ;;        because there is nothing to load any more.
289 ;;
290 ;;        Similar things you should do to GNUS, VM, RMAIL and others that
291 ;;        you call and whose setup you want to have immediately available
292 ;;
293 ;;  Delayed loading, require and autoload
294 ;;
295 ;;      Above you saw how to load your Emacs `rc' files. But the delayed
296 ;;      loading is not only suitable for those. It also helps you to load
297 ;;      files, that can't be autoloaded.
298 ;;
299 ;;        If you can arrange loading a packages with `autoload' command,
300 ;;        do that. Never put `require' or direct `load' command into your
301 ;;        Emacs `rc' file, because load commands eat start time.
302 ;;
303 ;;      Packages usually explain in the *installation* section two ways
304 ;;      how to load them: here is an example from tinytab.el
305 ;;
306 ;;          (require 'tinytab)
307 ;;
308 ;;          or use this; your .emacs is read quicker
309 ;;
310 ;;          (autoload 'tinytab-mode              "tinytab" "" t)
311 ;;          (autoload 'tinytab-return-key-toggle "tinytab" "" t)
312 ;;
313 ;;      The first way forces loading the whole file (takes time); and
314 ;;      the latter only tells that the package's functions
315 ;;      `tinytab-return-key-toggle' and `tinytab-mode' exists. If you
316 ;;      happen to call those functions, _only_ then the package gets
317 ;;      loaded. The big difference here is that when you put the
318 ;;      latter in your Emacs rc file, Emacs reads `autoload' statements much
319 ;;      faster than the `require' command.
320 ;;
321 ;;      It is not always possible arrange to load package with autoloads,
322 ;;      because the package may behave so that in order to get the features
323 ;;      installed, package must do the setup by itself: you can't do it
324 ;;      yourself. Here are few packages that can't be autoloaded:
325 ;;
326 ;;          crypt++     -- crypt(1) support.
327 ;;          tinymy      -- collection of utilities
328 ;;          fa-extras   -- Filling extras
329 ;;
330 ;;      When you would normally include a `require' command for these
331 ;;      into your Emacs `rc' file, you can now move the packages to load
332 ;;      list and keep only autoloads in the `rc' files.
333 ;;
334 ;;          ;; Old rc file
335 ;;
336 ;;          (autoload ....
337 ;;          (autoload ....
338 ;;          (require 'fa-extras)
339 ;;          (autoload ....
340 ;;
341 ;;          ;; New rc file
342 ;;
343 ;;          (autoload ....
344 ;;          (autoload ....
345 ;;          (autoload ....
346 ;;
347 ;;      And the missing `require' entry has been moved to
348 ;;      `tinyload-:load-list'.
349 ;;
350 ;;  Use separate rc file for load definitions
351 ;;
352 ;;      It may be good idea to make a separate `rc' file that only has
353 ;;      the load list definition and a call to tinyload.el, like this:
354 ;;
355 ;;          ;; emacs-rc-tinyload.el -- load definitions for tinyload.el
356 ;;          ;;
357 ;;          ;; If you compile this file, `defconst' shuts up Byte Compiler
358 ;;
359 ;;          (defconst tinyload-:load-list
360 ;;            '(...
361 ;;              ...))
362 ;;          (require 'tinyload)
363 ;;          (provide 'emacs-rc-tinyload)
364 ;;          ;; End of file
365 ;;
366 ;;      And then you add following call to your *$HOME/.emacs*, to the end
367 ;;      of the file, although the place really doesn't matter.
368 ;;
369 ;;          (require 'emacs-rc-tinyload)
370 ;;
371 ;;  Used timer process
372 ;;
373 ;;      A normal timer process is used to load the packages from the load
374 ;;      list. The timer awakens at regular intervals and loads one package at
375 ;;      a time: more packages are not loaded if there was input pending at
376 ;;      the time of previous load. The load messages are recorded to
377 ;;      *Messages* buffer. In old Emacs releases this buffer does not
378 ;;      exist; but it will be created for you.
379 ;;
380 ;;  About implementation
381 ;;
382 ;;      When `tinyload-:load-list' is set, the value of the variable is
383 ;;      saved under property `original'. When the idle timer runs, the list
384 ;;      is read from the beginning and each package at a time is loaded.
385 ;;      The last unloaded package position is saved under property 'pos.
386 ;;
387 ;;      The situation looks like this:
388 ;;
389 ;;          tinyload-:load-list 'original   --> (list) original contents
390 ;;          tinyload-:load-list 'pos        --> (nth nbr) next package to load.
391 ;;
392 ;;      If your do something in your emacs while the list is being looped,
393 ;;      or when the loader function is about to be called, that interrupts
394 ;;      the work. Next time the timer functions run runs, happens:
395 ;;
396 ;;      o   It checks if the current list matches `original'. Yes, means that
397 ;;          the list hasn't been modified. No, means that it should examine
398 ;;          the list all aver again, starting from the beginning.
399 ;;      o   If the list was original, it picks the `pos' point and
400 ;;          loads all the remaining packages, one at a time until it
401 ;;          sees activity.
402 ;;      o   If there is nothing to load, the `pos' points to the end
403 ;;          of list. Function returns immediately and does nothing.
404 ;;          At this point the loader process terminates itself by
405 ;;          clearing the idle timer list.
406 ;;
407 ;;  Force loading
408 ;;
409 ;;      There is also property `fatal-list' which contains entries that
410 ;;      couldn't be loaded. The list is updated while the loading takes
411 ;;      place. If you examine the failed files and make corrections;
412 ;;      you can try to reload the whole load list again if you call
413 ;;
414 ;;          C-u M-x tinyload-loader-process
415 ;;
416 ;;  Special features
417 ;;
418 ;;      In case you want to load all packages and leave nothing in
419 ;;      autoload state, add this code to your Emacs startup file. When the
420 ;;      loader process exits, it will check all Emacs functions for autoload
421 ;;      definitions and load those packages as well.
422 ;;
423 ;;          (add-hook 'tinyload-:process-uninstall-hook
424 ;;                    'tinyload-autoload-function-load)
425 ;;
426 ;;  Restart and cancel
427 ;;
428 ;;      If you want to restart the evaluation of load list, call `M-x'
429 ;;      `tinyload-install', which will install the package again by removing
430 ;;      old processes and resetting counters. To stop the loader process
431 ;;      permanently, call `tinyload-cancel'.
432 ;;
433 ;;  Bugs
434 ;;
435 ;;      Every effort has been made to check that Emacs has no activity
436 ;;      before the package is loaded at the background. A series of
437 ;;      `sit-for' `input-pending-p' and more obscure mini-buffer
438 ;;      checks have been run before the load kicks in. If a package
439 ;;      still gets loaded while you are doing something, please send
440 ;;      a suggestion how that event could be detected so that the load
441 ;;      wouldn't interrupt you again. Unfortunately, there is no single
442 ;;      solution to notice all user activity in a reliable way.
443 ;;
444 ;;      Despite of the efforts, an unlucky moment may cause loading the
445 ;;      package, when it would not have been appropriate. Please hang on
446 ;;      and wait for the load to finish, you're will regain control soon.
447
448 ;;}}}
449
450 ;;; Change Log:
451
452 ;;; Code:
453
454 ;;{{{ require
455
456 ;;; ......................................................... &require ...
457
458 (require 'tinylibm)
459
460 ;; #todo: Does Xemacs reportmail.el define this function too?
461 ;; #todo: 2000-11 Emacs 2?.7 seems to include reportmail.el
462
463 (eval-and-compile
464   (autoload 'display-time "time"))
465
466 (eval-when-compile (ti::package-use-dynamic-compilation))
467
468 (ti::package-defgroup-tiny TinyLoad tinyload-: extensions
469   "Overview of features
470         o  Delayed loading of packages (in some later time)
471         o  You no longer have to use `require' in your .emacs, instead,
472            you can put the package to `tinyload-:load-list' and have it loaded
473            when Emacs is idle.
474         o  Your .emacs starts faster when the `require' commands are out.")
475
476 ;;}}}
477
478 ;;{{{ Hooks
479
480 ;;; ......................................................... &v-hooks ...
481
482 (defcustom tinyload-:load-hook nil
483   "*Hook that is run when package is loaded."
484   :type  'hook
485   :group 'TinyLoad)
486
487 (defcustom tinyload-:process-install-hook nil
488   "*Hook run when `tinyload-install' is called."
489   :type  'hook
490   :group 'TinyLoad)
491
492 (defcustom tinyload-:process-uninstall-hook nil
493   "*Hook run when `tinyload-cancel' is called."
494   :type  'hook
495   :group 'TinyLoad)
496
497 ;;}}}
498 ;;{{{ variables: public
499
500 ;;; ........................................................ &v-public ...
501 ;;; User configurable
502
503 (defcustom tinyload-:idle-time 20
504   "*When Emacs is this many seconds idle, start load process.
505 Warning: Do not set this value below 4 seconds, because the previous
506 call must complete before the timer process is called again. Some
507 big packages may take a while to load."
508   :type  'integer
509   :group 'TinyLoad)
510
511 (defcustom tinyload-:init-time 2
512   "*Time in seconds to wait before activating loader for the first time.
513 This is the initial time it takes before the loader process starts for the
514 first time. The default is 2 seconds."
515   :type  'integer
516   :group 'TinyLoad)
517
518 (defcustom tinyload-:wait-next-load 0.5
519   "*Time in seconds in load process to see if there is user activity.
520 This is the time loader process waits before it tries to load next package;
521 a time gap where any activity cancels the process from continuing
522 if user types something in Emacs.
523 Suggested value range: 0.2 - 1.5 seconds."
524   :type   'integer
525   :group  'TinyLoad)
526
527 (defcustom tinyload-:load-file nil
528   "*File to liast packages to load.
529 If you set this variable, you can't use `tinyload-:load-list', because
530 `tinyload-:load-list' is initalized from this file's content.
531
532 This variable is menat for simpler load control than what
533 could be done in lisp level with `tinyload-:load-list'.
534
535 The format of the FILE is simple:
536
537 - Comments in file start with semicolon (;)
538 - Added file to load in one line, next to next line and so on
539 - Add check configuration-word right after the filename.
540   This must be a SINGLE word.
541
542 An example:
543
544     ;; tinyload configuration file start
545
546     paren
547     autorevert win32
548     gnus.el
549     reportmail  win32-xemacs
550
551     ;; tinyload configuration file end
552
553 The above file's configuration words above are \"win32\" and
554 \"win32-xemacs\", where e.g. package autorevert will only be loaded under
555 win32. Similarly reportmail package is only loaded if current OS is win32
556 and Emacs flavor is XEmacs.
557
558 The recognized configuration tokens, that must form a single word, are:
559
560     win32 emacs xemacs"
561   :type   'file
562   :group  'TinyLoad)
563
564 (defcustom tinyload-:load-list nil
565   "*List of packages to load when emacs has been idle.
566 The idle time in seconds to load packages is defined in `tinyload-:idle-time'.
567
568 References:
569
570     You can also manipulate this list with following functions:
571     `tinyload-load-list-add-function'
572     `tinyload-load-list-add-package'
573     `tinyload-load-list-delete-function'
574     `tinyload-load-list-delete-package'
575
576 Format:
577
578   '((PACKAGE-OF-FILE [FEATURE-SYM] [NOERR] [NOMSG] [FORM-BEFORE] [FORM-AFTER])
579      ...)
580
581   PACKAGE-OR-FILE can be any valid `load' command filename parameter:
582
583         \"package\"
584         \"package.el\"
585         \"package.elc\"
586         \"~/elisp/package.el\"
587
588   You must provide FEATURE-SYM if the package provides different feature than
589   the package name; e.g. entry (\"~/rc/emacs-rc-my\" 'rc-my) says; that you
590   want to do (load \"~/rc/emacs-rc-my\") only if (featurep 'rc-my) returns false.
591
592   [NOERR] is optional and parameter for `load' command
593   [NOMSG] is optional and parameter for `load' command
594
595   [FORM-BEFORE] is evaluated before load command.
596   [FORM-AFTER]  is evaluated after load command.
597
598 Note:
599
600   Nil entries in this table are skipped. This allows you to construct
601   dynamic load list entry like this:
602
603       (setq tinyload-:load-list
604         (list
605          (if (and (ti::emacs-p)
606                   (= 28 emacs-minor-version))
607              (list \"~/rc/emacs-rc-19.28\" 'rc-28))))
608
609   The `tinyload-:load-list' would be '(nil) in non-19.28 Emacs
610
611 Example:
612
613   (setq tinyload-:load-list
614     '(\"ffap.el\"
615       \"tinylibmail.el\"))"
616   :type  '(repeat sexp)
617   :group 'TinyLoad)
618
619 ;;}}}
620 ;;{{{ variables: private
621
622 ;;; ....................................................... &v-private ...
623 ;;; Private variables
624
625 (defvar tinyload-:timer-elt nil
626   "The timer process if used in current Emacs.")
627
628 (defvar tinyload-:process-busy-p nil
629   "When load process is loading something this flag is non-nil.
630 This prevents invoking multiple load processes.")
631
632 ;;}}}
633 ;;{{{ version
634
635 ;;; ....................................................... &v-version ...
636
637 (eval-and-compile
638   (ti::macrof-version-bug-report
639    "tinyload.el"
640    "tinyload"
641    tinyload-:version-id
642    "$Id: tinyload.el,v 2.66 2007/05/06 23:06:11 jaalto Exp $"
643    '(tinyload-:version-id
644      tinyload-:debug
645      tinyload-:load-hook
646      tinyload-:load-list
647      tinyload-:load-file
648      tinyload-:process-install-hook
649      tinyload-:process-uninstall-hook
650      tinyload-:timer-elt
651      tinyload-:process-busy-p
652      tinyload-:idle-time
653      tinyload-:init-time
654      tinyload-:wait-next-load)
655    '(tinyload-:debug-buffer)))
656
657 ;;;### (autoload 'tinyload-debug-toggle "tinyload" t t)
658
659 (eval-and-compile (ti::macrof-debug-standard "tinyload" "-:"))
660
661 ;;}}}
662 ;;{{{ installation
663
664 ;;; --------------------------------------------------------- &install ---
665 ;;;
666 ;;;###autoload
667 (defun tinyload-install (&optional remove)
668   "Install package or REMOVE.
669 This function removes any previous TinyLoad timer process and resets
670 the list pointer to 0."
671   (interactive "P")
672   (tinyload-config-file-load-default)
673   ;;  Kill old process(es)
674   (ti::compat-timer-cancel-function 'tinyload-loader-process)
675   (setq tinyload-:timer-elt nil)
676   (cond
677    ((or remove
678         (null tinyload-:load-list))
679     (let* ((str (concat
680                  "TinyLoad: Loader process terminated."
681                  (if (null tinyload-:load-list)
682                      " `tinyload-:load-list' is empty."
683                    ""))))
684       (tinyload-message str))
685     (tinyload-debug "Tinyload: Install, stopped. HOOK"
686                     tinyload-:process-uninstall-hook)
687     (run-hooks 'tinyload-:process-uninstall-hook))
688    (t
689     (put 'tinyload-:load-list 'pos 0)
690     (put 'tinyload-:load-list 'fatal-list nil)
691     ;;  Put startup info into *Messages*" buffer
692     (tinyload-message
693      (format
694       (concat "TinyLoad: Started with %d items in load list."
695               " Init %d and interval %d seconds.")
696       (length tinyload-:load-list)
697       tinyload-:init-time
698       tinyload-:idle-time))
699     (tinyload-debug "Tinyload: Install, started. HOOK"
700                     tinyload-:process-install-hook)
701     (display-time)
702     (setq tinyload-:timer-elt
703           (run-at-time
704            (format "%d sec" tinyload-:init-time)
705            tinyload-:idle-time
706            'tinyload-loader-process))
707     (tinyload-debug "tinyload-install: `run-at-time' timer elt"
708                     tinyload-:timer-elt)
709     (run-hooks 'tinyload-:process-install-hook)))
710   (setq tinyload-:process-busy-p nil)
711   tinyload-:timer-elt)
712
713 ;;; ----------------------------------------------------------------------
714 ;;;
715 (defun tinyload-cancel ()
716   "Kill the loaded process and stop loading.
717 To start loader process, call \\[tinyload-install]."
718   (interactive)
719   (tinyload-install 'remove))
720
721 ;;; ----------------------------------------------------------------------
722 ;;;
723 (defun tinyload-start ()
724   "Start loader process. This function is synonym to ´tinyload-install'"
725   (interactive)
726   (tinyload-install))
727
728 ;;}}}
729 ;;{{{ support functions
730
731 ;;; ----------------------------------------------------------------------
732 ;;;
733 ;;;###autoload
734 (defun tinyload-autoload-function-load (&optional verb)
735   "Load all autoloaded functions. VERB."
736   (interactive)
737   (ti::verb)
738   (let* ((fid "tinyload-autoload-function-load:")
739          (funcs (ti::system-autoload-function-list))
740          (load (when funcs
741                  (ti::system-autoload-function-file-list funcs)))
742          (count 0)
743          str)
744     (unless fid ;; No-op. XEmacs byte compiler silencer
745       (setq fid nil))
746     (tinyload-debug
747      (format "Tinyload: [debug] %s FUNCTIONS %s FILES %s"
748              fid
749              (prin1-to-string funcs)
750              (prin1-to-string load)))
751     (dolist (file load)
752       (condition-case err
753           (load file)
754         (error
755          (setq str (format
756                     "Tinyload: autoload function load fail %s %s "
757                     file (prin1-to-string err)))
758          (message str)
759          (tinyload-debug str)))
760       (incf count)
761       (when verb
762         (message "Tinyload: autoloading clean %d/%d %s"
763                  count (length load) file)))
764     load))
765
766 ;;; ----------------------------------------------------------------------
767 ;;;
768 (defun tinyload-feature-p (pkg &optional feature)
769   "Check if feature has been loaded.
770 See PKG and FEATURE from `tinyload-:load-list'"
771   ;;  User didn't give us separate feature name, construct
772   ;;  one from package name ~/elisp/test.el --> "test"
773   (let* ((fid "tinyload-feature-p")
774          status)
775     (unless fid ;; No-op. XEmacs byte compiler silencer
776       (setq fid nil))
777     (tinyload-debug
778      (format "TinyLoad: [debug] %s (a) PACKAGE [%s] FEATURE [%s]"
779              fid (prin1-to-string pkg) (prin1-to-string feature)))
780     ;;  Make feature name out of the package name if
781     ;;  it was not given  gnus.el -> 'gnus
782     (when (and (null feature)
783                (stringp pkg))
784       (setq feature (file-name-nondirectory pkg))
785       (if (and (string-match "^\\(.+\\)\\.el" feature)
786                (match-end 1))
787           (setq feature (match-string 1 feature))))
788     (setq status
789           (cond
790            ((and (not (null feature))
791                  (symbolp feature)
792                  (featurep feature))
793             'symbol)
794            ((and (stringp feature)
795                  (intern-soft feature)
796                  (featurep (intern-soft feature)))
797             'intern)
798            (t nil)))
799     (tinyload-debug
800      (format "TinyLoad: [debug] %s (b) PACKAGE [%s] FEATURE [%s] stat %s"
801              fid
802              (prin1-to-string pkg)
803              (prin1-to-string feature)
804              (prin1-to-string status)))
805     status))
806
807 ;;; ----------------------------------------------------------------------
808 ;;;
809 (defun tinyload-message (msg)
810   "Display MSG and put it to *Messages* Buffer."
811   (if (string-match "%" msg)
812       (setq msg (subst-char-with-string msg ?% "%%")))
813   (tinyload-debug msg)
814   (message msg)
815   ;;  Old releases don't have this buffer; generate one.
816   (when (and (ti::emacs-p)
817              (string-match "19.2[0-9]" emacs-version))
818     (with-current-buffer (get-buffer-create "*Messages*")
819       (ti::pmax) (insert msg "\n"))))
820
821 ;;; ----------------------------------------------------------------------
822 ;;;
823 (defun tinyload-status ()
824   "Print status. How many packages are left in load list."
825   (interactive)
826   (if (null tinyload-:timer-elt)
827       (message "TinyLoad process is not alive any more.")
828     (message "Position %s/%s in load list."
829              (get 'tinyload-:load-list 'pos)
830              (length tinyload-:load-list))))
831
832 ;;}}}
833 ;;{{{ Load list manipulation support functions
834
835 ;;; ----------------------------------------------------------------------
836 ;;;
837 (defun tinyload-load-list-search-elt (search position)
838   "SEARCH item in `tinyload-:load-list' by checking POSITION.
839
840 package feature noerr nomsg before after
841 0       1       2     3     4      5
842
843 The SEARCH item is checked with `equal' function."
844   (let (picked)
845     (dolist (elt tinyload-:load-list)
846       ;;  package feature noerr nomsg before after
847       (setq picked (nth position elt))
848       (when (equal picked search)
849         (return elt)))))
850
851 ;;; ----------------------------------------------------------------------
852 ;;;
853 (defun tinyload-load-list-search-function (function)
854   "Search FUNCTION in `tinyload-:load-list'."
855   (tinyload-load-list-search-elt function 4))
856
857 ;;; ----------------------------------------------------------------------
858 ;;;
859 (defun tinyload-load-list-search-package (package)
860   "Search PACKAGE in `tinyload-:load-list'."
861   (tinyload-load-list-search-elt package 0))
862
863 ;;; ----------------------------------------------------------------------
864 ;;;
865 (defun tinyload-load-list-add-function (function)
866   "Add FUNCTION to `tinyload-:load-list'.
867 This function places a null entry to the laod list, so that only the
868 load-before form is exected: it runs the FUNCTION."
869   (let ((elt   (list "run-function-only" nil 'noerr 'nomsg function nil))
870         (entry (tinyload-load-list-search-function function)))
871     (unless entry
872       (push elt tinyload-:load-list))))
873
874 ;;; ----------------------------------------------------------------------
875 ;;;
876 (defun tinyload-load-list-add-package (package &optional feature)
877   "Add PACKAGE FEATURE with 'noerr 'nomsg attributes to `tinyload-:load-list'."
878   (let ((elt   (list package feature 'noerr 'nomsg))
879         (entry (tinyload-load-list-search-package package)))
880     (unless entry
881       (push elt tinyload-:load-list))))
882
883 ;;; ----------------------------------------------------------------------
884 ;;;
885 (defun tinyload-load-list-delete-elt (elt)
886   "Remove ELT from `tinyload-:load-list'."
887   (setq tinyload-:load-list (delete elt tinyload-:load-list)))
888
889 ;;; ----------------------------------------------------------------------
890 ;;;
891 (defun tinyload-load-list-delete-function (function)
892   "Remove FUNCTION from `tinyload-:load-list'."
893   (let ((entry (tinyload-load-list-search-function function)))
894     (when entry
895       (tinyload-load-list-delete-elt entry))))
896
897 ;;; ----------------------------------------------------------------------
898 ;;;
899 (defun tinyload-load-list-delete-package (package)
900   "Remove PACKAGE from `tinyload-:load-list'."
901   (let ((entry (tinyload-load-list-search-package package)))
902     (when entry
903       (tinyload-load-list-delete-elt entry))))
904
905 ;;}}}
906 ;;{{{ Config file interface
907
908 ;;; ----------------------------------------------------------------------
909 ;;;
910 (defun tinyload-config-file-emacs-type-ok-p (string)
911   "Test STRING for xemacs, emacs and win32."
912   (if (null string)
913       t
914     (let* ((emacs-ok 'not-tested)
915            (os-ok    'not-tested))
916       (when (string-match "win32" string)
917         (setq os-ok (ti::win32-p)))
918       (when (string-match "emacs" string)
919         (setq emacs-ok
920               (or (and (string-match "xemacs" string)
921                        (ti::xemacs-p))
922                   (and (not (string-match "xemacs" string))
923                        (string-match "emacs" string)
924                        (ti::emacs-p)))))
925       (and emacs-ok
926            os-ok))))
927
928 ;;; ----------------------------------------------------------------------
929 ;;;
930 (defun tinyload-config-file-parse ()
931   "Parse entries ein configuration file and ignore comments.
932 File format is:
933
934     ;; Comment
935     ;; Another comment
936     file win32-xemacs
937     file.el emacs
938     file.elc
939
940     ;; End of file
941
942 In the above example, FILE means command \(load \"file\" 'noerr). You can
943 add additional .el or .elc extension to force loading uncompiled or
944 compiled version of the file.
945
946 The additional PARAMETER-WORD follows directly after the filename. It must
947 be only one word and you can separate different tests with dash(-). Valid
948 test names recognized are
949
950     win32
951     emacs
952     xemacs
953
954 For example if line reads:
955
956     file win32-xemacs
957
958 This means that package \"file\" if loaded only if current Emacs
959 flavor is XEmacs and the operating system is win32
960
961 Any empty lines, spaces and comment started with semicolon (;)
962 are ignored.
963
964 Return:
965
966   Similar list than what is described for variable
967   `tinyload-:load-list'"
968   (let* ((fid "tinyload-config-file-parse")
969          list
970          test
971          file)
972     (unless fid ;; No-op. XEmacs byte compiler silencer
973       (setq fid nil))
974     (ti::pmin)
975     (while (re-search-forward
976             "^[ \t]*\\([^ ;\t\r\n]+\\)[ \t]*\\([^ ;\t\r\n]+\\)" nil t)
977       (when (setq file (match-string 1))
978         (setq test (match-string 2))
979         (when (tinyload-config-file-emacs-type-ok-p test)
980           (push (list file) list))))
981     ;; Preserve read order
982     (setq list (nreverse list))
983     (tinyload-debug fid "RET" list)))
984
985 ;;; ----------------------------------------------------------------------
986 ;;;
987 (defun tinyload-config-file-load-1 (file)
988   "Load configuration file and return list in format `tinyload-:load-list'."
989   (interactive "fTinyLoad configuration file: ")
990   (with-temp-buffer
991     (insert-file-contents file)
992     (tinyload-config-file-parse)))
993
994 ;;; ----------------------------------------------------------------------
995 ;;;
996 (defun tinyload-config-file-load-default ()
997   "Load `tinyload-:load-file' and return list in format `tinyload-:load-list'."
998   (let* ((file tinyload-:load-file))
999     (tinyload-debug "tinyload-config-file-load-default"
1000                     "tinyload-:load-file"
1001                     file)
1002     (cond
1003      ((not (stringp file))
1004       nil)
1005      ((not (file-exists-p tinyload-:load-file))
1006       (message "Tinyload: tinyload-:load-file does not exist %s"
1007                tinyload-:load-file))
1008      (t
1009       (setq tinyload-:load-list
1010             (tinyload-config-file-load-1 file))))))
1011
1012 ;;}}}
1013 ;;{{{ main
1014
1015 ;;; ----------------------------------------------------------------------
1016 ;;;
1017 (defun tinyload-minibuffer-active-p ()
1018   "check if minibuffer is active."
1019   (if (fboundp 'active-minibuffer-window)
1020       (ti::funcall 'active-minibuffer-window)
1021     (eq (selected-window) (minibuffer-window))))
1022
1023 ;;; ----------------------------------------------------------------------
1024 ;;;
1025 (defun tinyload-no-action ()
1026   "Check that Emacs is still."
1027   (and
1028    ;; (ti::no-action-in-progress-p 'timer) isn't working right
1029    (sit-for 0.2)
1030    (not cursor-in-echo-area)
1031    (not (tinyload-minibuffer-active-p))))
1032
1033 ;;; ----------------------------------------------------------------------
1034 ;;;
1035 (defun tinyload-process-continue (&optional force)
1036   "Check if process is clear to continue and Emacs is not busy.
1037 Return status '(continue no-action no-input)."
1038   (let* ((fid "tinyload-process-continue")
1039          no-action
1040          no-input
1041          continue)
1042     (unless fid ;; No-op. XEmacs byte compiler silencer
1043       (setq fid nil))
1044     (setq no-action (tinyload-no-action)
1045           no-input  (null (input-pending-p))
1046           continue  (or force
1047                         (and no-input
1048                              no-action)))
1049     (tinyload-debug
1050      (format
1051       "TinyLoad: [debug] %s no-action: %s no-input: %s continue: %s busy: %s"
1052       fid
1053       (prin1-to-string no-action)
1054       (prin1-to-string no-input)
1055       (prin1-to-string continue)
1056       (if tinyload-:process-busy-p
1057           "yes"
1058         "no")))
1059
1060     (list continue no-action no-input)))
1061
1062 ;;; ----------------------------------------------------------------------
1063 ;;;
1064 (defun tinyload-eval (form type)
1065   "Eval FORM. TYPE is string AFTER or BEFORE."
1066   (condition-case err
1067       (if form
1068           (eval form))
1069     (error
1070      (let* ((str
1071              (format "Tinyload: [ERROR] EVAL %s generated an error %s %s"
1072                      type
1073                      (prin1-to-string err)
1074                      (prin1-to-string form))))
1075        (message str)
1076        (tinyload-debug str)))))
1077
1078 ;;; ----------------------------------------------------------------------
1079 ;;;
1080 (defun tinyload-load (pkg noerr nomsg)
1081   "Load PKG with NOERR NOMSG. Return load status."
1082   (let* (stat)
1083     (cond
1084      (noerr
1085       (condition-case data
1086           (setq stat (load pkg noerr nomsg))
1087         (error
1088          (message "TinyLoad: [%s] %s"
1089                   pkg
1090                   (prin1-to-string data))))
1091       (tinyload-debug "TinyLoad: 'noerr load %s: %s" pkg stat))
1092      (t
1093       (setq stat (ignore-errors (load pkg noerr nomsg)))))
1094     stat))
1095
1096 ;;; ----------------------------------------------------------------------
1097 ;;;
1098 (defun tinyload-load-failure (pkg elt)
1099   "Record PKG ELT failure to `tinyload-:load-list'. Return failed-list."
1100   ;;  Record failed entries.
1101   (let* ((failed-list (get 'tinyload-:load-list 'failed-list)))
1102     (add-to-list 'failed-list elt)
1103     (put 'tinyload-:load-list 'failed-list failed-list)
1104     (let ((str
1105            (format "TinyLoad: [ERROR] while loading %s" pkg)))
1106       (ding)
1107       (tinyload-debug str)
1108       (tinyload-message str))
1109     ;;  This will tell the path and put the message
1110     ;;  in *Message* buffers. It will also tell if
1111     ;;  it was .elc or .el that had troubles.
1112     ;;  >> FOR DEBUG PURPOSES
1113     (ignore-errors (locate-library pkg))
1114     failed-list))
1115
1116 ;;; ----------------------------------------------------------------------
1117 ;;;
1118 (defun tinyload-initialize ()
1119   "Initialise `tinyload-:load-list'.
1120 Return:
1121
1122  '(load-list pointer)."
1123   (let ((orig (get 'tinyload-:load-list 'original)))
1124     ;; first invocation
1125     (put 'tinyload-:process-busy-p 'count 0)
1126     ;;  No original values available, so set defaults
1127     (unless orig
1128       (put 'tinyload-:load-list 'original tinyload-:load-list))
1129     (unless (integerp (get 'tinyload-:load-list 'pos))
1130       (put 'tinyload-:load-list 'pos 0))
1131     ;; user has recently changed "list", do update.
1132     (unless (equal orig tinyload-:load-list)
1133       (put 'tinyload-:load-list 'original tinyload-:load-list)
1134       (put 'tinyload-:load-list 'pos 0))))
1135
1136 ;;; ----------------------------------------------------------------------
1137 ;;;
1138 (defun tinyload-terminate-process ()
1139   "Remove process."
1140   ;;  No more loading; do self kill so that this process is
1141   ;;  not unnecessarily held in timer list.
1142   ;;
1143   ;;  19.34 bug: Process can't remove itself. Ack. Fixed in
1144   ;;  new Emacs releases.
1145   (tinyload-message "TinyLoad: Bye, No more packages to load.")
1146   (setq tinyload-:process-busy-p nil)
1147   (tinyload-install 'remove))
1148
1149 ;;; ----------------------------------------------------------------------
1150 ;;;
1151 (defun tinyload-busy-count ()
1152   "Return `tinyload-:process-busy-p' busy count."
1153   (get 'tinyload-:process-busy-p 'count))
1154
1155 ;;; ----------------------------------------------------------------------
1156 ;;;
1157 (defun tinyload-busy-count-incf ()
1158   "Increase `tinyload-:process-busy-p' busy count."
1159   ;;  - If counter keeps incrementing all the time,
1160   ;;    then the main loop never cleared the flag
1161   ;;  - Keep on eye on the counter and prevent deadlock by resetting
1162   ;;    the busy signal.
1163   (let  ((busy-count (get 'tinyload-:process-busy-p 'count)))
1164     (cond
1165      ;; Not yet defined, set initial value
1166      ((not (integerp busy-count))
1167       (setq busy-count 0))
1168      (t
1169       (incf busy-count)))
1170     (put 'tinyload-:process-busy-p 'count  busy-count)
1171     (put 'tinyload-:process-busy-p 'count2 busy-count)))
1172
1173 ;;; ----------------------------------------------------------------------
1174 ;;;
1175 (defun tinyload-continue-check (&optional force)
1176   "Check if process can continue with FORCE.
1177 Return CONTINUE if there is no activity."
1178   (multiple-value-bind (continue no-act no-input)
1179       (tinyload-process-continue force)
1180     (tinyload-message
1181      (format
1182       "TinyLoad: timer triggered; invoking load process... [%s;%s;%s;%d]"
1183       (if no-act          "not-busy"   "busy")
1184       (if (null continue) "stop"       "cont")
1185       (if no-input        ""           "input")
1186       (or (tinyload-busy-count) 0)))
1187     (tinyload-debug
1188      (format "tinyload-continue-check: %s" (prin1-to-string continue)))
1189     continue))
1190
1191 ;;; ----------------------------------------------------------------------
1192 ;;;
1193 (defun tinyload-failed-list-update (elt)
1194   "Update `tinyload-:load-list' property 'failed-list with ELT."
1195   (let* ((fid         "tinyload-failed-list-update")
1196          (failed-list (get 'tinyload-:load-list 'failed-list)))
1197     (unless fid ;; No-op. XEmacs byte compiler silencer
1198       (setq fid nil))
1199     ;;  Remove entry from failed list
1200     (setq failed-list (delete elt failed-list))
1201     (put 'tinyload-:load-list 'failed-list failed-list)
1202     (tinyload-debug
1203      (format "TinyLoad: [Debug] %s failed-list: "  fid) failed-list)))
1204
1205 ;;; ----------------------------------------------------------------------
1206 ;;;
1207 (defun tinyload-library-info (pkg noerr)
1208   "Record PKG NOERR library info under debug."
1209   (when tinyload-:debug
1210     (message "TinyLoad: [debug] locating library %s %s"
1211              pkg (prin1-to-string noerr))
1212     (let ((tmp (locate-library pkg)))
1213       (tinyload-debug (format "TinyLoad: [debug] locate %s %s"
1214                               pkg (or tmp ""))))))
1215
1216 ;;; ----------------------------------------------------------------------
1217 ;;;
1218 (defun tinyload-load-ignore-message (pkg pos len)
1219   "Print PKG POS LEN status. Already in Emacs."
1220   (let* ((str (format "\
1221 TinyLoad: %-15s %s (%2d%% %2d/%2d) <ignored, feature already in emacs>"
1222                       pkg
1223                       "ok"
1224                       (/ (* 100 pos) len)
1225                       (1+ pos) (1+ len))))
1226     (tinyload-message str)))
1227
1228 ;;; ----------------------------------------------------------------------
1229 ;;;
1230 (defun tinyload-load-ok-message (pkg pos len stat)
1231   "Print PKG POS LEN status. Loaded."
1232   (let* ((str (format "TinyLoad: %-15s %s (%2d%% %2d/%2d)"
1233                       pkg
1234                       (if stat
1235                           "ok"
1236                         "'noerr!")
1237                       (/ (* 100 (1+ pos)) len)
1238                       (1+ pos) len)))
1239     (tinyload-message str)))
1240
1241 ;;; ----------------------------------------------------------------------
1242 ;;;
1243 (defun tinyload-busy-count-controller ()
1244   "Handle busy checking and deadlocks.
1245 Return:
1246   deadlock     if non-nil, deadlock was detected."
1247   (let* ((busy-count (tinyload-busy-count-incf))
1248          deadlock)
1249     (incf  busy-count)
1250     (when (> busy-count 5)
1251       (tinyload-debug "Tinyload: busy count too high, clearing DEADLOCK")
1252       (tinyload-message "TinyLoad: Deadlock detected, clearing...")
1253       (setq tinyload-:process-busy-p nil
1254             busy-count               0
1255             ;;  If there is infnite prompt open, we never would get
1256             ;;  past it, because the input-pending-p tests later would
1257             ;;  stop preceeding to load commands. FORCE going one load
1258             ;;  this time. The next busy, will again wait for deadlock,
1259             ;;  (if prompt is still open), but eventually the packages
1260             ;;  will get loaded.
1261             ;;
1262             ;;  Extended period of prompt open is an indication that
1263             ;;  use is not present.
1264             ;;
1265             ;;  #todo: to be asolutely sure, utilize top level `count2'
1266             ;;  which would keep track of deadlocks and never-loads.
1267             ;;  ==> if too hight, only then FORCE load.
1268             ;;
1269             deadlock t))
1270     (put  'tinyload-:process-busy-p 'count busy-count)
1271     deadlock))
1272
1273 ;;; ----------------------------------------------------------------------
1274 ;;; (tinyload-loader-process 'force)
1275 ;;;
1276 ;;;###autoload
1277 (defun tinyload-loader-process (&optional force)
1278   "Load packages defined in `tinyload-:load-list'.
1279 If called interactively, FORCE loading all packages in the list."
1280   (interactive (list 'force))
1281   (let* (continue
1282          list
1283          pos
1284          len
1285          stat)
1286     (tinyload-debug "TinyLoad: [debug] main()"
1287                     "INPUT PENDING STATUS"
1288                     (input-pending-p)
1289                     "TIMER ELT"
1290                     tinyload-:timer-elt)
1291     ;; ................................................... zombie test ...
1292     ;;  tinyload-:timer-elt
1293     ;;
1294     ;;  - Emacs 19.34 has a bug. If the load list has been finished and _this_
1295     ;;    function tries to remove itseld with (tinyload-install 'remove);
1296     ;;    the timer element is not removed. Suprise.
1297     ;;  - However If I manually execute C-u M-x tinyload-install; then
1298     ;;    the process is killed all right.
1299     ;;  - So when the (tinyload-install 'remove) is called below; it sets
1300     ;;    the timer elt to nil; _but_ emacs still keeps calling this
1301     ;;    function. We're are now a zombie; we did try to kill
1302     ;;    ourself; but Emacs didn't let that to happen.
1303     ;;  - While we're a zombie, we don't display any messages or
1304     ;;    do anything. Calling this zombie function  is no-op and won't
1305     ;;    take process time much.
1306     ;;
1307     ;;  There may be previous function still loading; don't
1308     ;;  interrupt it; but terminate this invocation.
1309     (when tinyload-:timer-elt
1310       (setq continue (tinyload-continue-check force))
1311       (if (tinyload-busy-count-controller)
1312           (setq force     t
1313                 continue  t)))
1314     (tinyload-debug "TinyLoad: [debug] main() continue status: "
1315                     continue
1316                     (if tinyload-:process-busy-p
1317                         "process busy" "process not busy"))
1318     (when (or force
1319               (and continue
1320                    (null tinyload-:process-busy-p)))
1321       (unwind-protect
1322           (catch 'exit
1323             (tinyload-initialize)
1324             ;; ........................................... load list ...
1325             (setq pos  (get 'tinyload-:load-list 'pos)
1326                   len  (length tinyload-:load-list)
1327                   list (nthcdr pos tinyload-:load-list))
1328             (tinyload-debug
1329              (format "TinyLoad: [Debug] list pointer: pos %d len %d" pos len))
1330             (unless list
1331               (tinyload-terminate-process)
1332               (throw 'exit t))
1333             (tinyload-debug "TinyLoad: [Debug] list" list)
1334             (dolist (elt list)
1335               (setq tinyload-:process-busy-p 'busy)
1336               ;;  simple STRING is package name only
1337               (when elt
1338                 (setq elt (ti::list-make elt)))
1339               (multiple-value-bind (pkg feature noerr nomsg
1340                                         form-before form-after)
1341                   elt
1342                 ;;  Remove entry from failed list
1343                 (tinyload-failed-list-update elt)
1344                 (tinyload-debug
1345                  (format (concat "TinyLoad: [Debug] LIST ELT "
1346                                  "pkg: %s feature: %s elt: %s ")
1347                          pkg
1348                          feature
1349                          (prin1-to-string elt)))
1350                 ;; ........................................... load it ...
1351                 (when (and elt pkg)
1352                   ;;  Try to sit for some time before preceeding, otherwise
1353                   ;;  if we can't sit still that long, user is
1354                   ;;  doing something..
1355                   (tinyload-debug "TinyLoad: >>> 1 -- input pending?")
1356                   (let* ((wait (or tinyload-:wait-next-load 0.3)))
1357                     (unless (and (sit-for wait)
1358                                  (not (input-pending-p)))
1359                       (tinyload-debug
1360                        (format "´THROW ´sit-for' didn't return t (activity) %d"
1361                                wait))
1362                       (throw 'exit t)))
1363                   (tinyload-debug "TinyLoad: >>> 2 -- feature present?")
1364                   (setq stat (tinyload-feature-p pkg feature))
1365                   (incf  pos)
1366                   (put 'tinyload-:load-list 'pos pos)
1367                   (tinyload-debug
1368                    (format "TinyLoad: >>> 3, pkg %s feature `%s' status: %s"
1369                            (prin1-to-string pkg)
1370                            (prin1-to-string feature)
1371                            (prin1-to-string stat)))
1372                   (tinyload-debug
1373                    (format "TinyLoad: [Debug] pkg forms before:%s after:%s"
1374                            (prin1-to-string form-before)
1375                            (prin1-to-string form-after)))
1376                   (cond
1377                    ;; ................................. feature in Emacs ...
1378                    (stat
1379                     (tinyload-load-ignore-message pkg pos len))
1380                    ;; ..................................... not in emacs ...
1381                    (t
1382                     (tinyload-eval form-before "BEFORE")
1383                     (tinyload-library-info pkg noerr)
1384                     (unless (tinyload-process-continue)
1385                       (tinyload-debug
1386                        (format "THROW 2 input-p didn't return t (activity)"))
1387                       (throw 'exit t))
1388                     (setq stat (tinyload-load pkg noerr nomsg))
1389                     (cond
1390                      (stat
1391                       (tinyload-eval form-after "AFTER")
1392                       (tinyload-load-ok-message pkg pos len stat))
1393                      (t
1394                       (tinyload-load-failure pkg elt)
1395                       (setq stat 'fatal))))))
1396                 (when (or (input-pending-p)
1397                           (eq stat 'fatal))
1398                   (throw 'exit t)))))
1399         ;; .................................................... unwind ...
1400         (setq tinyload-:process-busy-p nil)))))
1401
1402 ;;}}}
1403 ;;{{{ example
1404
1405 ;;; ......................................................... &example ...
1406 ;;; - Here is example at the time of writing tinyload v1.14
1407 ;;; - Hope you get some ideas from this.
1408
1409 ;;; --++-- --++-- --++-- --++-- --++-- --++-- --++-- --++-- -- example --
1410 (when nil ;;  Start of example - Emacs does not read code below
1411
1412   ;; ~/elisp/rc/emacs-rc-tinyload.el -- Delayed loading of files
1413   ;;
1414   ;;  Docid
1415   ;;
1416   ;;      This is a personal Emacs (rc) resource files and it
1417   ;;      is loaded from .emacs in the following manner
1418   ;;
1419   ;;          (require 'emacs-rc-tinyload)
1420   ;;
1421   ;;      This file may be under some other name in the current directory
1422   ;;      where you found it, but do not mind that.. Just rename this file to
1423   ;;      whatever is shown in the first line.
1424   ;;
1425   ;;  Description
1426   ;;
1427   ;;      This file configures all packages and files that can be loaded
1428   ;;      later when Emacs sits idle for tinyload.el. See for full description
1429   ;;      of the usage from there.
1430   ;;
1431   ;;      `emacs-rc-xxx'  are all Emacs resource files of various kind.
1432   ;;
1433   ;;      `ti::compat-window-system' is Emacs independent window system check
1434   ;;      function found from tinylib.el
1435
1436 ;;; ............................................................ &load ...
1437
1438   (let* ((w  (ti::compat-window-system)) ;XEmacs and Emacs detection
1439          (x  (eq w 'x))                  ;x windowed
1440          (win32 (eq w 'win32)))          ;Windows
1441     (setq tinyload-:load-list
1442           (list
1443            ;;  Those with 'noerr flag are not essential packages
1444            ;;
1445            ;;  In X Windowed emacs, Load non-compiled rc file in XEmacs, because
1446            ;;  the compiled faces are not compatible with XEmacs.
1447            (when w
1448              (list (if (ti::emacs-p) "emacs-rc-font" "emacs-rc-font.el")
1449                    'rc-font nil nil nil
1450                    ;;  The file defines function `my-face-change'
1451                    ;;  that is called after load.
1452                    ;;  It configures faces for this emacs.
1453                    '(progn
1454                       (cond
1455                        (nt (my-face-change 'pc))
1456                        (t  (my-face-change 'def))))))
1457            (list "emacs-rc-macros")
1458            (list "emacs-rc-setting")
1459            (list "emacs-rc-tiny")
1460            (list "emacs-rc-standard-packages")
1461            (list "emacs-rc-hooks")
1462            (unless win32 ;; I don't use mail here
1463              ;;  Package contain faces: load non-compiled version for XEmacs
1464              (list (if (ti::emacs-p)
1465                        "emacs-rc-mail"
1466                      "emacs-rc-mail.el")))
1467            ;;  Tiny Tools distribution
1468            (list "tinyef"       nil 'noerr)
1469            (list "tinytab"      nil 'noerr)
1470            (list "tinylisp"     nil)
1471            (list "tinymy"       nil)
1472            (list "tinysword"    nil 'noerr)
1473            (list "tinydiff"     nil 'noerr)
1474            (list "tinyreplace"  nil 'noerr)
1475            (list "tinytfo"      nil 'noerr)
1476            (list "tinydired"    nil 'noerr)
1477            (list "tinycache"    nil 'noerr)
1478            (list "tinyigrep"    nil 'noerr)
1479            (list "tinylibmenu"  nil)
1480            (list "tinymatch"    nil)
1481            (list "tinylibid"    nil 'noerr)
1482            (list "tinydesk"     nil 'noerr)
1483            (if (ti::emacs-p)
1484                (list "mldrag" nil 'noerr nil
1485                      '(progn (setq mldrag-load-hook 'mldrag-default-keys))))
1486            ;; Run extra fa-setup aftert package.
1487            (list "fa-extras" nil 'noerr nil nil '(progn (my-fa-setup)))
1488            ;;  Personal lisp function library. Run compression
1489            ;;  After loading this package.
1490            (list "mylib.el"
1491                  'mylib
1492                  nil
1493                  nil
1494                  nil
1495                  '(progn
1496                     (when (fboundp 'my-compress-household)
1497                       (my-compress-household)))))))
1498
1499   (defun my-fa-setup ()
1500     "Filladapt setup."
1501     (when (boundp 'filladapt-token-table)
1502       (defvar filladapt-token-table nil)
1503       (defconst filladapt-mode-line-string " Fa")
1504       (let* ((tok  "[*]+")
1505              (elt (assoc tok filladapt-token-table)))
1506         ;;  Clear the old definition
1507         (cond
1508          ((setq  filladapt-token-table (delq elt filladapt-token-table))
1509           (setq  filladapt-token-table
1510                  (cons (cons tok 'citation->)
1511                        filladapt-token-table))))
1512         (setq tok ">+")
1513         ;; (setq tok adaptive-fill-regexp)
1514         (cond
1515          ((setq elt (assoc tok filladapt-token-table))
1516           (setq filladapt-token-table (delq elt filladapt-token-table))
1517           (setq  filladapt-token-table
1518                  (list (cons adaptive-fill-regexp 'citation->))))))))
1519
1520   ;; (provide 'emacs-rc-tinyload)
1521
1522   ;; ;; End of file emacs-rc-tinyload.el
1523
1524   ) ;; ++Example-End++
1525 ;;; --++-- --++-- --++-- --++-- --++-- --++-- --++-- --++-- -- example --
1526
1527 ;;}}}
1528 ;;{{{ final setup
1529
1530 (tinyload-install)
1531 (provide   'tinyload)
1532 (run-hooks 'tinyload-:load-hook)
1533
1534 ;;}}}
1535
1536 ;;; tinyload.el ends here