
* org.el (org-set-tags-to): New command. * org-latex.el (org-export-latex-set-initial-vars): Also check in the plist. * org.el (org-additional-option-like-keywords): Add LATEX_CLASS keyword. * org-exp.el (org-infile-export-plist): Add LATEX_CLASS keyword. 2009-10-01 Carsten Dominik <carsten.dominik@gmail.com> * org-inlinetask.el (org-inlinetask-export): Option removed. (org-inlinetask-export-handler): Better export. * org-xoxo.el (org-export-xoxo-final-hook): New hook. (org-export-as-xoxo): Run the new hook. * org-html.el (org-export-html-final-hook): New hook. (org-export-as-html): Run the new hook. * org-docbook.el (org-export-docbook-final-hook): New hook. (org-export-as-docbook): Run the new hook. * org-ascii.el (org-export-ascii-final-hook): New hook. (org-export-as-ascii): Run the new hook. * org-latex.el (org-export-latex-treat-sub-super-char): Allow a space character as the character before the ^/_. (org-export-latex-final-hook): New hook. (org-export-as-latex): Run `org-export-latex-final-hook'. 2009-10-01 Carsten Dominik <carsten.dominik@gmail.com> * org-macs.el (org-if-unprotected-at): Fix docstring. * org-agenda.el (org-agenda-change-all-lines): Handle invisible text in the prefix (if category is a link). * org-latex.el (org-export-latex-preprocess): Deal properly with empty lines in verse environments. * org.el (org-format-latex-header): Inline fullpage.sty. * org-footnote.el (org-footnote-create-definition): Reveal context to add a new footnote definition. * org.el (org-ctrl-c-ctrl-c): Pass prefix arg to org-table-recalculate when cursor is in TBLFM line. * org-list.el (org-renumber-ordered-list): Fix cursor position when bullet length has changed. 2009-10-01 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-format-latex): Mention `org-format-latex-options' in the docstring. 2009-10-01 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-agenda-get): New function. * org-agenda.el (org-agenda-post-command-hook): No longer move point away from end of line. (org-agenda-add-entry-text, org-agenda-collect-markers) (org-finalize-agenda, org-agenda-mark-clocking-task) (org-agenda-dim-blocked-tasks, org-agenda-entry-text-show-here) (org-agenda-entry-text-show, org-agenda-highlight-todo) (org-agenda-compare-effort, org-agenda-filter-apply) (org-agenda-later, org-agenda-change-time-span) (org-agenda-post-command-hook, org-agenda-show-priority) (org-agenda-show-tags, org-agenda-goto, org-agenda-kill) (org-agenda-archive, org-agenda-archive-to-archive-sibling) (org-remove-subtree-entries-from-agenda, org-agenda-refile) (org-agenda-open-link, org-agenda-copy-local-variable) (org-agenda-switch-to, org-agenda-check-no-diary) (org-agenda-tree-to-indirect-buffer, org-agenda-todo) (org-agenda-add-note, org-agenda-change-all-lines) (org-agenda-priority, org-agenda-set-tags) (org-agenda-set-property, org-agenda-set-effort) (org-agenda-toggle-archive-tag, org-agenda-date-later) (org-agenda-show-new-time, org-agenda-date-prompt) (org-agenda-schedule, org-agenda-deadline, org-agenda-action) (org-agenda-clock-in, org-agenda-bulk-mark) (org-agenda-bulk-unmark, org-agenda-show-the-flagging-note): Use `org-get-at-bol'. * org-colview.el (org-columns-display-here) (org-columns-edit-allowed, org-agenda-columns): Use `org-get-at-bol'. 2009-10-01 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-special-ctrl-a/e): Improve documentation and customize type. (org-end-of-line): Don't jump to after the ellipsis. (org-mode-map): Bind <home> and <end> as well. 2009-10-01 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-fontify-meta-lines-and-blocks): Treat lines with a space after #+ as comments. (org-open-at-point): Run `org-follow-link-hook' always. * org-latex.el (org-export-latex-emph-format): Use better commands to insert special characters in verbatim snippets. 2009-10-01 Carsten Dominik <carsten.dominik@gmail.com> * org-faces.el (org-copy-face): New function. Use it to create various faces formerly created by using `copy-face'. * org-agenda.el (org-prepare-agenda): Don't officially mark this window dedicated. (org-agenda-quit): Kill the frame containing the agenda window if that frame was created for the agenda. 2009-10-01 Carsten Dominik <carsten.dominik@gmail.com> * org-agenda.el (org-agenda-date-prompt): Mark the changed time stamp in the agenda. 2009-10-01 Carsten Dominik <carsten.dominik@gmail.com> * org-agenda.el (org-prepare-agenda): Reset `org-drawers-for-agenda'. (org-prepare-agenda): Uniquify list of drawers. * org.el (org-complex-heading-regexp-format): New variable. (org-set-regexps-and-options): Define `org-complex-heading-regexp-format'. (org-drawers-for-agenda): New variable. (org-map-entries): Bind `org-drawers-for-agenda'. (org-prepare-agenda-buffers): Add to `org-drawers-for-agenda'. * org-remember.el (org-go-to-remember-target) (org-remember-handler): Use `org-complex-heading-regexp-format'. 2009-10-01 Carsten Dominik <carsten.dominik@gmail.com> * org-agenda.el (org-agenda-highlight-todo): Fix text property problem. * org.el (org-on-heading-p, org-at-heading-p): Make sure these are always with `invisible-ok'. (org-store-link): No error when there is nothing to link to in the agenda. * org-list.el (org-update-checkbox-count): Insert changed cookie before the old, to avoid problems with invisibility at the end of the line. (org-update-checkbox-count): Insert changed cookie before the old, to avoid problems with invisibility at the end of the line. * org.el (org-sort-entries-or-items): Include the final newline. (org-fontify-meta-lines-and-blocks): Add indented dynamic block lines for fontification. (org-dblock-start-re, org-dblock-end-re): Allow indentation. (org-prepare-dblock): Store the current indentation of the BEGIN line. (org-update-dblock): Apply the indentation of the begin line to the rest of the block. (org-ctrl-c-ctrl-c): Also find indented dblock lines. (org-startup-folded): New allowed value `showeverything'. (org-startup-options): Add STARTUP keyword `showeverything'. (org-set-startup-visibility): Respect value `showeverything' in org-startup-folded. 2009-10-01 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-closest-date): Fix issue with past preference. * org-archive.el (org-archive-set-tag) (org-archive-subtree-default): New commands. * org-clock.el (org-clock-clocktable-default-properties): New option. (org-clock-report): Use `org-clock-clocktable-default-properties'. 2009-10-01 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-iswitchb-completing-read): Fix typo. * org-crypt.el: New file. * org.el: Add an entry for org-crypt. 2009-10-01 Carsten Dominik <carsten.dominik@gmail.com> * org-agenda.el (org-agenda-menu): Reorganize the menu for more consistency. (org-batch-store-agenda-views): New function. (org-agenda-title-append): Define variable. (org-write-agenda): New export to Org files. (org-agenda-get-some-entry-text): New arguments INDENT and KEEP. (org-agenda): Allow to keep the restricted file list if a special variable is bound to t. (org-agenda): Define a special agenda view for working on flagged entries. (org-agenda-get-restriction-and-command): List the new agenda view. (org-agenda-show-the-flagging-note): New command. (org-agenda-mode-map): New key `?' for looking at the flagging note. * org.el (org-autoload): Autoload org-mobile.el. (org-org-menu): Add menu commands for MobileOrg in the Org menu. * org-mobile.el: New file. * org-id.el (org-id-get): Fix bug with forcing ID on an item. 2009-10-01 Carsten Dominik <carsten.dominik@gmail.com> * org-table.el (orgtbl-line-start-regexp): Match also TBLNAME statements. (org-table-get-remote-range): Match indented #+TBLNAME statements. * org.el (org-convert-to-odd-levels) (org-convert-to-oddeven-levels): Work also correctly if the file is in outline-mode. 2009-10-01 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-store-link): When in agenda buffer, link to referenced entry. (org-add-planning-info): Remove spaces at eol. * org-macs.el (org-with-point-at): Add a `lisp-indent-function' property. 2009-10-01 Carsten Dominik <carsten.dominik@gmail.com> * org-latex.el (org-export-latex-first-lines): Fix problem with LaTeX export of first line and selected subtree. * org.el (org-shifttab): Interpret arg differently when using only odd levels. 2009-10-01 Bastien Guerry <bzg@altern.org> * org.el (org-check-agenda-file): Use a more explicit message 2009-10-01 Carsten Dominik <carsten.dominik@gmail.com> * org-exp.el (org-export-remove-special-table-lines): Don't remove normal lines. 2009-10-01 Bastien Guerry <bzg@altern.org> * org.el (org-offer-links-in-entry): Don't use "Select link" as a prompt in the temporary window. * org-agenda.el (org-agenda-bulk-mark): Use a slightly soberer prefix for marked entries in the agenda view. 2009-10-01 Andreas Burtzlaff <andy13@gmx.net> (tiny change) * org.el (outline-end-of-subtree): Bugfix: advise this function in a way that prevents any trailing character from being displayed. 2009-10-01 Carsten Dominik <carsten.dominik@gmail.com> * org-agenda.el (org-agenda-menu): Fix bugs in the bulk action menu. * org-exp.el (org-export-remove-special-table-lines): Remove bad slow regexp match. 2009-10-01 Carsten Dominik <carsten.dominik@gmail.com> * org-latex.el (org-export-latex-preprocess): Do not protect in the LaTeX header. * org-src.el (org-edit-src-save): Save window setup while saving. (org-edit-src-code): Use new buffer name construction scheme. 2009-10-01 Carsten Dominik <carsten.dominik@gmail.com> * org-agenda.el (org-agenda-entry-text-exclude-regexps): New variable. (org-agenda-entry-text-cleanup-hook): New hook. (org-agenda-get-some-entry-text): Remove matches of `org-agenda-entry-text-exclude-regexps' and run the hook `org-agenda-entry-text-cleanup-hook'. * org.el (org-offer-links-in-entry): New argument ZERO to implement a link with index zero. (org-cycle-show-empty-lines): Not keep empty line under header hidden. (org-iswitchb-completing-read): Bind `switchb-use-virtual-buffers' to nil for special completion. (org-store-link): Don't error before the first heading. * org-agenda.el (org-agenda-open-link): Pass the prefix to `org-offer-links-in-entry'. 2009-10-01 Carsten Dominik <carsten.dominik@gmail.com> * org-agenda.el (org-agenda-quit): Provide the window argument for `window-dedicated-p', Emacs 22 needs it. (org-format-agenda-item): If the category is a link, arrange for invisible text to replaced with spaces. (org-compile-prefix-format): Add the extra space. (org-prefix-category-length): New variable. * org-exp.el (org-export-cleanup-toc-line): Remove footnote references from TOC lines. * org.el (org-selected-window): New variable. * org-table.el (org-table-edit-formulas): Remember the selected window. (org-table-fedit-finish, org-table-fedit-abort): Select the window that was originally selected. * org-exp.el (org-export-preprocess-apply-macros): Scan the expansion of a macro for more macro definitions. * org-agenda.el (org-agenda-dim-blocked-tasks): Make sure the invisibility overlay starts on the newline.
636 lines
24 KiB
EmacsLisp
636 lines
24 KiB
EmacsLisp
;;; org-protocol.el --- Intercept calls from emacsclient to trigger custom actions.
|
|
;;
|
|
;; Copyright (C) 2008, 2009
|
|
;; Free Software Foundation, Inc.
|
|
;;
|
|
;; Author: Bastien Guerry <bzg AT altern DOT org>
|
|
;; Author: Daniel M German <dmg AT uvic DOT org>
|
|
;; Author: Sebastian Rose <sebastian_rose AT gmx DOT de>
|
|
;; Author: Ross Patterson <me AT rpatterson DOT net>
|
|
;; Maintainer: Sebastian Rose <sebastian_rose AT gmx DOT de>
|
|
;; Keywords: org, emacsclient, wp
|
|
;; Version: 6.31a
|
|
|
|
;; This file is part of GNU Emacs.
|
|
;;
|
|
;; GNU Emacs 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.
|
|
|
|
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;; Commentary:
|
|
;;
|
|
;; Intercept calls from emacsclient to trigger custom actions.
|
|
;;
|
|
;; This is done by advising `server-visit-files' to scann the list of filenames
|
|
;; for `org-protocol-the-protocol' and sub-procols defined in
|
|
;; `org-protocol-protocol-alist' and `org-protocol-protocol-alist-default'.
|
|
;;
|
|
;; Any application that supports calling external programs with an URL
|
|
;; as argument may be used with this functionality.
|
|
;;
|
|
;;
|
|
;; Usage:
|
|
;; ------
|
|
;;
|
|
;; 1.) Add this to your init file (.emacs probably):
|
|
;;
|
|
;; (add-to-list 'load-path "/path/to/org-protocol/")
|
|
;; (require 'org-protocol)
|
|
;;
|
|
;; 3.) Ensure emacs-server is up and running.
|
|
;; 4.) Try this from the command line (adjust the URL as needed):
|
|
;;
|
|
;; $ emacsclient \
|
|
;; org-protocol://store-link://http:%2F%2Flocalhost%2Findex.html/The%20title
|
|
;;
|
|
;; 5.) Optionally add custom sub-protocols and handlers:
|
|
;;
|
|
;; (setq org-protocol-protocol-alist
|
|
;; '(("my-protocol"
|
|
;; :protocol "my-protocol"
|
|
;; :function my-protocol-handler-fuction)))
|
|
;;
|
|
;; A "sub-protocol" will be found in URLs like this:
|
|
;;
|
|
;; org-protocol://sub-protocol://data
|
|
;;
|
|
;; If it works, you can now setup other applications for using this feature.
|
|
;;
|
|
;;
|
|
;; As of March 2009 Firefox users follow the steps documented on
|
|
;; http://kb.mozillazine.org/Register_protocol, Opera setup is described here:
|
|
;; http://www.opera.com/support/kb/view/535/
|
|
;;
|
|
;;
|
|
;; Documentation
|
|
;; -------------
|
|
;;
|
|
;; org-protocol.el comes with and installs handlers to open sources of published
|
|
;; online content, store and insert the browser's URLs and cite online content
|
|
;; by clicking on a bookmark in Firefox, Opera and probably other browsers and
|
|
;; applications:
|
|
;;
|
|
;; * `org-protocol-open-source' uses the sub-protocol \"open-source\" and maps
|
|
;; URLs to local filenames defined in `org-protocol-project-alist'.
|
|
;;
|
|
;; * `org-protocol-store-link' stores an Org-link (if Org-mode is present) and
|
|
;; pushes the browsers URL to the `kill-ring' for yanking. This handler is
|
|
;; triggered through the sub-protocol \"store-link\".
|
|
;;
|
|
;; * Call `org-protocol-remember' by using the sub-protocol \"remember\". If
|
|
;; Org-mode is loaded, emacs will popup a remember buffer and fill the
|
|
;; template with the data provided. I.e. the browser's URL is inserted as an
|
|
;; Org-link of which the page title will be the description part. If text
|
|
;; was select in the browser, that text will be the body of the entry.
|
|
;;
|
|
;; You may use the same bookmark URL for all those standard handlers and just
|
|
;; adjust the sub-protocol used:
|
|
;;
|
|
;; location.href='org-protocol://sub-protocol://'+
|
|
;; encodeURIComponent(location.href)+'/'+
|
|
;; encodeURIComponent(document.title)+'/'+
|
|
;; encodeURIComponent(window.getSelection())
|
|
;;
|
|
;; The handler for the sub-protocol \"remember\" detects an optional template
|
|
;; char that, if present, triggers the use of a special template.
|
|
;; Example:
|
|
;;
|
|
;; location.href='org-protocol://sub-protocol://x/'+ ...
|
|
;;
|
|
;; use template ?x.
|
|
;;
|
|
;; Note, that using double shlashes is optional from org-protocol.el's point of
|
|
;; view because emacsclient sqashes the slashes to one.
|
|
;;
|
|
;;
|
|
;; provides: 'org-protocol
|
|
;;
|
|
;;; Code:
|
|
|
|
(require 'org)
|
|
(eval-when-compile
|
|
(require 'cl))
|
|
|
|
(declare-function org-publish-initialize-files-alist "org-publish"
|
|
(&optional refresh))
|
|
(declare-function org-publish-get-project-from-filename "org-publish"
|
|
(filename &optional up))
|
|
(declare-function server-edit "server" (&optional arg))
|
|
|
|
|
|
(defgroup org-protocol nil
|
|
"Intercept calls from emacsclient to trigger custom actions.
|
|
|
|
This is done by advising `server-visit-files' to scann the list of filenames
|
|
for `org-protocol-the-protocol' and sub-procols defined in
|
|
`org-protocol-protocol-alist' and `org-protocol-protocol-alist-default'."
|
|
:version "22.1"
|
|
:group 'convenience
|
|
:group 'org)
|
|
|
|
|
|
;;; Variables:
|
|
|
|
(defconst org-protocol-protocol-alist-default
|
|
'(("org-remember" :protocol "remember" :function org-protocol-remember :kill-client t)
|
|
("org-store-link" :protocol "store-link" :function org-protocol-store-link)
|
|
("org-open-source" :protocol "open-source" :function org-protocol-open-source))
|
|
"Default protocols to use.
|
|
See `org-protocol-protocol-alist' for a description of this variable.")
|
|
|
|
|
|
(defconst org-protocol-the-protocol "org-protocol"
|
|
"This is the protocol to detect if org-protocol.el is loaded.
|
|
`org-protocol-protocol-alist-default' and `org-protocol-protocol-alist' hold the
|
|
sub-protocols that trigger the required action. You will have to define just one
|
|
protocol handler OS-wide (MS-Windows) or per application (Linux). That protocol
|
|
handler should call emacsclient.")
|
|
|
|
|
|
;;; User variables:
|
|
|
|
(defcustom org-protocol-reverse-list-of-files t
|
|
"* The filenames passed on the commandline are passed to the emacs-server in
|
|
reversed order. Set to `t' (default) to re-reverse the list, i.e. use the
|
|
sequence on the command line. If nil, the sequence of the filenames is
|
|
unchanged."
|
|
:group 'org-protocol
|
|
:type 'boolean)
|
|
|
|
|
|
(defcustom org-protocol-project-alist nil
|
|
"* Map URLs to local filenames for `org-protocol-open-source' (open-source).
|
|
|
|
Each element of this list must be of the form:
|
|
|
|
(module-name :property value property: value ...)
|
|
|
|
where module-name is an arbitrary name. All the values are strings.
|
|
|
|
Possible properties are:
|
|
|
|
:online-suffix - the suffix to strip from the published URLs
|
|
:working-suffix - the replacement for online-suffix
|
|
:base-url - the base URL, e.g. http://www.example.com/project/
|
|
Last slash required.
|
|
:working-directory - the local working directory. This is, what base-url will
|
|
be replaced with.
|
|
|
|
Example:
|
|
|
|
(setq org-protocol-project-alist
|
|
'((\"http://orgmode.org/worg/\"
|
|
:online-suffix \".php\"
|
|
:working-suffix \".org\"
|
|
:base-url \"http://orgmode.org/worg/\"
|
|
:working-directory \"/home/user/org/Worg/\")
|
|
(\"http://localhost/org-notes/\"
|
|
:online-suffix \".html\"
|
|
:working-suffix \".org\"
|
|
:base-url \"http://localhost/org/\"
|
|
:working-directory \"/home/user/org/\")))
|
|
|
|
Consider using the interactive functions `org-protocol-create' and
|
|
`org-protocol-create-for-org' to help you filling this variable with valid contents."
|
|
:group 'org-protocol
|
|
:type 'alist)
|
|
|
|
|
|
(defcustom org-protocol-protocol-alist nil
|
|
"* Register custom handlers for org-protocol.
|
|
|
|
Each element of this list must be of the form:
|
|
|
|
(module-name :protocol protocol :function func :kill-client nil)
|
|
|
|
protocol - protocol to detect in a filename without trailing colon and slashes.
|
|
See rfc1738 section 2.1 for more on this.
|
|
If you define a protocol \"my-protocol\", `org-protocol-check-filename-for-protocol'
|
|
will search filenames for \"org-protocol:/my-protocol:/\"
|
|
and trigger your action for every match. `org-protocol' is defined in
|
|
`org-protocol-the-protocol'. Double and tripple slashes are compressed
|
|
to one by emacsclient.
|
|
|
|
function - function that handles requests with protocol and takes exactly one
|
|
argument: the filename with all protocols stripped. If the function
|
|
returns nil, emacsclient and -server do nothing. Any non-nil return
|
|
value is considered a valid filename and thus passed to the server.
|
|
|
|
`org-protocol.el provides some support for handling those filenames,
|
|
if you stay with the conventions used for the standard handlers in
|
|
`org-protocol-protocol-alist-default'. See `org-protocol-split-data'.
|
|
|
|
kill-client - If t, kill the client immediately, once the sub-protocol is
|
|
detected. This is neccessary for actions that can be interupted by
|
|
`C-g' to avoid dangeling emacsclients. Note, that all other command
|
|
line arguments but the this one will be discarded, greedy handlers
|
|
still receive the whole list of arguments though.
|
|
|
|
Here is an example:
|
|
|
|
(setq org-protocol-protocol-alist
|
|
'((\"my-protocol\"
|
|
:protocol \"my-protocol\"
|
|
:function my-protocol-handler-fuction)
|
|
(\"your-protocol\"
|
|
:protocol \"your-protocol\"
|
|
:function your-protocol-handler-fuction)))"
|
|
:group 'org-protocol
|
|
:type '(alist))
|
|
|
|
(defcustom org-protocol-default-template-key "w"
|
|
"The default org-remember-templates key to use."
|
|
:group 'org-protocol
|
|
:type 'string)
|
|
|
|
|
|
;;; Helper functions:
|
|
|
|
(defun org-protocol-sanitize-uri (uri)
|
|
"emacsclient compresses double and tripple slashes.
|
|
Slashes are sanitized to double slashes here."
|
|
(when (string-match "^\\([a-z]+\\):/" uri)
|
|
(let* ((splitparts (split-string uri "/+")))
|
|
(setq uri (concat (car splitparts) "//" (mapconcat 'identity (cdr splitparts) "/")))))
|
|
uri)
|
|
|
|
|
|
(defun org-protocol-split-data(data &optional unhexify separator)
|
|
"Split, what a org-protocol handler function gets as only argument.
|
|
data is that one argument. Data is splitted at each occurrence of separator
|
|
(regexp). If no separator is specified or separator is nil, assume \"/+\".
|
|
The results of that splitting are return as a list. If unhexify is non-nil,
|
|
hex-decode each split part. If unhexify is a function, use that function to
|
|
decode each split part."
|
|
(let* ((sep (or separator "/+"))
|
|
(split-parts (split-string data sep)))
|
|
(if unhexify
|
|
(if (fboundp unhexify)
|
|
(mapcar unhexify split-parts)
|
|
(mapcar 'org-protocol-unhex-string split-parts))
|
|
split-parts)))
|
|
|
|
(defun org-protocol-unhex-string(str)
|
|
"Unhex hexified unicode strings as returned from the JavaScript function
|
|
encodeURIComponent. E.g. `%C3%B6' is the german Umlaut `ü'."
|
|
(setq str (or str ""))
|
|
(let ((tmp "")
|
|
(case-fold-search t))
|
|
(while (string-match "\\(%[0-9a-f][0-9a-f]\\)+" str)
|
|
(let* ((start (match-beginning 0))
|
|
(end (match-end 0))
|
|
(hex (match-string 0 str))
|
|
(replacement (org-protocol-unhex-compound hex)))
|
|
(setq tmp (concat tmp (substring str 0 start) replacement))
|
|
(setq str (substring str end))))
|
|
(setq tmp (concat tmp str))
|
|
tmp))
|
|
|
|
|
|
(defun org-protocol-unhex-compound (hex)
|
|
"Unhexify unicode hex-chars. E.g. `%C3%B6' is the german Umlaut `ü'."
|
|
(let* ((bytes (remove "" (split-string hex "%")))
|
|
(ret "")
|
|
(eat 0)
|
|
(sum 0))
|
|
(while bytes
|
|
(let* ((b (pop bytes))
|
|
(a (elt b 0))
|
|
(b (elt b 1))
|
|
(c1 (if (> a ?9) (+ 10 (- a ?A)) (- a ?0)))
|
|
(c2 (if (> b ?9) (+ 10 (- b ?A)) (- b ?0)))
|
|
(val (+ (lsh c1 4) c2))
|
|
(shift
|
|
(if (= 0 eat) ;; new byte
|
|
(if (>= val 252) 6
|
|
(if (>= val 248) 5
|
|
(if (>= val 240) 4
|
|
(if (>= val 224) 3
|
|
(if (>= val 192) 2 0)))))
|
|
6))
|
|
(xor
|
|
(if (= 0 eat) ;; new byte
|
|
(if (>= val 252) 252
|
|
(if (>= val 248) 248
|
|
(if (>= val 240) 240
|
|
(if (>= val 224) 224
|
|
(if (>= val 192) 192 0)))))
|
|
128)))
|
|
(if (>= val 192) (setq eat shift))
|
|
(setq val (logxor val xor))
|
|
(setq sum (+ (lsh sum shift) val))
|
|
(if (> eat 0) (setq eat (- eat 1)))
|
|
(when (= 0 eat)
|
|
(setq ret (concat ret (char-to-string sum)))
|
|
(setq sum 0))
|
|
)) ;; end (while bytes
|
|
ret ))
|
|
|
|
(defun org-protocol-flatten-greedy (param-list &optional strip-path replacement)
|
|
"Greedy handlers might recieve a list like this from emacsclient:
|
|
'( (\"/dir/org-protocol:/greedy:/~/path1\" (23 . 12)) (\"/dir/param\")
|
|
where \"/dir/\" is the absolute path to emacsclients working directory. This
|
|
function transforms it into a flat list utilizing `org-protocol-flatten' and
|
|
transforms the elements of that list as follows:
|
|
|
|
If strip-path is non-nil, remove the \"/dir/\" prefix from all members of
|
|
param-list.
|
|
|
|
If replacement is string, replace the \"/dir/\" prefix with it.
|
|
|
|
The first parameter, the one that contains the protocols, is always changed.
|
|
Everything up to the end of the protocols is stripped.
|
|
|
|
Note, that this function will always behave as if
|
|
`org-protocol-reverse-list-of-files' was set to t and the returned list will
|
|
reflect that. I.e. emacsclients first parameter will be the first one in the
|
|
returned list."
|
|
(let* ((l (org-protocol-flatten (if org-protocol-reverse-list-of-files
|
|
param-list
|
|
(reverse param-list))))
|
|
(trigger (car l))
|
|
(len 0)
|
|
dir
|
|
ret)
|
|
(when (string-match "^\\(.*\\)\\(org-protocol:/+[a-zA-z0-9][-_a-zA-z0-9]*:/+\\)\\(.*\\)" trigger)
|
|
(setq dir (match-string 1 trigger))
|
|
(setq len (length dir))
|
|
(setcar l (concat dir (match-string 3 trigger))))
|
|
(if strip-path
|
|
(progn
|
|
(dolist (e l ret)
|
|
(setq ret
|
|
(append ret
|
|
(list
|
|
(if (stringp e)
|
|
(if (stringp replacement)
|
|
(setq e (concat replacement (substring e len)))
|
|
(setq e (substring e len)))
|
|
e)))))
|
|
ret)
|
|
l)))
|
|
|
|
|
|
(defun org-protocol-flatten (l)
|
|
"Greedy handlers might recieve a list like this from emacsclient:
|
|
'( (\"/dir/org-protocol:/greedy:/~/path1\" (23 . 12)) (\"/dir/param\")
|
|
where \"/dir/\" is the absolute path to emacsclients working directory. This
|
|
function transforms it into a flat list."
|
|
(if (null l) ()
|
|
(if (listp l)
|
|
(append (org-protocol-flatten (car l)) (org-protocol-flatten (cdr l)))
|
|
(list l))))
|
|
|
|
;;; Standard protocol handlers:
|
|
|
|
(defun org-protocol-store-link (fname)
|
|
"Process an org-protocol://store-link:// style url
|
|
and store a browser URL as an org link. Also pushes the links URL to the
|
|
`kill-ring'.
|
|
|
|
The location for a browser's bookmark has to look like this:
|
|
|
|
javascript:location.href='org-protocol://store-link://'+ \\
|
|
encodeURIComponent(location.href)
|
|
encodeURIComponent(document.title)+'/'+ \\
|
|
|
|
Don't use `escape()'! Use `encodeURIComponent()' instead. The title of the page
|
|
could contain slashes and the location definitely will.
|
|
|
|
The sub-protocol used to reach this function is set in
|
|
`org-protocol-protocol-alist'."
|
|
(let* ((splitparts (org-protocol-split-data fname t))
|
|
(uri (org-protocol-sanitize-uri (car splitparts)))
|
|
(title (cadr splitparts))
|
|
orglink)
|
|
(if (boundp 'org-stored-links)
|
|
(setq org-stored-links (cons (list uri title) org-stored-links)))
|
|
(kill-new uri)
|
|
(message "`%s' to insert new org-link, `%s' to insert `%s'"
|
|
(substitute-command-keys"\\[org-insert-link]")
|
|
(substitute-command-keys"\\[yank]")
|
|
uri))
|
|
nil)
|
|
|
|
(defun org-protocol-remember (info)
|
|
"Process an org-protocol://remember:// style url.
|
|
|
|
The sub-protocol used to reach this function is set in
|
|
`org-protocol-protocol-alist'.
|
|
|
|
This function detects an URL, title and optinal text, separated by '/'
|
|
The location for a browser's bookmark has to look like this:
|
|
|
|
javascript:location.href='org-protocol://remember://'+ \\
|
|
encodeURIComponent(location.href)+'/' \\
|
|
encodeURIComponent(document.title)+'/'+ \\
|
|
encodeURIComponent(window.getSelection())
|
|
|
|
By default, it uses the character `org-protocol-default-template-key',
|
|
which should be associated with a template in `org-remember-templates'.
|
|
But you may prepend the encoded URL with a character and a slash like so:
|
|
|
|
javascript:location.href='org-protocol://org-store-link://b/'+ ...
|
|
|
|
Now template ?b will be used."
|
|
|
|
(if (and (boundp 'org-stored-links)
|
|
(fboundp 'org-remember))
|
|
(let* ((parts (org-protocol-split-data info t))
|
|
(template (or (and (= 1 (length (car parts))) (pop parts))
|
|
org-protocol-default-template-key))
|
|
(url (org-protocol-sanitize-uri (car parts)))
|
|
(type (if (string-match "^\\([a-z]+\\):" url)
|
|
(match-string 1 url)))
|
|
(title (cadr parts))
|
|
(region (caddr parts))
|
|
(orglink (org-make-link-string url title))
|
|
remember-annotation-functions)
|
|
(setq org-stored-links
|
|
(cons (list url title) org-stored-links))
|
|
(kill-new orglink)
|
|
(org-store-link-props :type type
|
|
:link url
|
|
:description title
|
|
:initial region)
|
|
(raise-frame)
|
|
(org-remember nil (string-to-char template)))
|
|
|
|
(message "Org-mode not loaded."))
|
|
nil)
|
|
|
|
(defun org-protocol-open-source (fname)
|
|
"Process an org-protocol://open-source:// style url.
|
|
|
|
Change a filename by mapping URLs to local filenames as set
|
|
in `org-protocol-project-alist'.
|
|
|
|
The location for a browser's bookmark should look like this:
|
|
|
|
javascript:location.href='org-protocol://open-source://'+ \\
|
|
encodeURIComponent(location.href)"
|
|
|
|
;; As we enter this function for a match on our protocol, the return value
|
|
;; defaults to nil.
|
|
(let ((result nil)
|
|
(f (org-protocol-unhex-string fname)))
|
|
(catch 'result
|
|
(dolist (prolist org-protocol-project-alist)
|
|
(let* ((base-url (plist-get (cdr prolist) :base-url))
|
|
(wsearch (regexp-quote base-url)))
|
|
|
|
(when (string-match wsearch f)
|
|
(let* ((wdir (plist-get (cdr prolist) :working-directory))
|
|
(strip-suffix (plist-get (cdr prolist) :online-suffix))
|
|
(add-suffix (plist-get (cdr prolist) :working-suffix))
|
|
(start-pos (+ (string-match wsearch f) (length base-url)))
|
|
(end-pos (string-match
|
|
(concat (regexp-quote strip-suffix) "\\([?#].*\\)?$") f))
|
|
(the-file (concat wdir (substring f start-pos end-pos) add-suffix)))
|
|
(if (file-readable-p the-file)
|
|
(throw 'result the-file))
|
|
(if (file-exists-p the-file)
|
|
(message "%s: permission denied!" the-file)
|
|
(message "%s: no such file or directory." the-file))))))
|
|
result)))
|
|
|
|
|
|
;;; Core functions:
|
|
|
|
(defun org-protocol-check-filename-for-protocol (fname restoffiles client)
|
|
"Detect if `org-protocol-the-protocol' and a known sub-protocol is used in fname.
|
|
Sub-protocols are registered in `org-protocol-protocol-alist' and
|
|
`org-protocol-protocol-alist-default'.
|
|
This is, how the matching is done:
|
|
|
|
(string-match \"protocol:/+sub-protocol:/+\" ...)
|
|
|
|
protocol and sub-protocol are regexp-quoted.
|
|
|
|
If a matching protcol is found, the protcol is stripped from fname and the
|
|
result is passed to the protocols function as the only parameter. If the
|
|
function returns nil, the filename is removed from the list of filenames
|
|
passed from emacsclient to the server.
|
|
If the function returns a non nil value, that value is passed to the server
|
|
as filename."
|
|
(let ((sub-protocols (append org-protocol-protocol-alist org-protocol-protocol-alist-default)))
|
|
(catch 'fname
|
|
(let ((the-protocol (concat (regexp-quote org-protocol-the-protocol) ":/+")))
|
|
(when (string-match the-protocol fname)
|
|
(dolist (prolist sub-protocols)
|
|
(let ((proto (concat the-protocol (regexp-quote (plist-get (cdr prolist) :protocol)) ":/+")))
|
|
(when (string-match proto fname)
|
|
(let* ((func (plist-get (cdr prolist) :function))
|
|
(greedy (plist-get (cdr prolist) :greedy))
|
|
(splitted (split-string fname proto))
|
|
(result (if greedy restoffiles (cadr splitted))))
|
|
(when (plist-get (cdr prolist) :kill-client)
|
|
(message "Greedy org-protocol handler. Killing client.")
|
|
(server-edit))
|
|
(when (fboundp func)
|
|
(unless greedy
|
|
(throw 'fname (funcall func result)))
|
|
(funcall func result)
|
|
(throw 'fname t))))))))
|
|
;; (message "fname: %s" fname)
|
|
fname)))
|
|
|
|
|
|
(defadvice server-visit-files (before org-protocol-detect-protocol-server activate)
|
|
"Advice server-visit-flist to call `org-protocol-modify-filename-for-protocol'."
|
|
(let ((flist (if org-protocol-reverse-list-of-files
|
|
(reverse (ad-get-arg 0))
|
|
(ad-get-arg 0)))
|
|
(client (ad-get-arg 1)))
|
|
(catch 'greedy
|
|
(dolist (var flist)
|
|
(let ((fname (expand-file-name (car var)))) ;; `\' to `/' on windows. FIXME: could this be done any better?
|
|
(setq fname (org-protocol-check-filename-for-protocol fname (member var flist) client))
|
|
(if (eq fname t) ;; greedy? We need the `t' return value.
|
|
(progn
|
|
(ad-set-arg 0 nil)
|
|
(throw 'greedy t))
|
|
(if (stringp fname) ;; probably filename
|
|
(setcar var fname)
|
|
(ad-set-arg 0 (delq var (ad-get-arg 0))))))
|
|
))))
|
|
|
|
;;; Org specific functions:
|
|
|
|
(defun org-protocol-create-for-org ()
|
|
"Create a org-protocol project for the current file's Org-mode project.
|
|
This works, if the file visited is part of a publishing project in
|
|
`org-publish-project-alist'. This functions calls `org-protocol-create' to do
|
|
most of the work."
|
|
(interactive)
|
|
(require 'org-publish)
|
|
(org-publish-initialize-files-alist)
|
|
(let ((all (or (org-publish-get-project-from-filename buffer-file-name))))
|
|
(if all (org-protocol-create (cdr all))
|
|
(message "Not in an org-project. Did mean %s?"
|
|
(substitute-command-keys"\\[org-protocol-create]")))))
|
|
|
|
|
|
(defun org-protocol-create(&optional project-plist)
|
|
"Create a new org-protocol project interactively.
|
|
An org-protocol project is an entry in `org-protocol-project-alist'
|
|
which is used by `org-protocol-open-source'.
|
|
Optionally use project-plist to initialize the defaults for this worglet. If
|
|
project-plist is the CDR of an element in `org-publish-project-alist', reuse
|
|
:base-directory, :html-extension and :base-extension."
|
|
(interactive)
|
|
(let ((working-dir (expand-file-name(or (plist-get project-plist :base-directory) default-directory)))
|
|
(base-url "http://orgmode.org/worg/")
|
|
(strip-suffix (or (plist-get project-plist :html-extension) ".html"))
|
|
(working-suffix (if (plist-get project-plist :base-extension)
|
|
(concat "." (plist-get project-plist :base-extension))
|
|
".org"))
|
|
|
|
(worglet-buffer nil)
|
|
|
|
(insert-default-directory t)
|
|
(minibuffer-allow-text-properties nil))
|
|
|
|
(setq base-url (read-string "Base URL of published content: " base-url nil base-url t))
|
|
(if (not (string-match "\\/$" base-url))
|
|
(setq base-url (concat base-url "/")))
|
|
|
|
(setq working-dir
|
|
(expand-file-name
|
|
(read-directory-name "Local working directory: " working-dir working-dir t)))
|
|
(if (not (string-match "\\/$" working-dir))
|
|
(setq working-dir (concat working-dir "/")))
|
|
|
|
(setq strip-suffix
|
|
(read-string
|
|
(concat "Extension to strip from published URLs ("strip-suffix"): ")
|
|
strip-suffix nil strip-suffix t))
|
|
|
|
(setq working-suffix
|
|
(read-string
|
|
(concat "Extension of editable files ("working-suffix"): ")
|
|
working-suffix nil working-suffix t))
|
|
|
|
(when (yes-or-no-p "Save the new worglet to your init file? ")
|
|
(setq org-protocol-project-alist
|
|
(cons `(,base-url . (:base-url ,base-url
|
|
:working-directory ,working-dir
|
|
:online-suffix ,strip-suffix
|
|
:working-suffix ,working-suffix))
|
|
org-protocol-project-alist))
|
|
(customize-save-variable 'org-protocol-project-alist org-protocol-project-alist))))
|
|
|
|
(provide 'org-protocol)
|
|
|
|
;; arch-tag: b5c5c2ac-77cf-4a94-a649-2163dff95846
|
|
;;; org-protocol.el ends here
|