Skip to content

Instantly share code, notes, and snippets.

@matthew-ball
Created June 22, 2012 04:13
Show Gist options
  • Save matthew-ball/2970172 to your computer and use it in GitHub Desktop.
Save matthew-ball/2970172 to your computer and use it in GitHub Desktop.
;; FILE: /home/chu/Programming/lisp/elisp/ref-man/org-ref-man.el
;; AUTHOR: Matthew Ball (copyleft 2012)
;; TIME: Fri 25 May 2012 02:46:58 EST
;;; COMMENT:
;; Welcome to `org-ref-man', a reference manager for GNU Emacs utilising the functionality of `org-mode'.
;; The project's goals state:
;; 1. Creates and manages a database of source material.
;; 2. Interact with this database with emacs-lisp.
;; 3. Creates an `org-mode' buffer with the information read from the database.
;; 4. Writes information to the database created from the corresponding `org-mode' buffer.
;; TODO:
;; 1. Populate database.
;; 2. Write parser to read from `org-table' entries.
(require 'ref-man-db)
(require 'org-table)
(eval-when-compile (require 'cl))
;; COMMENT: custom variables
(defcustom ref-man-directory (expand-file-name "~/Documents/Papers/PDFs/")
"`ref-man' main directory; for storing/locating reference material."
:type 'directory :safe 'stringp :group 'ref-man)
(defcustom ref-man-extension "pdf" ;; TODO: should make this a list
"`ref-man' file extension."
:type 'string :safe 'stringp :group 'ref-man)
;; COMMENT: constants
(defconst ref-man-version "0.3" "Version of the system's `ref-man' install.")
(defconst ref-man-mode-name "org-ref-man" "The `ref-man' mode name.")
(defconst ref-man-buffer-name (concat "*" ref-man-mode-name "*") "The `ref-man' buffer name.")
(defconst ref-man-column-length 20 "Column length for `ref-man'.")
;; COMMENT: variables
(defvar ref-man-mode-hook nil "Run hook when entering `ref-man' mode.")
(defvar ref-man-files nil "List of files.")
(defvar ref-man-filter-regexp nil "Current filter regexp used by `ref-man' mode.") ;; NOTE: should be able to filter `tags'
;; COMMENT: functions
(defun ref-man-reset-files ()
"Reset the `ref-man-files' list."
(setq ref-man-files nil))
(defun ref-man-find-files ()
"Return a list of all files in the `ref-man-directory' directory which matches the `ref-man-extension' extension."
(if (file-exists-p ref-man-directory)
(let (files result)
(setq files (directory-files ref-man-directory t (concat "\." ref-man-extension "$") t))
(dolist (file-name files)
(when (and (file-readable-p file-name) (not (file-directory-p file-name)))
(setq result (cons file-name result))))
result)))
(defun ref-man-buffer-setup ()
"Render the file browser in the `*ref-man*' buffer."
(if (y-or-n-p "Scan `ref-man-directory' location?")
(setq ref-man-files (ref-man-find-files)) ;; NOTE: find files
(setq ref-man-files (ref-man-load-database)) ;; NOTE: load database
)
;;(ref-man-populate-database)
(ref-man-read-from-database)
(if (not (file-exists-p ref-man-directory))
(insert (concat "ERROR: Directory " ref-man-directory " does not exist."))
(if ref-man-files
(progn
(insert "|----------------------------------------+----------+--------|\n")
(insert "| *Title* | *Author* | *Year* |\n")
(insert "|----------------------------------------+----------+--------|\n")
;; TODO: need to write a mini-parser to remove the cruft from the list
(dolist (entry ref-man-files)
(insert
(concat "| [[" (plist-get entry :file) "][" (plist-get entry :title) "]] | "
(plist-get entry :author) " | "
(plist-get entry :year) " |"))
(insert "\n"))
(insert "|----------------------------------------+----------+--------|\n\n")
(insert "* Bibliography Information\n"))
(insert (concat "ERROR: No files were loaded."))
)))
;; NOTE: this is redundant
;; (defun ref-man-populate-database ()
;; "Populate the `ref-man' database with the contents of the `ref-man-files' variable."
;; (dolist (entry ref-man-files)
;; ;; TODO: write a "mini-parser"
;; (ref-man-create-record
;; (plist-get entry :file)
;; (plist-get entry :title)
;; (plist-get entry :author)
;; (plist-get entry :type)
;; (plist-get entry :year)
;; (plist-get entry :tags))))
(defun ref-man-read-from-database ()
"..."
(dolist (record ref-man-record-database) ;; NOTE: loop through the records of the (external) `ref-man-db' database
(add-to-list 'ref-man-files record) ;; NOTE: ... add the record to the (internal) `ref-man' database
))
;; TODO: this could probably be moved into the `org-config.el' file
(defun my-org-extract-link ()
"Extract the link location at point and put it on the killring."
(interactive)
(save-excursion
;; TODO: capture `file-name' details
;; TODO: capture `title' details
(when (org-in-regexp org-bracket-link-regexp 1)
(let ((record-file (org-link-unescape (org-match-string-no-properties 1)))
(record-title (org-link-unescape (org-match-string-no-properties 3))))
(message (concat "File: " record-file ", Title: " record-title))))))
(defun ref-man-save-to-database () ;; TODO: this function is very close to working
"Read a list structure from the `org-table' entries.
NOTE: This function needs to manipulate the contents of the `org-table' - namely, it removes the first three entries (headings) and any `hline' entries.
The function `org-table-to-lisp' extracts /exactly/ the contents of the table. Since there is a different structure to the database file, this attempts to clean things up, extracting the necessary data."
(let ((list-data (org-table-to-lisp))
(save-file-list '()))
(setq list-data (cdr (cdr (cdr list-data)))) ;; NOTE: remove the hline, headings and hline
(dolist (record list-data) ;; NOTE: go through the `list-data' list and add each element to `save-file-list'
(when (not (eq record 'hline)) ;; NOTE: remove the final hline
;; NOTE: the following is really hackish
(let ((record-file (car record))
(record-title nil)
(record-author (car (cdr record)))
(record-year (car (cdr (cdr record)))))
(with-temp-buffer
(switch-to-buffer "ref-man-save-file-list")
(print (concat "(:file " record-file
" :title " record-title
" :author " record-author
" :year " record-year ")\n")
(current-buffer))))
(add-to-list 'save-file-list record)))
;; TODO: manipulate `save-file-list' data structure
;; It's currently in the form:
;; ("[[file-name][title]]" "author" "year")
;; ... and needs to be ...
;; (:file "file-name" :title "title" :author "author" :year "year")
;; (insert (replace-regexp "\"[[file-name][title]]\"" ":file file-name :title title"))
;; sed -i 's/"\[\[\([^\]]*\)][\([^\]]*\)\]" "\([^"]*\)" "\([^"]*\)"/:file "\1" :title "\2"/' :author "\3" :year "\4"/'
;; DEBUG: saving the database ...
(with-temp-buffer
(switch-to-buffer "ref-man-test")
(print save-file-list (current-buffer))
;; (replace-regexp "[[*][*]]" ":file :title") ;; FIX: doesn't work
)
;; (ref-man-save-database list-data) ;; NOTE: save the contents of the `org-table'
;; (ref-man-save-database ref-man-files) ;; NOTE: save the `ref-man-files' list
))
(defun ref-man-parse-table (table-string &rest junk)
"Parse an `org-table' entry.")
;; COMMENT: commands
(defun ref-man-add-file (file &optional title author type notes) ;; TODO: ...
"Add a file to the `ref-man-files' database."
(interactive "f\nSelect file:"))
(defun ref-man-quit (&rest junk)
"Bury the `*ref-man*' buffer."
(interactive)
(setq ref-man-files nil) ;; NOTE: clear database
(when (string= (buffer-name) ref-man-buffer-name)
(kill-buffer))
(delete-other-windows))
(defun ref-man-show-version (&rest junk)
"Print the `ref-man' version number."
(interactive)
(message (concat "`ref-man' - Version: " ref-man-version)))
(defun ref-man-goto-file (&rest junk) ;; NOTE: this is a bit limiting, requires *all* files to be in the same main directory
"Follow filename in `*ref-man*' buffer."
(interactive)
(find-file (concat ref-man-directory (thing-at-point 'filename))))
;; COMMENT: mode configuration
(defvar ref-man-mode-map
(let ((map (make-keymap)))
;; sorting
;; (define-key map (kbd "s") 'ref-man-sort-files) ;; sort by FILE or {AUTHOR, JOURNAL, YEAR, TAGS} ;; ERROR: this won't work
;; open file
(define-key map (kbd "RET") 'ref-man-goto-file)
;; file management
(define-key map (kbd "a") 'ref-man-add-file)
(define-key map (kbd "r") 'ref-man-remove-file) ;; TODO: rename to `ref-man-delete-file'
;; misc
(define-key map (kbd "g") 'ref-man-refresh)
(define-key map (kbd "q") 'ref-man-quit)
(define-key map (kbd "v") 'ref-man-show-version)
;; widgets
(define-key map [down-mouse-1] 'widget-button-click)
(define-key map [down-mouse-2] 'widget-button-click)
(define-key map (kbd "<tab>") 'widget-forward)
(define-key map (kbd "<backtab>") 'widget-backward)
(define-key map (kbd "<S-tab>") 'widget-backward)
map)
"Keymap for `ref-man' major mode")
(defun ref-man-mode (&rest junk)
"Major mode for managing reference files in GNU Emacs."
(kill-all-local-variables)
(setq truncate-lines t)
(ref-man-buffer-setup) ;; NOTE: render `*ref-man*' buffer
(org-mode)
;; NOTE: the following ...
(beginning-of-buffer)
(next-line)
(org-cycle)
(beginning-of-buffer)
;; NOTE: ... is there just to set up the display (there is a better way)
(use-local-map ref-man-mode-map)
(toggle-read-only))
(put 'ref-man-mode 'mode-class 'special)
;;;###autoload
(defun ref-man ()
"Switch to `*ref-man*' buffer and start mode."
(interactive)
(save-excursion
(split-window-below)
(switch-to-buffer ref-man-buffer-name)
(when (not (eq major-mode 'ref-man-mode))
(ref-man-mode))))
(provide 'org-ref-man)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment