]> code.delx.au - gnu-emacs-elpa/blobdiff - packages/load-relative/load-relative.el
Add 'packages/load-relative/' from commit '064074d7dcc8d26d59ab9ceb099b43e6870c58d1'
[gnu-emacs-elpa] / packages / load-relative / load-relative.el
diff --git a/packages/load-relative/load-relative.el b/packages/load-relative/load-relative.el
new file mode 100644 (file)
index 0000000..b2346a3
--- /dev/null
@@ -0,0 +1,255 @@
+;;; load-relative.el --- relative file load (within a multi-file Emacs package)
+
+;; Author: Rocky Bernstein
+;; Version: 1.1
+;; Keywords: internal
+;; URL: http://github.com/rocky/emacs-load-relative
+;; Compatibility: GNU Emacs 23.x
+
+;; Copyright (C) 2015 Free Software Foundation, Inc
+
+;; This program is free software: you can redistribute it and/or
+;; modify it under the terms of the GNU General Public License as
+;; published by the Free Software Foundation, either version 3 of the
+;; License, or (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful, but
+;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+;; General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see
+;; <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Here we provide functions which facilitate writing multi-file Emacs
+;; packages and facilitate running from the source tree without having
+;; to "install" code or fiddle with evil `load-path'. See
+;; https://github.com/rocky/emacs-load-relative/wiki/NYC-Lisp-talk for
+;; the the rationale behind this.
+;;
+;; The functions we add are relative versions of `load', and `require'
+;; and versions which take list arguments. We also add a `__FILE__'
+;; function and a `provide-me' macro.
+
+;; The latest version of this code is at:
+;;     http://github.com/rocky/emacs-load-relative/
+
+;; `__FILE__' returns the file name that that the calling program is
+;; running.  If you are `eval''ing a buffer then the file name of that
+;; buffer is used. The name was selected to be analogous to the name
+;; used in C, Perl, Python, and Ruby.
+
+;; `load-relative' loads an Emacs Lisp file relative to another
+;; (presumably currently running) Emacs Lisp file. For example suppose
+;; you have Emacs Lisp files "foo.el" and "bar.el" in the same
+;; directory. To load "bar.el" from inside Emacs lisp file "foo.el":
+;;
+;;     (require 'load-relative)
+;;     (load-relative "baz")
+;;
+;; The above `load-relative' line could above have also been written as:
+;;
+;;     (load-relative "./baz")
+;; or:
+;;     (load-relative "baz.el")  # if you want to exclude any byte-compiled files
+;;
+;; Use `require-relative' if you want to `require' the file instead of
+;; `load'ing it:
+;;
+;;    (require-relative "baz")
+;;
+;; or:
+;;
+;;    (require-relative "./baz")
+;;
+;; The above not only does a `require' on 'baz', but makes sure you
+;; get that from the same file as you would have if you had issued
+;; `load_relative'.
+;;
+;; Use `require-relative-list' when you have a list of files you want
+;; to `require'. To `require-relative' them all in one shot:
+;;
+;;     (require-relative-list '("dbgr-init" "dbgr-fringe"))
+;;
+;; Finally, macro `provide-me' saves you the trouble of adding a
+;; symbol after `provide' using the file basename (without directory
+;; or file extension) as the name of the thing you want to
+;; provide.
+;;
+;; Using this constrains the `provide' name to be the same as
+;; the filename, but I consider that a good thing.
+
+;;; Code:
+
+;;;###autoload
+(defun __FILE__ (&optional symbol)
+  "Return the string name of file/buffer that is currently begin executed.
+
+The first approach for getting this information is perhaps the
+most pervasive and reliable. But it the most low-level and not
+part of a public API, so it might change in future
+implementations. This method uses the name that is recorded by
+readevalloop of `lread.c' as the car of variable
+`current-load-list'.
+
+Failing that, we use `load-file-name' which should work in some
+subset of the same places that the first method works. However
+`load-file-name' will be nil for code that is eval'd. To cover
+those cases, we try `buffer-file-name' which is initially
+correct, for eval'd code, but will change and may be wrong if the
+code sets or switches buffers after the initial execution.
+
+As a last resort, you can pass in SYMBOL which should be some
+symbol that has been previously defined if none of the above
+methods work we will use the file-name value find via
+`symbol-file'."
+  ;; Not used right now:
+  ;; Failing the above the next approach we try is to use the value of
+  ;; $# - 'the name of this file as a string'. Although it doesn't
+  ;; work for eval-like things, it has the advantage that this value
+  ;; persists after loading or evaluating a file. So it would be
+  ;; suitable if __FILE__ were called from inside a function.
+
+
+  (cond
+   ;; lread.c's readevalloop sets (car current-load-list)
+   ;; via macro LOADHIST_ATTACH of lisp.h. At least in Emacs
+   ;; 23.0.91 and this code goes back to '93.
+   ((stringp (car-safe current-load-list)) (car current-load-list))
+
+   ;; load-like things. 'relative-file-expand' tests in
+   ;; test/test-load.el indicates we should put this ahead of
+   ;; $#.
+   (load-file-name)
+
+   ;; Pick up "name of this file as a string" which is set on
+   ;; reading and persists. In contrast, load-file-name is set only
+   ;; inside eval. As such, it won't work when not in the middle of
+   ;; loading.
+   ;; (#$)
+
+   ;; eval-like things
+   ((buffer-file-name))
+
+   ;; When byte compiling. FIXME: use a more thorough precondition like
+   ;; byte-compile-file is somehwere in the backtrace or that
+   ;; bytecomp-filename comes from that routine?
+   ((boundp 'bytecomp-filename) bytecomp-filename)
+
+   (t (symbol-file symbol)) ;; last resort
+   ))
+
+(defun autoload-relative (function-or-list
+                          file &optional docstring interactive type
+                          symbol)
+  "Autoload an Emacs Lisp file relative to Emacs Lisp code that is in
+the process of being loaded or eval'd.
+
+
+Define FUNCTION to autoload from FILE. FUNCTION is a symbol.
+
+FILE is a string to pass to `load'.
+
+DOCSTRING is documentation for the function.
+
+INTERACATIVE if non-nil says function can be called
+interactively.
+
+TYPE indicates the type of the object: nil or omitted says
+function is a function, `keymap' says function is really a
+keymap, and `macro' or t says function is really a macro.  Third
+through fifth args give info about the real definition.  They
+default to nil.  If function is already defined other than as an
+autoload, this does nothing and returns nil.
+
+SYMBOL is the location of of the file of where that was
+defined (as given by `symbol-file' is used if other methods of
+finding __FILE__ don't work."
+
+  (if (listp function-or-list)
+      (mapcar (lambda(function)
+                (autoload function-or-list
+                  (relative-expand-file-name file symbol)
+                  docstring interactive type))
+              file)
+    (autoload function-or-list (relative-expand-file-name file symbol)
+      docstring interactive type))
+  )
+
+;;;###autoload
+(defun load-relative (file-or-list &optional symbol)
+  "Load an Emacs Lisp file relative to Emacs Lisp code that is in
+the process of being loaded or eval'd.
+
+FILE-OR-LIST is either a string or a list of strings containing
+files that you want to loaded. If SYMBOL is given, the location of
+of the file of where that was defined (as given by `symbol-file' is used
+if other methods of finding __FILE__ don't work."
+
+  (if (listp file-or-list)
+      (mapcar (lambda(relative-file)
+                (load (relative-expand-file-name relative-file symbol)))
+              file-or-list)
+    (load (relative-expand-file-name file-or-list symbol)))
+  )
+
+(defun relative-expand-file-name(relative-file &optional opt-file)
+  "Expand RELATIVE-FILE relative to the Emacs Lisp code that is in
+the process of being loaded or eval'd.
+
+WARNING: it is best to to run this function before any
+buffer-setting or buffer changing operations."
+  (let ((file (or opt-file (__FILE__) default-directory))
+        (prefix))
+    (unless file
+      (error "Can't expand __FILE__ here and no file name given"))
+    (setq prefix (file-name-directory file))
+    (expand-file-name (concat prefix relative-file))))
+
+;;;###autoload
+(defun require-relative (relative-file &optional opt-file opt-prefix)
+  "Run `require' on an Emacs Lisp file relative to the Emacs Lisp code
+that is in the process of being loaded or eval'd. The symbol used in require
+is the base file name (without directory or file extension) treated as a
+symbol.
+
+WARNING: it is best to to run this function before any
+buffer-setting or buffer changing operations."
+  (let ((require-string-name
+         (concat opt-prefix (file-name-sans-extension
+                             (file-name-nondirectory relative-file)))))
+    (require (intern require-string-name)
+             (relative-expand-file-name relative-file opt-file))))
+
+;;;###autoload
+(defmacro require-relative-list (list &optional opt-prefix)
+  "Run `require-relative' on each name in LIST which should be a list of
+strings, each string being the relative name of file you want to run."
+  `(progn
+     (eval-when-compile
+       (require 'cl
+                (dolist (rel-file ,list)
+                  (require-relative rel-file (__FILE__) ,opt-prefix))))
+     (dolist (rel-file ,list)
+       (require-relative rel-file (__FILE__) ,opt-prefix))))
+
+;;;###autoload
+(defmacro provide-me ( &optional prefix )
+  "Call `provide' with the feature's symbol name made from
+source-code's file basename sans extension. For example if you
+write (provide-me) inside file ~/lisp/foo.el, this is the same as
+writing: (provide 'foo).
+
+With a prefix, that prefix is prepended to the `provide' So in
+the previous example, if you write (provide-me \"bar-\") this is the
+same as writing (provide 'bar-foo)
+"
+  `(provide (intern (concat ,prefix (file-name-sans-extension
+                                     (file-name-nondirectory (__FILE__)))))))
+
+(provide-me)
+
+;;; load-relative.el ends here