From 667d581e06b861f5cea64eb4352cab377e60c71d Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Tue, 10 Nov 2015 18:04:28 -0500 Subject: [PATCH] Complete rework of org config --- contrib/ob-rust.el | 207 ++++++++++++++++ modules/lib/defuns-org.el | 93 ++++++++ modules/module-org.el | 479 +++++++++++++++++++++++++++++--------- 3 files changed, 671 insertions(+), 108 deletions(-) create mode 100644 contrib/ob-rust.el diff --git a/contrib/ob-rust.el b/contrib/ob-rust.el new file mode 100644 index 000000000..d0bad3e07 --- /dev/null +++ b/contrib/ob-rust.el @@ -0,0 +1,207 @@ +;;; ob-rust.el --- org-babel functions for rust + +;; Author: Philip Munksgaard +;; Keywords: literate programming, reproducible research +;; Homepage: http://orgmode.org + +;;; Commentary: + +;; Org-Babel support for evaluating rust code. +;; +;; This is simply an adaptation of ob-C.el that instead targets rust code. +;; Thanks to the original creators Eric Schulte and Thierry Banel +;; +;; Very limited implementation: +;; - currently only support :results output +;; - not much in the way of error feedback + +;;; Code: +(eval-when-compile + (require 'cl)) +(require 'ob) +(require 'cc-mode) + +(declare-function org-entry-get "org" + (pom property &optional inherit literal-nil)) + + +(defvar org-babel-tangle-lang-exts) +(add-to-list 'org-babel-tangle-lang-exts '("rust")) + +(defvar org-babel-default-header-args:rust '()) + +(defvar org-babel-rust-compiler "rustc" + "Command used to compile a rust source code file into an +executable.") + +(defun org-babel-expand-body:rust (body params) + (org-babel-rust-expand body params)) + +(defun org-babel-execute:rust (body params) + "Execute a block of rust code with org-babel. +This function is called by `org-babel-execute-src-block'." + (print body) + (princ params) + (let* ((tmp-src-file (org-babel-temp-file + "rust-src-" + ".rs")) + (tmp-bin-file (org-babel-temp-file "rust-bin-" org-babel-exeext)) + (cmdline (cdr (assoc :cmdline params))) + (flags (cdr (assoc :flags params))) + (full-body (org-babel-rust-expand body params)) + (compile + (progn + (with-temp-file tmp-src-file (insert full-body)) + (org-babel-eval + (format "%s -o %s %s %s" + org-babel-rust-compiler + (org-babel-process-file-name tmp-bin-file) + (mapconcat 'identity + (if (listp flags) flags (list flags)) " ") + (org-babel-process-file-name tmp-src-file)) "")))) + (let ((results + (org-babel-trim + (org-babel-eval + (concat tmp-bin-file (if cmdline (concat " " cmdline) "")) "")))) + (org-babel-reassemble-table + (org-babel-result-cond (cdr (assoc :result-params params)) + (org-babel-read results) + (let ((tmp-file (org-babel-temp-file "rust-"))) + (with-temp-file tmp-file (insert results)) + (org-babel-import-elisp-from-file tmp-file))) + (org-babel-pick-name + (cdr (assoc :colname-names params)) (cdr (assoc :colnames params))) + (org-babel-pick-name + (cdr (assoc :rowname-names params)) (cdr (assoc :rownames params))))) + )) + +(defun org-babel-rust-expand (body params) + "Expand a block of rust code with org-babel." + (print params) + (print body) + (let ((vars (mapcar #'cdr (org-babel-get-header params :var))) + (main-p (not (string= (cdr (assoc :main params)) "no"))) + (uses (or (cdr (assoc :uses params)) + (org-babel-read (org-entry-get nil "uses" t)))) + (externs (org-babel-read + (or (cdr (assoc :externs params)) + (org-babel-read (org-entry-get nil "externs" t))))) + (attributes (org-babel-read + (or (cdr (assoc :attributes params)) + (org-babel-read (org-entry-get nil "attributes" t)))))) + (mapconcat 'identity + (list + ;; attributes + (mapconcat + (lambda (attr) (format "#[%s]" attr)) + (if (listp attributes) attributes (list attributes)) "\n") + ;; externs + (mapconcat + (lambda (ext) (format "extern crate %s;" ext)) + (if (listp externs) externs (list externs)) "\n") + ;; uses + (mapconcat + (lambda (use) (format "use %s;" use)) + (if (listp uses) uses (list uses)) "\n") + ;; variables + (mapconcat 'org-babel-rust-var-to-rust vars "\n") + ;; body + (if main-p + (org-babel-rust-ensure-main-wrap body) + body) "\n") "\n"))) + +(defun org-babel-rust-ensure-main-wrap (body) + "Wrap BODY in a \"main\" function call if none exists." + (if (string-match "[ \t]*fn+[ \t\n\r]*main[ \t]*(.*)" body) + body + (format "fn main() {\n%s\n}\n" body))) + +(defun org-babel-prep-session:rust (session params) + "This function does nothing as rust is a compiled language with no +support for sessions" + (error "rust is a compiled languages -- no support for sessions")) + +(defun org-babel-load-session:rust (session body params) + "This function does nothing as rust is a compiled language with no +support for sessions" + (error "rust is a compiled languages -- no support for sessions")) + +;; helper functions + +(defun org-babel-rust-format-val (type val) + "Handle the FORMAT part of TYPE with the data from VAL." + (let ((format-data (cadr type))) + (if (stringp format-data) + (cons "" (format format-data val)) + (funcall format-data val)))) + +(defun org-babel-rust-val-to-rust-type (val) + "Determine the type of VAL. +Return a list (TYPE-NAME FORMAT). TYPE-NAME should be the name of the type. +FORMAT can be either a format string or a function which is called with VAL." + (cond + ((integerp val) '("int" "%d")) + ((floatp val) '("f64" "%f")) + ((or (listp val) (vectorp val)) + (lexical-let ((type (org-babel-rust-val-to-rust-list-type val))) + (list (concat "&'static [" (car type) "]") + (lambda (val) + (cons + (format "[%d]%s" + (length val) + (car (org-babel-rust-format-val type (elt val 0)))) + (concat "&[" + (mapconcat (lambda (v) + (cdr (org-babel-rust-format-val type v))) + val + ", ") + "]")))))) + (t ;; treat unknown types as string + '("&'static str" (lambda (val) + (let ((s (format "%s" val))) ;; convert to string for unknown types + (cons (format "[%d]" (1+ (length s))) + (concat "\"" s "\"")))))))) + +(defun org-babel-rust-val-to-rust-list-type (val) + "Determine the rust array type of a VAL." + (let (type) + (mapc + #'(lambda (i) + (let* ((tmp-type (org-babel-rust-val-to-rust-type i)) + (type-name (car type)) + (tmp-type-name (car tmp-type))) + (when (and type (not (string= type-name tmp-type-name))) + (if (and (member type-name '("int" "f64")) + (member tmp-type-name '("int" "f64"))) + (setq tmp-type '("f64" "" "%f")) + (error "Only homogeneous lists are supported by rust. You can not mix %s and %s" + type-name + tmp-type-name))) + (setq type tmp-type))) + val) + type)) + +(defun org-babel-rust-var-to-rust (pair) + "Convert an elisp val into a string of rust code specifying a var +of the same value." + ;; TODO list support + (let ((var (car pair)) + (val (cdr pair))) + (when (symbolp val) + (setq val (symbol-name val)) + (when (= (length val) 1) + (setq val (string-to-char val)))) + (let* ((type-data (org-babel-rust-val-to-rust-type val)) + (type (car type-data)) + (formated (org-babel-rust-format-val type-data val)) + ;; (suffix (car formated)) + (data (cdr formated))) + (format "static %s: %s = %s;" + var + type + ;suffix + data)))) + +(provide 'ob-rust) + +;;; ob-rust.el ends here diff --git a/modules/lib/defuns-org.el b/modules/lib/defuns-org.el index c97dab8f9..aa09048a2 100644 --- a/modules/lib/defuns-org.el +++ b/modules/lib/defuns-org.el @@ -210,5 +210,98 @@ COUNT-FOOTNOTES? is non-nil." (message (format "%d words in %s." wc (if mark-active "region" "buffer"))))) +;;;###autoload (autoload 'narf:org-attach "defuns-org" nil t) +(evil-define-command narf:org-attach (&optional link) + (interactive "") + (require 'org-attach) + (let ((path ".attach") + (new-name (concat (int-to-string (truncate (float-time))) "-" (f-filename link))) + new-path) + (unless (file-exists-p path) + (make-directory path)) + (when path + (setq new-path (format "%s/%s" path new-name)) + (cond ((string-match-p "^https?://" link) + (url-copy-file link new-path)) + (t (copy-file link new-path))) + (insert (format "[[./%s]]" (abbreviate-file-name new-path)))))) + +;;;###autoload +(defun narf/org-remove-link () + "Replace an org link by its description or if empty its address" + (interactive) + (if (org-in-regexp org-bracket-link-regexp 1) + (let ((remove (list (match-beginning 0) (match-end 0))) + (description (if (match-end 3) + (org-match-string-no-properties 3) + (org-match-string-no-properties 1)))) + (apply 'delete-region remove) + (insert description)))) + +;;;###autoload +(defun narf/org-table-next-row () + (interactive) + (if (org-at-table-p) (org-table-next-row) (org-down-element))) + +;;;###autoload +(defun narf/org-table-previous-row () + (interactive) + (if (org-at-table-p) (narf--org-table-previous-row) (org-up-element))) + +;;;###autoload +(defun narf/org-table-next-field () + (interactive) + (if (org-at-table-p) (org-table-next-field) (org-end-of-line))) + +;;;###autoload +(defun narf/org-table-previous-field () + (interactive) + (if (org-at-table-p) (org-table-previous-field) (org-beginning-of-line))) + + +;;;###autoload +(defun narf/org-table-append-row-or-shift-right () + (interactive) + (org-shiftmetaright) + (when (org-at-table-p) (org-metaright))) + +;;;###autoload +(defun narf/org-table-prepend-row-or-shift-left () + (interactive) + (if (org-at-table-p) + (org-shiftmetaright) + (org-shiftmetaleft))) + +;;;###autoload +(defun narf/org-table-append-field-or-shift-down () + (interactive) + (org-shiftmetadown) + (when (org-at-table-p) (org-metadown))) + +;;;###autoload +(defun narf/org-table-prepend-field-or-shift-up () + (interactive) + (if (org-at-table-p) + (org-shiftmetadown) + (org-shiftmetaup))) + +(defun narf--org-table-previous-row () + "Go to the previous row (same column) in the current table. Before doing so, +re-align the table if necessary. (Necessary because org-mode has a +`org-table-next-row', but not `org-table-previous-row')" + (interactive) + (org-table-maybe-eval-formula) + (org-table-maybe-recalculate-line) + (if (and org-table-automatic-realign + org-table-may-need-update) + (org-table-align)) + (let ((col (org-table-current-column))) + (beginning-of-line 0) + (when (or (not (org-at-table-p)) (org-at-table-hline-p)) + (beginning-of-line)) + (org-table-goto-column col) + (skip-chars-backward "^|\n\r") + (when (org-looking-at-p " ") (forward-char)))) + (provide 'defuns-org) ;;; defuns-org.el ends here diff --git a/modules/module-org.el b/modules/module-org.el index 951892ed6..6979e6c34 100644 --- a/modules/module-org.el +++ b/modules/module-org.el @@ -7,8 +7,10 @@ :keymap (make-sparse-keymap) ; defines evil-org-mode-map :group 'evil-org) -(defvar org-directory "~/Dropbox/notes/") -(associate! org-mode :match "/Dropbox/notes/.+$") +(defvar org-directory "~/Dropbox/org/") +(defvar org-directory-contacts (concat org-directory "work/contacts/")) +(defvar org-directory-projects (concat org-directory "work/projects/")) +(defvar org-directory-invoices (concat org-directory "work/invoices/")) (add-hook! org-load 'narf|org-init) (add-hook! org-mode 'evil-org-mode) @@ -28,7 +30,7 @@ (org-table-map-tables 'org-table-align 'quietly) (org-align-all-tags)) (defun narf|org-update () - (org-update-checkbox-count-maybe t)) + (org-update-statistics-cookies t)) (add-hook! org-mode (add-hook 'before-save-hook 'narf|org-realign nil t) (add-hook 'before-save-hook 'narf|org-update nil t)) @@ -46,20 +48,29 @@ t)) org-archive-location (concat org-directory "/archive/%s::") + org-attach-directory ".attach/" + ;; org-mobile-inbox-for-pull (concat org-directory "inbox.org") + ;; org-mobile-directory "~/Dropbox/Apps/MobileOrg" + + org-catch-invisible-edits nil org-confirm-elisp-link-function nil org-completion-use-ido t org-hidden-keywords '(title) org-special-ctrl-a/e t org-hierarchical-todo-statistics t - org-checkbox-hierarchical-statistics t + org-checkbox-hierarchical-statistics nil org-tags-column -87 + org-loop-over-headlines-in-active-region t + org-footnote-auto-label 'plain org-log-done t - org-startup-folded t - org-todo-keywords '((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d)") - (sequence "DOING(s)" "PENDING(p)") - (sequence "|" "CANCELLED(c)")) - org-blank-before-new-entry '((heading . auto) (plain-list-item . auto)) + org-agenda-window-setup 'current-window + org-src-window-setup 'current-window + org-startup-folded 'content + org-todo-keywords '((sequence "TODO(t)" "|" "DONE(d)") + (sequence "LEAD(l)" "NEXT(n)" "ACTIVE(a)" "PENDING(p)" "|" "CANCELLED(c)")) + org-blank-before-new-entry '((heading . nil) + (plain-list-item . auto)) org-export-backends '(ascii html latex md opml) org-tag-alist '(("@home" . ?h) @@ -67,7 +78,7 @@ ("@projects" . ?r)) org-capture-templates - '(("t" "TODO" entry (file+headline "~/Dropbox/notes/todo.org" "Inbox") "* TODO %? %u\n%i") + '(("t" "TODO" entry (file+headline "~/Dropbox/notes/gtd.org" "Inbox") "** TODO %? %u\n%i") ("T" "Project TODO" entry (file+headline (narf/project-org-filename) "Tasks") "** TODO %?\n%i" :prepend t) ("N" "Project Note" entry (file+headline (narf/project-org-filename) "Notes") "** %u %?\n%i") ("c" "Changelog" entry (file+datetree (narf/project-org-filename)) "** %<%H:%M>: %? :unsorted:\n%i" :prepend t) @@ -75,33 +86,38 @@ ("j" "Journal" entry (file+datetree "~/Dropbox/notes/journal.org") "** %?%^g\nAdded: %U\n%i") ("a" "Trivia" entry (file "~/Dropbox/notes/trivia.org") "* %u %?\n%i" :prepend t) ("s" "Writing Scraps" entry (file "~/Dropbox/notes/writing.org") "* %u %?\n%i" :prepend t) - ("v" "Vocab" entry (file "~/Dropbox/notes/vocab.org") "* %?\n%i" :prepend t) - ("e" "Excerpt" entry (file "~/Dropbox/notes/excerpts.org") "* %u %?\n%i" :prepend t))) + ("v" "Vocab" entry (file (concat org-directory "notes/vocab.org")) "* %?\n%i" :prepend t) + ("e" "Excerpt" entry (file (concat org-directory "notes/excerpts.org")) "* %u %?\n%i" :prepend t))) (add-to-list 'org-link-frame-setup '(file . find-file))) (defun narf@org-ex () - (exmap! "edit" 'org-edit-special) + (exmap! "oe[dit]" 'org-edit-special) (exmap! "refile" 'org-refile) (exmap! "archive" 'org-archive-subtree) (exmap! "agenda" 'org-agenda) (exmap! "todo" 'org-show-todo-tree) (exmap! "link" 'org-link) - (exmap! "wc" 'narf/org-word-count)) + (exmap! "wc" 'narf/org-word-count) + (exmap! "at[tach]" 'narf:org-attach)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun narf@org-babel () - (setq org-confirm-babel-evaluate nil ; you don't need my permission + (setq org-confirm-babel-evaluate nil ; you don't need my permission org-src-fontify-natively t ; make code pretty org-src-tab-acts-natively t) + (require 'ob-http) + (require 'ob-rust) (org-babel-do-load-languages 'org-babel-load-languages '((python . t) (ruby . t) (sh . t) (js . t) (css . t) (plantuml . t) (emacs-lisp . t) (matlab . t) - (latex . t) (calc . t))) + (latex . t) (calc . t) + (http . t) (rust . t))) + (add-to-list 'org-src-lang-modes '("rust" . rust)) (add-to-list 'org-src-lang-modes '("puml" . puml)) (add-to-list 'org-src-lang-modes '("plantuml" . puml))) @@ -109,32 +125,72 @@ (setq-default org-latex-preview-ltxpng-directory (concat narf-temp-dir "ltxpng/") org-latex-create-formula-image-program 'dvipng - org-startup-with-latex-preview t + org-startup-with-latex-preview nil + org-highlight-latex-and-related nil org-highlight-latex-and-related '(latex)) - (plist-put org-format-latex-options :scale 1.5)) + + (require 'company-math) + (define-company-backend! org-mode + (math-symbols-latex + math-symbols-unicode + latex-commands + company-capf + company-yasnippet + company-dabbrev-code + company-keywords)) + + (plist-put org-format-latex-options :scale 1.65)) (defun narf@org-looks () (setq org-image-actual-width nil org-startup-with-inline-images t + org-startup-indented t org-pretty-entities t - org-fontify-whole-heading-line t - org-hide-emphasis-markers t) - (after! iimage (diminish 'iimage-mode)) - ;; (setq iimage-mode-image-regex-alist - ;; '(("\\(`?file://\\|\\[\\[\\|<\\|`\\)?\\([-+./_0-9a-zA-Z]+\\.\\(GIF\\|JP\\(?:E?G\\)\\|P\\(?:BM\\|GM\\|N[GM]\\|PM\\)\\|SVG\\|TIFF?\\|X\\(?:[BP]M\\)\\|gif\\|jp\\(?:e?g\\)\\|p\\(?:bm\\|gm\\|n[gm]\\|pm\\)\\|svg\\|tiff?\\|x\\(?:[bp]m\\)\\)\\)\\(\\]\\]\\|>\\|'\\)?" . 2) - ;; ("<\\(http://.+\\.\\(GIF\\|JP\\(?:E?G\\)\\|P\\(?:BM\\|GM\\|N[GM]\\|PM\\)\\|SVG\\|TIFF?\\|X\\(?:[BP]M\\)\\|gif\\|jp\\(?:e?g\\)\\|p\\(?:bm\\|gm\\|n[gm]\\|pm\\)\\|svg\\|tiff?\\|x\\(?:[bp]m\\)\\)\\)>" . 1))) + org-pretty-entities-include-sub-superscripts t + org-use-sub-superscripts '{} + org-fontify-whole-heading-line nil + org-hide-emphasis-markers t + org-hide-leading-stars t) + + (add-hook! org-mode + (setq truncate-lines nil + word-wrap t)) + + (after! org-indent (diminish 'org-indent-mode)) + (after! iimage (diminish 'iimage-mode)) + (setq iimage-mode-image-regex-alist + '(("\\(`?file://\\|\\[\\[\\|<\\|`\\)?\\([-+./_0-9a-zA-Z]+\\.\\(GIF\\|JP\\(?:E?G\\)\\|P\\(?:BM\\|GM\\|N[GM]\\|PM\\)\\|SVG\\|TIFF?\\|X\\(?:[BP]M\\)\\|gif\\|jp\\(?:e?g\\)\\|p\\(?:bm\\|gm\\|n[gm]\\|pm\\)\\|svg\\|tiff?\\|x\\(?:[bp]m\\)\\)\\)\\(\\]\\]\\|>\\|'\\)?" . 2) + ("<\\(http://.+\\.\\(GIF\\|JP\\(?:E?G\\)\\|P\\(?:BM\\|GM\\|N[GM]\\|PM\\)\\|SVG\\|TIFF?\\|X\\(?:[BP]M\\)\\|gif\\|jp\\(?:e?g\\)\\|p\\(?:bm\\|gm\\|n[gm]\\|pm\\)\\|svg\\|tiff?\\|x\\(?:[bp]m\\)\\)\\)>" . 1))) + (when IS-MAC ;; Display images with quicklook on OSX (add-to-list 'org-file-apps '("\\.\\(jpe?g\\|png\\|gif\\|pdf\\)\\'" . "qlmanage -p %s"))) - (setq org-hide-leading-stars t) - (after! org-indent (diminish 'org-indent-mode)) - ;; (add-hook! org-mode 'org-indent-mode) - (add-hook! org-mode (setq line-spacing '0.2)) + (add-hook! org-mode (setq line-spacing '0.1)) + ;; (add-hook! org-mode (text-scale-set 1)) (require 'org-bullets) - (setq org-bullets-bullet-list '("▪" "◇" "•" "◦" )) - (add-hook! org-mode 'org-bullets-mode)) + (setq org-bullets-bullet-list '("▪" "•" "◦" "*" )) + (add-hook! org-mode 'org-bullets-mode) + + (defun narf--not-in-org-src-block (beg end) + (notany (lambda (overlay) + (eq (overlay-get overlay 'face) 'org-block-background)) + (overlays-in beg end))) + + (font-lock-add-keywords + 'org-mode `(("\\(#\\+begin_src\\>\\)" + (0 (narf/show-as ?#))) + ("\\(#\\+end_src\\>\\)" + (0 (narf/show-as ?#))) + ("\\(#\\+begin_quote\\>\\)" + (0 (narf/show-as ?➤))) + ("\\(#\\+end_quote\\>\\)" + (0 (narf/show-as ?➤))) + ("\\([-+] \\[X\\]\\)" + (0 (narf/show-as ?☑))) + ("\\([-+] \\[ \\]\\)" + (0 (narf/show-as ?☐)))))) (defun narf@org-plantuml () (setq org-plantuml-jar-path puml-plantuml-jar-path) @@ -142,18 +198,59 @@ (add-to-list 'org-babel-default-header-args:plantuml '(:cmdline . "-config ~/.plantuml")))) +(defun narf@org-links () + (defun narf--org-file2title (file) + (s-replace " " "_" (downcase file))) + (defun narf-org-link-trello (url) + (browse-url (format "https://trello.com/%s" (url-encode-url url)))) + (defun narf-org-link-contact (id) + (org-open-file (format "%swork/contacts/%s.org" org-directory (narf--org-file2title id)) t)) + (defun narf-org-link-invoice (id) + (org-open-file (format "%swork/invoices/%s.yml" org-directory (narf--org-file2title id)) t)) + (defun narf-org-link-project (id) + (org-open-file (format "%swork/projects/%s.org" org-directory (narf--org-file2title id)) t)) + + (org-add-link-type "trello" 'narf-org-link-trello) + (org-add-link-type "contact" 'narf-org-link-contact) + (org-add-link-type "project" 'narf-org-link-project) + (org-add-link-type "invoice" 'narf-org-link-invoice) + + (defun narf-org-complete (type) + (let ((default-directory (symbol-value (intern (format "org-directory-%ss" type))))) + (let ((file (org-iread-file-name ">>> "))) + (format "%s:%s" type (capitalize (s-replace "_" " " (f-base file))))))) + (defun org-contact-complete-link () + (narf-org-complete "contact")) + (defun org-project-complete-link () + (narf-org-complete "project")) + (defun org-invoice-complete-link () + (narf-org-complete "invoice"))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun narf|org-init () + (after! autoinsert + (add-template! (format "%s.\\.org$" org-directory-contacts) "__contact.org" 'org-mode) + (add-template! (format "%s.\\.org$" org-directory-projects) "__projects.org" 'org-mode) + (add-template! (format "%s.\\.yml$" org-directory-invoices) "__invoices.org" 'org-mode)) + (advice-add 'evil-force-normal-state :before 'org-remove-occur-highlights) (narf@org-vars) (narf@org-babel) (narf@org-latex) (narf@org-looks) + (narf@org-links) (narf@org-plantuml) (add-hook! org-mode 'narf@org-ex) + ;; Align table, if in table when exiting insert mode + (defun narf|org-realign-table () + (when (org-table-p) + (org-table-align))) + (add-hook! org-mode + (add-hook 'evil-insert-state-exit-hook 'narf|org-realign-table t t)) + (require 'org-agenda) (setq org-agenda-restore-windows-after-quit t org-agenda-custom-commands @@ -166,24 +263,55 @@ ("tg" tags-todo "+gamedev") ("tw" tags-tree "+webdev"))) - ;; Custom link types - (org-add-link-type "trello" 'narf-org-link-trello) - (org-add-link-type "contact" 'narf-org-link-contact) - (org-add-link-type "invoice" 'narf-org-link-invoice) - - (defun narf-org-link-trello (url) - "Open a trello url" - (browse-url (format "https://trello.com/%s" (url-encode-url url)))) - (defun narf-org-link-contact (id) - (org-open-file (format "%scontacts.org" org-directory) t nil (format "#%s" id))) - (defun narf-org-link-invoice (id)) - (defun narf-project-org-filename (cat) (interactive (list (completing-read "Choose category:" (mapcar 'f-filename (f-directories org-project-directory))))) (expand-file-name (concat (file-name-nondirectory (directory-file-name (narf/project-root))) ".org") (expand-file-name cat org-project-directory))) + ;; Add element delimiter text-objects so we can use evil-surround to + ;; manipulate them. + (evil-define-text-object evil-inner-org-bold-element (count &optional beg end type) + :extend-selection nil (evil-select-quote ?* beg end type count)) + (evil-define-text-object evil-a-org-bold-element (count &optional beg end type) + :extend-selection nil (evil-select-quote ?* beg end type count t)) + + (evil-define-text-object evil-inner-org-italic-element (count &optional beg end type) + :extend-selection nil (evil-select-quote ?/ beg end type count)) + (evil-define-text-object evil-a-org-italic-element (count &optional beg end type) + :extend-selection nil (evil-select-quote ?/ beg end type count t)) + + (evil-define-text-object evil-inner-org-underline-element (count &optional beg end type) + :extend-selection nil (evil-select-quote ?_ beg end type count)) + (evil-define-text-object evil-a-org-underline-element (count &optional beg end type) + :extend-selection nil (evil-select-quote ?_ beg end type count t)) + + (evil-define-text-object evil-inner-org-code-element (count &optional beg end type) + :extend-selection nil (evil-select-quote ?= beg end type count)) + (evil-define-text-object evil-a-org-code-element (count &optional beg end type) + :extend-selection nil (evil-select-quote ?= beg end type count t)) + + (evil-define-text-object evil-inner-org-verbatim-element (count &optional beg end type) + :extend-selection nil (evil-select-quote ?~ beg end type count)) + (evil-define-text-object evil-a-org-verbatim-element (count &optional beg end type) + :extend-selection nil (evil-select-quote ?~ beg end type count t)) + + (define-key evil-outer-text-objects-map "*" 'evil-a-org-bold-element) + (define-key evil-inner-text-objects-map "*" 'evil-inner-org-bold-element) + + (define-key evil-outer-text-objects-map "/" 'evil-a-org-italic-element) + (define-key evil-inner-text-objects-map "/" 'evil-inner-org-italic-element) + + (define-key evil-outer-text-objects-map "_" 'evil-a-org-underline-element) + (define-key evil-inner-text-objects-map "_" 'evil-inner-org-underline-element) + + (define-key evil-outer-text-objects-map "=" 'evil-a-org-code-element) + (define-key evil-inner-text-objects-map "=" 'evil-inner-org-code-element) + + (define-key evil-outer-text-objects-map "~" 'evil-a-org-verbatim-element) + (define-key evil-inner-text-objects-map "~" 'evil-inner-org-verbatim-element) + + ;; Keybinds (bind! (:map org-mode-map "RET" nil "C-j" nil @@ -197,82 +325,112 @@ :ni "A-h" 'org-metaleft ; M-h :ni "A-k" 'org-metaup ; M-k :ni "A-j" 'org-metadown ; M-j - :ni "A-l" 'org-shiftmetaright ; M-L - :ni "A-h" 'org-shiftmetaleft ; M-H - :ni "A-k" 'org-shiftmetaup ; M-K - :ni "A-j" 'org-shiftmetadown ; M-J - :ni "" 'org-beginning-of-line - :ni "" 'org-end-of-line - :ni "" 'org-up-element - :ni "" 'org-down-element + ;; Expand tables (or shiftmeta move) + :ni "A-L" 'narf/org-table-append-row-or-shift-right + :ni "A-H" 'narf/org-table-prepend-row-or-shift-left + :ni "A-K" 'narf/org-table-prepend-field-or-shift-up + :ni "A-J" 'narf/org-table-append-field-or-shift-down + + :i "C-L" 'narf/org-table-next-field + :i "C-H" 'narf/org-table-previous-field + :i "C-K" 'narf/org-table-previous-row + :i "C-J" 'narf/org-table-next-row + + :nv "j" 'evil-next-visual-line + :nv "k" 'evil-previous-visual-line :ni "M-a" 'mark-whole-buffer - :n "gr" 'org-babel-execute-src-block-maybe + :n "gr" 'org-babel-execute-src-block-maybe - :i "C-e" 'org-end-of-line - :i "C-a" 'org-beginning-of-line - ;; Add new header line before this line - :i "" 'narf/org-insert-item-before - ;; Add new header line after this line - :i "" 'narf/org-insert-item-after + :i "C-e" 'org-end-of-line + :i "C-a" 'org-beginning-of-line + ;; Add new header line before this line + :i "" 'narf/org-insert-item-before + ;; Add new header line after this line + :i "" 'narf/org-insert-item-after - :i "M-b" (λ (narf/org-surround "*")) ; bold - :i "M-u" (λ (narf/org-surround "_")) ; underline - :i "M-i" (λ (narf/org-surround "/")) ; italics - :i "M-`" (λ (narf/org-surround "+")) ; strikethrough + :i "M-b" (λ (narf/org-surround "*")) ; bold + :i "M-u" (λ (narf/org-surround "_")) ; underline + :i "M-i" (λ (narf/org-surround "/")) ; italics + :i "M-`" (λ (narf/org-surround "+")) ; strikethrough - :v "M-b" "S*" - :v "M-u" "S_" - :v "M-i" "S/" - :v "M-`" "S+" + :v "M-b" "S*" + :v "M-u" "S_" + :v "M-i" "S/" + :v "M-`" "S+" + + :i "M-b" (λ (if (org-element-bold-parser) (evil-surround-delete ?\*) (insert "**") (backward-char))) + :i "M-u" (λ (if (org-element-bold-parser) (evil-surround-delete ?\_) (insert "__") (backward-char))) + :i "M-i" (λ (if (org-element-bold-parser) (evil-surround-delete ?\/) (insert "//") (backward-char))) + :i "M-`" (λ (if (org-element-bold-parser) (evil-surround-delete ?\+) (insert "++") (backward-char))) :n ",;" 'helm-org-in-buffer-headings :nv ",l" 'org-insert-link - :n ",=" 'org-align-all-tags - :n ",/" 'org-sparse-tree - :n ",?" 'org-tags-view - :n ",a" 'org-attach - :n ",D" 'org-time-stamp-inactive - :n ",T" 'org-show-todo-tree - :n ",d" 'org-time-stamp - :n ",r" 'org-refile - :n ",s" 'org-schedule - :n ",t" 'org-todo - :n ",SPC" 'narf/org-toggle-checkbox - :n "," 'org-archive-subtree + :n ",=" 'org-align-all-tags + :n ",f" 'org-sparse-tree + :n ",?" 'org-tags-view + :n ",a" 'org-attach + :n ",D" 'org-time-stamp-inactive + :n ",T" 'org-show-todo-tree + :n ",d" 'org-time-stamp + :n ",r" 'org-refile + :n ",s" 'org-schedule + :n ",t" 'org-todo + :n ",SPC" 'narf/org-toggle-checkbox + :n "," 'org-archive-subtree - :n "gr" 'org-babel-execute-src-block-maybe - :m "gh" 'outline-up-heading - :m "gj" (λ (hide-subtree) (call-interactively 'org-forward-heading-same-level) (show-children)) - :m "gk" (λ (hide-subtree) (call-interactively 'org-backward-heading-same-level) (show-children)) - ;; :m "gk" 'org-backward-heading-same-level - :m "gl" (λ (call-interactively 'outline-next-visible-heading) (show-children)) - :n "go" 'org-open-at-point - :n "gO" 'org-attach-open - :n "gC-o" 'org-attach-reveal - :n "gI" (λ (if (> (length org-inline-image-overlays) 0) - (org-remove-inline-images) - (org-display-inline-images nil t (line-beginning-position) (line-end-position)))) - :n "gQ" 'org-fill-paragraph - :n "ga" 'org-attach - :n "gA" 'org-agenda - :n "gt" 'org-show-todo-tree - :m "]l" 'org-next-link - :m "[l" 'org-previous-link - :m "$" 'org-end-of-line - :m "^" 'org-beginning-of-line - :nv "<" 'org-metaleft - :nv ">" 'org-metaright - :n "-" 'org-cycle-list-bullet - :n "" 'narf/org-insert-item-before - :n "" 'narf/org-insert-item-after - :n "RET" (λ (cond ((org-at-item-checkbox-p) - (org-toggle-checkbox)) - ((org-entry-is-todo-p) - (org-todo 'done)))) - :n [tab] 'org-cycle) + :n "za" 'org-cycle + :n "zA" 'org-shifttab + :n "zm" 'hide-body + :n "zr" 'show-all + :n "zo" 'show-subtree + :n "zO" 'show-all + :n "zc" 'hide-subtree + :n "zC" 'hide-all + + :n "gr" (λ (cond ((org-table-p) + (org-table-align)) + ((org-in-src-block-p) + (org-babel-execute-src-block)) + ((org-inside-LaTeX-fragment-p) + (org-toggle-latex-fragment)) + (t (org-toggle-inline-images)))) + :m "gh" 'outline-up-heading + :m "gj" (λ (hide-subtree) (call-interactively 'org-forward-heading-same-level) (show-children)) + :m "gk" (λ (hide-subtree) (call-interactively 'org-backward-heading-same-level) (show-children)) + ;; :m "gk" 'org-backward-heading-same-level + :m "gl" (λ (call-interactively 'outline-next-visible-heading) (show-children)) + + :n "go" 'org-open-at-point + :n "gO" (λ (let ((org-link-frame-setup (append '((file . find-file-other-window)) org-link-frame-setup))) + (call-interactively 'org-open-at-point))) + + :n "ga" 'org-attach + :n "gC-o" 'org-attach-reveal + :n "gI" (λ (if (> (length org-inline-image-overlays) 0) + (org-remove-inline-images) + (org-display-inline-images nil t (line-beginning-position) (line-end-position)))) + :n "gQ" 'org-fill-paragraph + :n "gA" 'org-agenda + :n "gt" 'org-show-todo-tree + :m "]l" 'org-next-link + :m "[l" 'org-previous-link + :m "$" 'org-end-of-line + :m "^" 'org-beginning-of-line + :n "<" 'org-metaleft + :n ">" 'org-metaright + :v "<" (λ (org-metaleft) (evil-visual-restore)) + :v ">" (λ (org-metaright) (evil-visual-restore)) + :n "-" 'org-cycle-list-bullet + :n "" 'narf/org-insert-item-before + :n "" 'narf/org-insert-item-after + :n "RET" (λ (cond ((org-at-item-checkbox-p) + (org-toggle-checkbox)) + ((org-entry-is-todo-p) + (org-todo 'done)))) + :n [tab] 'org-cycle) (:after org-agenda (:map org-agenda-mode-map @@ -280,7 +438,112 @@ :e "C-j" 'org-agenda-next-item :e "C-k" 'org-agenda-previous-item :e "C-n" 'org-agenda-next-item - :e "C-p" 'org-agenda-previous-item)))) + :e "C-p" 'org-agenda-previous-item))) + + (progn ;; Org hacks + (defun org-insert-link (&optional complete-file link-location default-description) + (interactive "P") + (let* ((wcf (current-window-configuration)) + (origbuf (current-buffer)) + (region (if (org-region-active-p) (buffer-substring (region-beginning) (region-end)))) + (remove (and region (list (region-beginning) (region-end)))) + (desc region) + tmphist ; byte-compile incorrectly complains about this + (link link-location) + (abbrevs org-link-abbrev-alist-local) + entry file all-prefixes auto-desc) + (cond + (link-location) ; specified by arg, just use it. + ((org-in-regexp org-bracket-link-regexp 1) + ;; We do have a link at point, and we are going to edit it. + (setq remove (list (match-beginning 0) (match-end 0))) + (setq desc (if (match-end 3) (org-match-string-no-properties 3))) + (setq link (read-string "Link: " + (org-link-unescape + (org-match-string-no-properties 1))))) + ((or (org-in-regexp org-angle-link-re) + (org-in-regexp org-plain-link-re)) + ;; Convert to bracket link + (setq remove (list (match-beginning 0) (match-end 0)) + link (read-string "Link: " (org-remove-angle-brackets (match-string 0))))) + ((member complete-file '((4) (16))) + ;; Completing read for file names. + (setq link (org-file-complete-link complete-file))) + (t (org-link-fontify-links-to-this-file) + (setq tmphist (append (mapcar 'car org-stored-links) org-insert-link-history)) + (setq all-prefixes (append (mapcar 'car abbrevs) + (mapcar 'car org-link-abbrev-alist) + org-link-types)) + (unwind-protect + (progn (setq link (org-completing-read + "Link: " (append (mapcar (lambda (x) (concat x ":")) all-prefixes) + (mapcar 'car org-stored-links)) + nil nil nil 'tmphist (caar org-stored-links))) + (if (not (string-match "\\S-" link)) + (user-error "No link selected")) + (mapc (lambda(l) + (when (equal link (cadr l)) (setq link (car l) auto-desc t))) + org-stored-links) + (if (or (member link all-prefixes) + (and (equal ":" (substring link -1)) + (member (substring link 0 -1) all-prefixes) + (setq link (substring link 0 -1)))) + (setq link (with-current-buffer origbuf + (org-link-try-special-completion link))))) + (set-window-configuration wcf)) + (setq entry (assoc link org-stored-links)) + (or entry (push link org-insert-link-history)) + (setq desc (or desc (nth 1 entry))))) + (if (funcall (if (equal complete-file '(64)) 'not 'identity) + (not org-keep-stored-link-after-insertion)) + (setq org-stored-links (delq (assoc link org-stored-links) + org-stored-links))) + (if (and (string-match org-plain-link-re link) + (not (string-match org-ts-regexp link))) + (setq link (org-remove-angle-brackets link))) + (when (and buffer-file-name + (string-match "^file:\\(.+?\\)::\\(.+\\)" link)) + (let* ((path (match-string 1 link)) + (case-fold-search nil) + (search (match-string 2 link))) + (save-match-data + (if (equal (file-truename buffer-file-name) (file-truename path)) + (setq link search))))) + (when (string-match "^\\(file:\\|docview:\\)\\(.*\\)" link) + (let* ((type (match-string 1 link)) + (path (match-string 2 link)) + (origpath path) + (case-fold-search nil)) + (cond ((or (eq org-link-file-path-type 'absolute) + (equal complete-file '(16))) + (setq path (abbreviate-file-name (expand-file-name path)))) + ((eq org-link-file-path-type 'noabbrev) + (setq path (expand-file-name path))) + ((eq org-link-file-path-type 'relative) + (setq path (file-relative-name path))) + (t (save-match-data + (if (string-match (concat "^" (regexp-quote (expand-file-name + (file-name-as-directory default-directory)))) + (expand-file-name path)) + (setq path (substring (expand-file-name path) (match-end 0))) + (setq path (abbreviate-file-name (expand-file-name path))))))) + (setq link (concat type path)) + (if (equal desc origpath) + (setq desc path)))) + (if org-make-link-description-function + (setq desc + (or (condition-case nil + (funcall org-make-link-description-function link desc) + (error (progn (message "Can't get link description from `%s'" + (symbol-name org-make-link-description-function)) + (sit-for 2) nil))) + (read-string "Description: " default-description))) + (if default-description (setq desc default-description) + (setq desc (or (and auto-desc desc) + (read-string "Description: " desc))))) + (unless (string-match "\\S-" desc) (setq desc nil)) + (if remove (apply 'delete-region remove)) + (insert (org-make-link-string link desc)))))) (provide 'module-org) ;;; module-org.el ends here