2012-05-07 14:41:15 +02:00
|
|
|
|
;;; ein-utils.el --- Utility module
|
|
|
|
|
|
|
|
|
|
;; Copyright (C) 2012- Takafumi Arakaki
|
|
|
|
|
|
|
|
|
|
;; Author: Takafumi Arakaki
|
|
|
|
|
|
|
|
|
|
;; This file is NOT part of GNU Emacs.
|
|
|
|
|
|
|
|
|
|
;; ein-utils.el 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.
|
|
|
|
|
|
|
|
|
|
;; ein-utils.el 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 ein-utils.el. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
;;; Commentary:
|
|
|
|
|
|
|
|
|
|
;;
|
|
|
|
|
|
|
|
|
|
;;; Code:
|
|
|
|
|
|
|
|
|
|
(eval-when-compile (require 'cl))
|
|
|
|
|
(require 'json)
|
|
|
|
|
|
2012-05-19 14:34:00 +02:00
|
|
|
|
(defgroup ein nil
|
|
|
|
|
"IPython notebook client in Emacs"
|
|
|
|
|
:group 'applications
|
|
|
|
|
:prefix "ein:")
|
|
|
|
|
|
|
|
|
|
(defcustom ein:url-or-port '(8888)
|
|
|
|
|
"List of default url-or-port values.
|
|
|
|
|
This will be used for completion. So put your IPython servers.
|
|
|
|
|
You can connect to servers not in this list \(but you will need
|
|
|
|
|
to type every time)."
|
|
|
|
|
:type '(repeat (choice (integer :tag "Port number" 8888)
|
|
|
|
|
(string :tag "URL" "http://127.0.0.1:8888")))
|
|
|
|
|
:group 'ein)
|
2012-05-07 14:41:15 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(defmacro ein:aif (test-form then-form &rest else-forms)
|
2012-05-17 21:26:55 +02:00
|
|
|
|
"Anaphoric IF. Adapted from `e2wm:aif'."
|
2012-05-07 14:41:15 +02:00
|
|
|
|
(declare (debug (form form &rest form)))
|
|
|
|
|
`(let ((it ,test-form))
|
|
|
|
|
(if it ,then-form ,@else-forms)))
|
|
|
|
|
(put 'ein:aif 'lisp-indent-function 2)
|
|
|
|
|
|
|
|
|
|
(defmacro ein:aand (test &rest rest)
|
2012-05-17 21:26:55 +02:00
|
|
|
|
"Anaphoric AND. Adapted from `e2wm:aand'."
|
2012-05-07 14:41:15 +02:00
|
|
|
|
(declare (debug (form &rest form)))
|
|
|
|
|
`(let ((it ,test))
|
|
|
|
|
(if it ,(if rest (macroexpand-all `(ein:aand ,@rest)) 'it))))
|
|
|
|
|
|
|
|
|
|
|
2012-05-12 22:55:06 +02:00
|
|
|
|
(defmacro ein:deflocal (name &optional initvalue docstring)
|
|
|
|
|
"Define permanent buffer local variable named NAME.
|
|
|
|
|
INITVALUE and DOCSTRING are passed to `defvar'."
|
2012-05-13 06:04:08 +02:00
|
|
|
|
(declare (indent defun)
|
|
|
|
|
(doc-string 3))
|
2012-05-12 22:55:06 +02:00
|
|
|
|
`(progn
|
|
|
|
|
(defvar ,name ,initvalue ,docstring)
|
|
|
|
|
(make-variable-buffer-local ',name)
|
|
|
|
|
(put ',name 'permanent-local t)))
|
|
|
|
|
|
2012-05-14 03:06:41 +02:00
|
|
|
|
(defmacro ein:with-read-only-buffer (buffer &rest body)
|
|
|
|
|
(declare (indent 1))
|
|
|
|
|
`(with-current-buffer ,buffer
|
|
|
|
|
(setq buffer-read-only t)
|
|
|
|
|
(save-excursion
|
|
|
|
|
(let ((inhibit-read-only t))
|
|
|
|
|
,@body))))
|
|
|
|
|
|
2012-05-16 04:15:10 +02:00
|
|
|
|
(defvar ein:dotty-syntax-table
|
|
|
|
|
(let ((table (make-syntax-table c-mode-syntax-table)))
|
|
|
|
|
(modify-syntax-entry ?. "w" table)
|
|
|
|
|
(modify-syntax-entry ?_ "w" table)
|
|
|
|
|
table)
|
|
|
|
|
"Adapted from `python-dotty-syntax-table'.")
|
|
|
|
|
|
2012-05-16 05:04:30 +02:00
|
|
|
|
(defun ein:object-at-point ()
|
|
|
|
|
"Return dotty.words.at.point, just before previous opening parenthesis."
|
|
|
|
|
(save-excursion
|
|
|
|
|
(unless (looking-at "(")
|
|
|
|
|
(search-backward "(" (point-at-bol) t))
|
|
|
|
|
(with-syntax-table ein:dotty-syntax-table
|
|
|
|
|
(thing-at-point 'word))))
|
|
|
|
|
|
2012-05-13 02:51:47 +02:00
|
|
|
|
|
|
|
|
|
;;; URL utils
|
|
|
|
|
|
|
|
|
|
(defvar ein:url-localhost-template "http://127.0.0.1:%s")
|
|
|
|
|
|
|
|
|
|
(defun ein:url (url-or-port &rest paths)
|
|
|
|
|
(loop with url = (if (integerp url-or-port)
|
|
|
|
|
(format ein:url-localhost-template url-or-port)
|
|
|
|
|
url-or-port)
|
|
|
|
|
for p in paths
|
|
|
|
|
do (setq url (concat (ein:trim-right url "/")
|
|
|
|
|
"/"
|
|
|
|
|
(ein:trim-left p "/")))
|
|
|
|
|
finally return url))
|
|
|
|
|
|
2012-05-13 06:51:26 +02:00
|
|
|
|
(defun ein:url-no-cache (url)
|
|
|
|
|
"Imitate `cache=false' of `jQuery.ajax'.
|
|
|
|
|
See: http://api.jquery.com/jQuery.ajax/"
|
|
|
|
|
(concat url (format-time-string "?_=%s")))
|
|
|
|
|
|
2012-05-13 02:51:47 +02:00
|
|
|
|
|
|
|
|
|
;;; JSON utils
|
2012-05-12 22:55:06 +02:00
|
|
|
|
|
2012-05-07 14:41:15 +02:00
|
|
|
|
(defmacro ein:with-json-setting (&rest body)
|
|
|
|
|
`(let ((json-object-type 'plist)
|
|
|
|
|
(json-array-type 'list))
|
|
|
|
|
,@body))
|
|
|
|
|
|
|
|
|
|
(defun ein:json-read ()
|
|
|
|
|
"Read json from `url-retrieve'-ed buffer.
|
|
|
|
|
|
|
|
|
|
* `json-object-type' is `plist'. This is mainly for readability.
|
|
|
|
|
* `json-array-type' is `list'. Notebook data is edited locally thus
|
|
|
|
|
data type must be edit-friendly. `vector' type is not."
|
|
|
|
|
(goto-char (point-max))
|
|
|
|
|
(backward-sexp)
|
|
|
|
|
(ein:with-json-setting
|
|
|
|
|
(json-read)))
|
|
|
|
|
|
|
|
|
|
(defun ein:json-read-from-string (string)
|
|
|
|
|
(ein:with-json-setting
|
|
|
|
|
(json-read-from-string string)))
|
|
|
|
|
|
2012-05-17 14:06:01 +02:00
|
|
|
|
|
|
|
|
|
;;;
|
2012-05-07 14:41:15 +02:00
|
|
|
|
|
2012-05-20 22:19:00 +02:00
|
|
|
|
(defun ein:propertize-read-only (string &rest properties)
|
|
|
|
|
(apply #'propertize string 'read-only t 'front-sticky t properties))
|
2012-05-07 14:41:15 +02:00
|
|
|
|
|
2012-05-20 22:19:00 +02:00
|
|
|
|
(defun ein:insert-read-only (string &rest properties)
|
|
|
|
|
(insert (apply #'ein:propertize-read-only string properties)))
|
2012-05-07 14:41:15 +02:00
|
|
|
|
|
2012-05-17 14:06:01 +02:00
|
|
|
|
|
|
|
|
|
;;; String manipulation
|
2012-05-07 14:41:15 +02:00
|
|
|
|
|
2012-05-13 02:51:47 +02:00
|
|
|
|
(defun ein:trim (string &optional regexp)
|
|
|
|
|
(ein:trim-left (ein:trim-right string regexp) regexp))
|
|
|
|
|
|
|
|
|
|
(defun ein:trim-left (string &optional regexp)
|
|
|
|
|
(unless regexp (setq regexp "\\s-\\|\n"))
|
|
|
|
|
(ein:trim-regexp string (format "^\\(%s\\)+" regexp)))
|
|
|
|
|
|
|
|
|
|
(defun ein:trim-right (string &optional regexp)
|
|
|
|
|
(unless regexp (setq regexp "\\s-\\|\n"))
|
|
|
|
|
(ein:trim-regexp string (format "\\(%s\\)+$" regexp)))
|
|
|
|
|
|
|
|
|
|
(defun ein:trim-regexp (string regexp)
|
|
|
|
|
(if (string-match regexp string)
|
|
|
|
|
(replace-match "" t t string)
|
|
|
|
|
string))
|
2012-05-07 14:41:15 +02:00
|
|
|
|
|
2012-05-17 14:03:45 +02:00
|
|
|
|
(defun ein:join-str (sep strings)
|
|
|
|
|
(mapconcat 'identity strings sep))
|
|
|
|
|
|
2012-05-17 20:12:40 +02:00
|
|
|
|
(defun ein:join-path (paths)
|
|
|
|
|
(mapconcat 'file-name-as-directory paths ""))
|
|
|
|
|
|
2012-05-07 14:41:15 +02:00
|
|
|
|
(defmacro ein:case-equal (str &rest clauses)
|
|
|
|
|
"Similar to `case' but comparison is done by `equal'.
|
|
|
|
|
Adapted from twittering-mode.el's `case-string'."
|
|
|
|
|
(declare (indent 1))
|
|
|
|
|
`(cond
|
|
|
|
|
,@(mapcar
|
|
|
|
|
(lambda (clause)
|
|
|
|
|
(let ((keylist (car clause))
|
|
|
|
|
(body (cdr clause)))
|
|
|
|
|
`(,(if (listp keylist)
|
|
|
|
|
`(or ,@(mapcar (lambda (key) `(equal ,str ,key))
|
|
|
|
|
keylist))
|
|
|
|
|
't)
|
|
|
|
|
,@body)))
|
|
|
|
|
clauses)))
|
|
|
|
|
|
2012-05-17 14:06:01 +02:00
|
|
|
|
|
|
|
|
|
;;
|
|
|
|
|
|
2012-05-07 14:41:15 +02:00
|
|
|
|
(defun ein:plist-iter (plist)
|
2012-05-12 22:57:49 +02:00
|
|
|
|
"Return list of (key . value) in PLIST."
|
2012-05-07 14:41:15 +02:00
|
|
|
|
(loop for p in plist
|
|
|
|
|
for i from 0
|
|
|
|
|
for key-p = (= (% i 2) 0)
|
|
|
|
|
with key = nil
|
|
|
|
|
if key-p do (setq key p)
|
|
|
|
|
else collect `(,key . ,p)))
|
|
|
|
|
|
2012-05-16 19:15:48 +02:00
|
|
|
|
(defun ein:get-value (obj)
|
|
|
|
|
"Get value from obj if it is a variable or function."
|
|
|
|
|
(cond
|
|
|
|
|
((not (symbolp obj)) obj)
|
|
|
|
|
((boundp obj) (eval obj))
|
|
|
|
|
((fboundp obj) (funcall obj))))
|
|
|
|
|
|
2012-05-14 21:03:23 +02:00
|
|
|
|
(defun ein:choose-setting (symbol value)
|
|
|
|
|
"Choose setting in stored in SYMBOL based on VALUE.
|
|
|
|
|
The value of SYMBOL can be string, alist or function."
|
|
|
|
|
(let ((setting (eval symbol)))
|
|
|
|
|
(cond
|
|
|
|
|
((stringp setting) setting)
|
|
|
|
|
((functionp setting) (funcall setting value))
|
|
|
|
|
((listp setting)
|
2012-05-16 19:15:48 +02:00
|
|
|
|
(ein:get-value (or (assoc-default value setting)
|
|
|
|
|
(assoc-default 'default setting))))
|
2012-05-14 21:03:23 +02:00
|
|
|
|
(t (error "Unsupported type of `%s': %s" symbol (type-of setting))))))
|
|
|
|
|
|
2012-05-10 00:26:47 +02:00
|
|
|
|
(defmacro ein:setf-default (place val)
|
|
|
|
|
"Set VAL to PLACE using `setf' if the value of PLACE is `nil'."
|
|
|
|
|
`(unless ,place
|
|
|
|
|
(setf ,place ,val)))
|
|
|
|
|
|
2012-05-15 23:04:18 +02:00
|
|
|
|
(defun ein:eval-if-bound (symbol)
|
|
|
|
|
(if (boundp symbol) (eval symbol)))
|
|
|
|
|
|
2012-05-07 14:41:15 +02:00
|
|
|
|
(defun ein:remove-by-index (list indices)
|
|
|
|
|
"Remove elements from LIST if its index is in INDICES.
|
|
|
|
|
NOTE: This function creates new list."
|
|
|
|
|
(loop for l in list
|
|
|
|
|
for i from 0
|
|
|
|
|
when (not (memq i indices))
|
|
|
|
|
collect l))
|
|
|
|
|
|
2012-05-17 14:06:01 +02:00
|
|
|
|
|
|
|
|
|
;; utils.js compatible
|
|
|
|
|
|
2012-05-07 14:41:15 +02:00
|
|
|
|
(defun ein:utils-uuid ()
|
2012-05-12 22:22:23 +02:00
|
|
|
|
"Return string with random (version 4) UUID.
|
|
|
|
|
Adapted from org-mode's `org-id-uuid'."
|
|
|
|
|
(let ((rnd (md5 (format "%s%s%s%s%s%s%s"
|
|
|
|
|
(random t)
|
|
|
|
|
(current-time)
|
|
|
|
|
(user-uid)
|
|
|
|
|
(emacs-pid)
|
|
|
|
|
(user-full-name)
|
|
|
|
|
user-mail-address
|
|
|
|
|
(recent-keys)))))
|
|
|
|
|
(format "%s-%s-4%s-%s%s-%s"
|
|
|
|
|
(substring rnd 0 8)
|
|
|
|
|
(substring rnd 8 12)
|
|
|
|
|
(substring rnd 13 16)
|
|
|
|
|
(format "%x"
|
|
|
|
|
(logior
|
|
|
|
|
#b10000000
|
|
|
|
|
(logand
|
|
|
|
|
#b10111111
|
|
|
|
|
(string-to-number
|
|
|
|
|
(substring rnd 16 18) 16))))
|
|
|
|
|
(substring rnd 18 20)
|
|
|
|
|
(substring rnd 20 32))))
|
|
|
|
|
|
2012-05-07 14:41:15 +02:00
|
|
|
|
|
|
|
|
|
(provide 'ein-utils)
|
|
|
|
|
|
|
|
|
|
;;; ein-utils.el ends here
|