From f98f7ab55266f6f3184a4b47783210ef4e68f8dc Mon Sep 17 00:00:00 2001 From: fuxialexander Date: Mon, 28 May 2018 13:56:56 +0800 Subject: [PATCH 1/4] Add: org: +ipython Add: packages --- modules/lang/org/+ipython.el | 46 +++++++ modules/lang/org/autoload/ipython.el | 194 +++++++++++++++++++++++++++ modules/lang/org/packages.el | 4 +- 3 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 modules/lang/org/+ipython.el create mode 100644 modules/lang/org/autoload/ipython.el diff --git a/modules/lang/org/+ipython.el b/modules/lang/org/+ipython.el new file mode 100644 index 000000000..82d8a22e6 --- /dev/null +++ b/modules/lang/org/+ipython.el @@ -0,0 +1,46 @@ +;;; lang/org/+ipython.el -*- lexical-binding: t; -*- +(defvar +org-ob-ipython-resources-dir ".ob-ipython-resrc") +(defvar +ob-ipython-local-runtime-dir + (substring (shell-command-to-string (concat "jupyter --runtime-dir")) 0 -1)) +(def-package! ob-ipython + :when (featurep! +ipython) + :after (ob) + :config + (setq ob-ipython-resources-dir +org-ob-ipython-resources-dir) + ;; popup + (set! + :popup "^\\*Org Src" + '((size . 100) + (side . right) + (slot . -1) + (window-height . 0.6)) + '((quit) + (select . t) + (modeline))) + (set! + :popup "^\\*Python" + '((slot . 0) + (side . right) + (size . 100)) + '((select) (quit) (transient))) + (set! + :popup "\\*ob-ipython.*" + '((slot . 2) + (side . right) + (size . 100) + (window-height . 0.2)) + '((select) (quit) (transient))) + (set! + :popup "\\*Python:.*" + '((slot . 0) + (side . right) + (size . 100)) + '((select) (quit) (transient))) + ;; advices for remote kernel and org-src-edit + (advice-add 'org-babel-edit-prep:ipython :override #'+org*org-babel-edit-prep:ipython) + (advice-add 'org-babel-ipython-initiate-session :override #'+org*org-babel-ipython-initiate-session) + (advice-add 'ob-ipython--create-repl :override #'+org*ob-ipython--create-repl) + (advice-add 'org-babel-execute:ipython :override #'+org*org-babel-execute:ipython) + ;; retina resolution image hack + (when (eq window-system 'ns) + (advice-add 'ob-ipython--write-base64-string :around #'+org*ob-ipython--write-base64-string))) diff --git a/modules/lang/org/autoload/ipython.el b/modules/lang/org/autoload/ipython.el new file mode 100644 index 000000000..f5fa6eb57 --- /dev/null +++ b/modules/lang/org/autoload/ipython.el @@ -0,0 +1,194 @@ +;;; lang/org/autoload/ipython.el -*- lexical-binding: t; -*- +;; * remote +;;;###autoload +(defun +org*org-babel-ipython-initiate-session (&optional session params) + "Create a session named SESSION according to PARAMS." + (if (string= session "none") + (error + "ob-ipython currently only supports evaluation using a session. +Make sure your src block has a :session param.") + (when (not (s-ends-with-p ".json" session)) + (ob-ipython--create-kernel + (ob-ipython--normalize-session + session) + (cdr (assoc :kernel params)))) + (ob-ipython--create-repl + (ob-ipython--normalize-session + session) + params))) + +;;;###autoload +(defun +org*ob-ipython--create-repl (name &optional params) + "Create repl based on NAME and PARAMS. +If PARAMS specifies remote kernel, copy the kernel config from remote server and +create a repl connecting to remote session." + (let ((cmd (s-join + " " + (ob-ipython--kernel-repl-cmd + name)))) + (if (string= "default" name) + (progn + (run-python cmd nil nil) + (format + "*%s*" + python-shell-buffer-name)) + (if (string-match + "^remote-.*ssh.json" + name) + (when (not (ignore-errors + (process-live-p + (get-process + (format + "Python:ob-ipython-%s" + name))))) + (let* ((remote (s-split "-" name)) + (remote-host (nth 1 remote)) + (remote-session (nth 3 remote))) + (+org/ob-ipython-generate-local-path-from-remote + remote-session + remote-host + params))) + (let* ((process-name (format + "Python:ob-ipython-%s" + name)) + (python-shell-prompt-detect-enabled nil) + (python-shell-completion-native-enable nil) + (buf (python-shell-make-comint + cmd + process-name + t)) + (dir (cdr (assoc :pydir params)))) + (if dir + (with-current-buffer + buf + (setq-local + default-directory + dir))) + (sleep-for 1) + (format "*%s*" process-name)))))) + +;;;###autoload +(defun +org*org-babel-execute:ipython (body params) + "Execute a BODY of IPython code with PARAMS in org-babel. +This function is called by `org-babel-execute-src-block'." + (message default-directory) + (let ((session (cdr (assoc :session params)))) + (org-babel-ipython-initiate-session + session + params)) + (ob-ipython--clear-output-buffer) + (if (cdr (assoc :async params)) + (ob-ipython--execute-async + body + params) + (ob-ipython--execute-sync + body + params))) + +;;;###autoload +(defun +org/ob-ipython-generate-local-path-from-remote (session host params) + "Given a remote SESSION with PARAMS and corresponding HOST, copy remote config to local, start a jupyter console to generate a new one." + (let* ((runtime-dir + (substring (shell-command-to-string (concat "ssh " host " jupyter --runtime-dir")) 0 -1)) + (runtime-file (concat runtime-dir "/" "kernel-" session ".json")) + (tramp-path (concat "/ssh:" host ":" runtime-file)) + (tramp-copy (concat +ob-ipython-local-runtime-dir "/remote-" host "-kernel-" session ".json")) + (local-path + (concat + "Python:ob-ipython-" + (file-name-sans-extension (file-name-nondirectory tramp-copy)) + "-ssh.json"))) + ;; scp remote file to local + (copy-file tramp-path tramp-copy t) + ;; connect to remote use new config + (let* ((python-shell-interpreter-interactive-arg " console --simple-prompt") + (python-shell-prompt-detect-enabled nil) + (python-shell-completion-native-enable nil) + (buf (python-shell-make-comint + (concat + ob-ipython-command + " console --simple-prompt --existing " + tramp-copy + " --ssh " + host) + (concat "" local-path) + t)) + (proc (get-buffer-process buf)) + (dir (cdr (assoc :pydir params)))) + (sleep-for 3) + (if dir + (with-current-buffer + buf + (setq-local default-directory dir))) + (format "*%s*" proc)))) + +;; * org-src-edit +;;;###autoload +(defun +org*org-babel-edit-prep:ipython (info) + (let* ((params (nth 2 info)) + (session (cdr (assoc :session params)))) + (org-babel-ipython-initiate-session + session + params)) + ;; Support for python.el's "send-code" commands within edit buffers. + (setq-local + python-shell-buffer-name + (format + "Python:ob-ipython-%s" + (->> + info + (nth 2) + (assoc :session) + cdr ob-ipython--normalize-session))) + (setq-local + default-directory + (format + "%s" + (->> + info + (nth 2) + (assoc :pydir) + cdr ob-ipython--normalize-session))) + (ob-ipython-mode 1) + ;; hack on company mode to use company-capf rather than company-anaconda + (when (featurep! :completion company) + (setq-local + company-backends + '(company-capf + company-dabbrev + company-files + company-yasnippet)) + (setq-local + company-idle-delay + nil)) + (when (featurep 'lpy) + (setq lispy-python-proc + (format + "Python:ob-ipython-%s" + (->> + info + (nth 2) + (assoc :session) + cdr ob-ipython--normalize-session))) + (setq lispy--python-middleware-loaded-p + nil) + (lispy--python-middleware-load))) + +;; * retina +;;;###autoload +(defun +org/ob-ipython-mac-2x-image-file-name (filename &optional scale) + "Return the name of high-resolution image file for FILENAME. +The optional arg SCALE is scale factor, and defaults to 2." + (let ((pos (or (string-match "\\.[^./]*\\'" filename) (length filename)))) + (format + "%s@%dx%s" + (substring filename 0 pos) + (or scale 2) + (substring filename pos)))) +;;;###autoload +(defun +org*ob-ipython--write-base64-string (oldfunc &rest args) + (let ((file (car args)) + (b64-string (cdr args))) + (let ((file2x (+org/ob-ipython-mac-2x-image-file-name file))) + (apply oldfunc file2x b64-string) + (shell-command (concat "convert " file2x " -resize 50% " file))))) diff --git a/modules/lang/org/packages.el b/modules/lang/org/packages.el index 91c73748e..da17aa643 100644 --- a/modules/lang/org/packages.el +++ b/modules/lang/org/packages.el @@ -29,7 +29,9 @@ (when (featurep! :lang restclient) (package! ob-restclient)) (when (featurep! :lang crystal) - (package! ob-crystal))) + (package! ob-crystal)) + (when (featurep! +ipython) + (package! ob-ipython))) (when (featurep! +export) (package! ox-pandoc) From 21631aa8bf7f9e222a9b3bcc486542f2731db0d2 Mon Sep 17 00:00:00 2001 From: fuxialexander Date: Mon, 28 May 2018 14:09:03 +0800 Subject: [PATCH 2/4] Add: org: +ipython `+right-popup` option --- modules/lang/org/+ipython.el | 57 ++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/modules/lang/org/+ipython.el b/modules/lang/org/+ipython.el index 82d8a22e6..8c483987c 100644 --- a/modules/lang/org/+ipython.el +++ b/modules/lang/org/+ipython.el @@ -8,34 +8,35 @@ :config (setq ob-ipython-resources-dir +org-ob-ipython-resources-dir) ;; popup - (set! - :popup "^\\*Org Src" - '((size . 100) - (side . right) - (slot . -1) - (window-height . 0.6)) - '((quit) - (select . t) - (modeline))) - (set! - :popup "^\\*Python" - '((slot . 0) - (side . right) - (size . 100)) - '((select) (quit) (transient))) - (set! - :popup "\\*ob-ipython.*" - '((slot . 2) - (side . right) - (size . 100) - (window-height . 0.2)) - '((select) (quit) (transient))) - (set! - :popup "\\*Python:.*" - '((slot . 0) - (side . right) - (size . 100)) - '((select) (quit) (transient))) + (when (featurep! +right-popup) + (set! + :popup "^\\*Org Src" + '((size . 100) + (side . right) + (slot . -1) + (window-height . 0.6)) + '((quit) + (select . t) + (modeline))) + (set! + :popup "^\\*Python" + '((slot . 0) + (side . right) + (size . 100)) + '((select) (quit) (transient))) + (set! + :popup "\\*ob-ipython.*" + '((slot . 2) + (side . right) + (size . 100) + (window-height . 0.2)) + '((select) (quit) (transient))) + (set! + :popup "\\*Python:.*" + '((slot . 0) + (side . right) + (size . 100)) + '((select) (quit) (transient)))) ;; advices for remote kernel and org-src-edit (advice-add 'org-babel-edit-prep:ipython :override #'+org*org-babel-edit-prep:ipython) (advice-add 'org-babel-ipython-initiate-session :override #'+org*org-babel-ipython-initiate-session) From d1812e040f56a7c6e6fc1878f4d4a4aadc1965e1 Mon Sep 17 00:00:00 2001 From: fuxialexander Date: Tue, 29 May 2018 18:29:42 +0800 Subject: [PATCH 3/4] Fix: packages.el: ob-ipython repo --- modules/lang/org/packages.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/lang/org/packages.el b/modules/lang/org/packages.el index da17aa643..b09375c88 100644 --- a/modules/lang/org/packages.el +++ b/modules/lang/org/packages.el @@ -31,7 +31,7 @@ (when (featurep! :lang crystal) (package! ob-crystal)) (when (featurep! +ipython) - (package! ob-ipython))) + (package! ob-ipython :recipe (:fetcher github :repo "fuxialexander/ob-ipython" :files ("*"))))) (when (featurep! +export) (package! ox-pandoc) From 734b4643736adfe481ae82d5b0b6039b41db4e7f Mon Sep 17 00:00:00 2001 From: fuxialexander Date: Tue, 29 May 2018 18:32:52 +0800 Subject: [PATCH 4/4] Fix: load! ipython when featurep! --- modules/lang/org/config.el | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/lang/org/config.el b/modules/lang/org/config.el index be1238292..ec0924aa5 100644 --- a/modules/lang/org/config.el +++ b/modules/lang/org/config.el @@ -8,6 +8,7 @@ (if (featurep! +babel) (load! "+babel")) (if (featurep! +capture) (load! "+capture")) (if (featurep! +export) (load! "+export")) +(if (featurep! +ipython) (load! "+ipython")) (if (featurep! +present) (load! "+present")) ;; TODO (if (featurep! +publish) (load! "+publish"))