diff --git a/.travis.yml b/.travis.yml index 77c3ab06c..bceca2d7e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,14 +37,14 @@ env: matrix: include: - os: linux - env: EMACS_CI=emacs-25-3 IPYTHON=5.8.0 PY=python PIP="${PY} -m pip install --user" + env: EMACS_CI=emacs-25-1 IPYTHON=5.8.0 PY=python PIP="${PY} -m pip install --user" - os: linux env: EMACS_CI=emacs-26-1 IPYTHON=6.5.0 PY=python3 PIP="${PY} -m pip install --user" - os: linux env: EMACS_CI=emacs-26-3 IPYTHON=7.8.0 PY=python3 PIP="${PY} -m pip install --user" - os: osx language: generic - env: EVM_EMACS=emacs-25.2 IPYTHON=5.8.0 PY=python PIP="pip install" TOXENV=py27 + env: EVM_EMACS=emacs-26.3 IPYTHON=5.8.0 PY=python3 PIP="${PY} -m pip install" TOXENV=py37 allow_failures: - env: EMACS_CI=emacs-snapshot @@ -53,11 +53,11 @@ install: - sh tools/install-virtualenv.sh - | if [ "x$TRAVIS_OS_NAME" = "xosx" ]; then - eval "$(pyenv init -)" ; pyenv activate $TOXENV ; + pip install virtualenv ; fi - ${PIP} --upgrade pip - - ${PIP} wheel jupyter ipython\<=$IPYTHON jedi\>=0.15.1 ipykernel numpy\<=1.16.0 matplotlib\<=3.0.2 + - ${PIP} wheel jupyter ipython\<=$IPYTHON jedi\>=0.15.1 numpy\<=1.16.0 matplotlib\<=3.0.2 epc - ${PY} -m ipykernel install --user - sh tools/install-R.sh - sh tools/install-julia.sh @@ -88,4 +88,4 @@ script: make test-jupyterhub fi - rm -rf $HOME/.matplotlib $HOME/.cache/fontconfig - - make test || ( ( zip -q - log/{testein,testfunc,ecukes}.* 2>/dev/null | uuencode log.zip ) && ( printf "To diagnose, travis logs -i | dos2unix | sed '/^begin 644/,/^end/!d' | uudecode" ) && false) + - make test || ( ( zip -q - log/{testein,testfunc,ecukes}.* 2>/dev/null | uuencode log.zip ) && ( printf "To diagnose, travis logs -i | sed '/^begin 664/,/^end/!d' | uudecode" ) && false) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7162d505c..bd8717c29 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,11 +6,13 @@ Fork the repo on github. Clone the fork to your home directory. Install cask. Run `make dist` to ensure correct cask functionality. -Run `make test` to ensure a correct baseline. +Run `make test` to ensure a correct baseline. You need Jupyter, R and Julia +executables to be available through your `$PATH` for this (and also, +`matplotlib` and kernels for R and Julia installed and visible for the +aforementioned Jupyter). -Until we institute a virtualenv for the required testing software (jupyter, R, matplotlib, etc.), out-of-the-box `make test` remains problematic. - -Remove the MELPA-installed EIN by deleting the package directory (on my system, it's `~/.emacs.d/elpa/ein-20190122.1341`) or running `M-x package-delete`. +Remove the MELPA-installed EIN by deleting the package directory (on my system, +it's `~/.emacs.d/elpa/ein-20190122.1341`) or running `M-x package-delete`. In your `init.el` or `.emacs`, add the following: @@ -27,7 +29,8 @@ Dev tools Quick sanity checking --------------------- -`make quick` runs a syntax check and the unit tests. It is far quicker than the laborious `make test`. +`make quick` runs a syntax check and the unit tests. It is far quicker than the +laborious `make test`. Unit tests ---------- @@ -37,6 +40,8 @@ Integration tests ----------------- If you add a feature, we encourage writing an integration test. -`cask exec ecukes` is the bulk of `make test`. Ecukes is our friend and guardian. We follow its opinionated file structure in `~/emacs-ipython-notebook/features`. +`cask exec ecukes` is the bulk of `make test`. Ecukes is our friend and +guardian. We follow its opinionated file structure in +`~/emacs-ipython-notebook/features`. To run say just the login tests, `cask exec ecukes --tags "@login"`. diff --git a/Cask b/Cask index 13832ef23..d3d7cbf3a 100644 --- a/Cask +++ b/Cask @@ -7,6 +7,7 @@ (development (depends-on "websocket") (depends-on "request") + (depends-on "jedi") (depends-on "dash") (depends-on "ert-runner") (depends-on "ecukes") @@ -20,7 +21,10 @@ (depends-on "smartrep") (depends-on "polymode") (depends-on "markdown-mode") + (depends-on "clojure-mode") (depends-on "julia-mode") + (depends-on "haskell-mode") + (depends-on "hy-mode") (depends-on "undo-tree") (depends-on "ess") (depends-on "px") diff --git a/Makefile b/Makefile index bab278276..a7f72041b 100644 --- a/Makefile +++ b/Makefile @@ -66,7 +66,7 @@ quick: test-compile test-ob-ein-recurse test-unit test-jupyterhub: test-compile # jupyterhub slightly temperamental with json-readtable-error # seems to be affecting ob-ipython too but probably my bug.. just need to find it - -cask exec ecukes --tags @jupyterhub --reporter magnars + -cask exec ecukes --tags @jupyterhub,~@complete-ipy7 --reporter magnars .PHONY: test test: quick test-int test-poly @@ -75,12 +75,12 @@ test: quick test-int test-poly test-poly: cask exec ert-runner -L ./lisp -L ./test -l test/testfunc.el test/test-poly.el test/test-func.el cp test/test-poly.el features/support/test-poly.el - cask exec ecukes --reporter magnars ; (ret=$$? ; rm -f features/support/test-poly.el && exit $$ret) + cask exec ecukes --reporter magnars --tags ~@skip-travis; (ret=$$? ; rm -f features/support/test-poly.el && exit $$ret) .PHONY: test-int test-int: cask exec ert-runner -L ./lisp -L ./test -l test/testfunc.el test/test-func.el - cask exec ecukes --reporter magnars + cask exec ecukes --reporter magnars --tags ~@poly-complete,~@skip-travis .PHONY: test-unit test-unit: diff --git a/doc/source/index.rst b/doc/source/index.rst index 69689ad2a..c37758a53 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -306,7 +306,9 @@ created via `ein:notebook-create-checkpoint`. :replace: s/C-c TAB/C-c C-i/ s/C-c RET/C-c C-m/ -.. el:function:: ein:worksheet-execute-all-cell +.. el:function:: ein:worksheet-execute-all-cells +.. el:function:: ein:worksheet-execute-all-cells-above +.. el:function:: ein:worksheet-execute-all-cells-below .. el:function:: ein:worksheet-delete-cell .. el:function:: ein:junk-rename .. el:function:: ein:iexec-mode diff --git a/features/execute-all-cells.feature b/features/execute-all-cells.feature new file mode 100644 index 000000000..3f170ed19 --- /dev/null +++ b/features/execute-all-cells.feature @@ -0,0 +1,52 @@ +@execute-all-cells +Scenario: Execute all cells + Given new python notebook + And I type "2 ** 3" + And I press "C-c C-b" + And I type "2 ** 4" + And I press "C-c C-b" + And I type "2 ** 5" + And I press "C-c C-b" + And I type "2 ** 6" + When I call "ein:worksheet-execute-all-cells" + And I wait 1 second + Then I should see "8" + And I should see "16" + And I should see "32" + And I should see "64" + +@execute-all-cells +Scenario: Execute all cells above + Given new python notebook + And I type "2 ** 3" + And I press "C-c C-b" + And I type "2 ** 4" + And I press "C-c C-b" + And I type "2 ** 5" + And I press "C-c C-b" + And I type "2 ** 6" + And I press "C-c C-p" + When I call "ein:worksheet-execute-all-cells-above" + And I wait 1 second + Then I should see "8" + And I should see "16" + And I should not see "32" + And I should not see "64" + +@execute-all-cells +Scenario: Execute all cells below + Given new python notebook + And I type "2 ** 3" + And I press "C-c C-b" + And I type "2 ** 4" + And I press "C-c C-b" + And I type "2 ** 5" + And I press "C-c C-b" + And I type "2 ** 6" + And I press "C-c C-p" + When I call "ein:worksheet-execute-all-cells-below" + And I wait 1 second + Then I should not see "8" + And I should not see "16" + And I should see "32" + And I should see "64" diff --git a/features/notebook.feature b/features/notebook.feature index 6a1189ac7..2ddedff3a 100644 --- a/features/notebook.feature +++ b/features/notebook.feature @@ -53,6 +53,23 @@ Scenario: company completion Given new python notebook Given I set "ein:completion-backend" to eval "(quote ein:use-none-backend)" +@complete @skip-travis +Scenario: company completion without execution + Given I set "ein:completion-backend" to eval "(quote ein:use-company-backend)" + Given I kill all websocket buffers + Given new python notebook + And I type "import itertools" + And I press "RET" + And I type "itertool" + And I call "company-complete" + Then I should see "itertools" + And I type ".chai" + And I call "company-complete" + Then I should see "itertools.chain" + Given I set "ein:completion-backend" to eval "(quote ein:use-ac-backend)" + Given new python notebook + Given I set "ein:completion-backend" to eval "(quote ein:use-none-backend)" + @rename Scenario: rename notebook Given new python notebook diff --git a/features/ob-ein.feature b/features/ob-ein.feature index 31011ee89..2f6007c14 100644 --- a/features/ob-ein.feature +++ b/features/ob-ein.feature @@ -112,6 +112,43 @@ Scenario: Specific port, portless localhost refers to same, concurrent execution And I wait for buffer to say "3.1415" And I should not see "[....]" +@org @complete @skip-travis +Scenario: Completion in an anonymous source block + Given I set "ein:completion-backend" to eval "(quote ein:use-company-backend)" + Given I eval (global-company-mode +1) + Given I stop the server + When I open temp file "complete.org" + And I call "org-mode" + And I type "" And I press "C-" And I go to word "Header" + +@poly-complete +Scenario: completion in polymode notebook + Given I set "ein:completion-backend" to eval "(quote ein:use-none-backend)" + Given jedi completion environment + Given new python notebook + And I type "import itertools" + And I press "RET" + And I type "itertools.chai" + And I call "jedi:complete" + And I wait for the smoke to clear + Then jedi completions should contain "chain" diff --git a/features/pytools.feature b/features/pytools.feature new file mode 100644 index 000000000..eb4aa4a23 --- /dev/null +++ b/features/pytools.feature @@ -0,0 +1,31 @@ +@pytools +Scenario: Check that pytools are being loaded. + Given new python notebook + And I type "__ein_pytools_version" + And I press "M-RET" + And I wait for the smoke to clear + Then I should see "1.1.0" + +@pytools @matplotlib +Scenario: Setting matplotlib figure size. + Given new python notebook + And I type "import matplotlib.pyplot as plt" + And I press "M-RET" + And I wait for the smoke to clear + And I eval (ein:pytools-set-figure-size 8.0 6.0) + And I type "__ein_rcParams['figure.figsize']" + And I press "M-RET" + And I wait for the smoke to clear + Then I should see "[8.0, 6.0]" + +@pytools @matplotlib +Scenario: Use generic command to set a matplotlib parameter. + Given new python notebook + And I type "import matplotlib.pyplot as plt" + And I press "M-RET" + And I wait for the smoke to clear + And I eval (ein:pytools-set-matplotlib-parameter "figure.dpi" 120) + And I type "__ein_rcParams['figure.dpi']" + And I press "M-RET" + And I wait for the smoke to clear + Then I should see "120" diff --git a/features/step-definitions/ein-steps.el b/features/step-definitions/ein-steps.el index a100549c3..b3ca780cf 100644 --- a/features/step-definitions/ein-steps.el +++ b/features/step-definitions/ein-steps.el @@ -163,6 +163,11 @@ (switch-to-buffer buf-name) (Then "I should be in buffer \"%s\"" buf-name)))))) +(When "^case sensitive checked notebook$" + (lambda () + (let ((case-fold-search nil)) + (Then "new python notebook")))) + (When "^I kill buffer and reopen$" (lambda () (let ((name (ein:$notebook-notebook-name ein:%notebook%))) @@ -280,7 +285,7 @@ (When "^I wait for the smoke to clear" (lambda () (ein:testing-flush-queries) - (And "I wait 1 second"))) ;; eldoc-documentation-function not flushing + (And "I wait 10 seconds"))) ;; eldoc-documentation-function not flushing (When "^I keep clicking \"\\(.+\\)\" until \"\\(.+\\)\"$" (lambda (go stop) @@ -345,7 +350,7 @@ (And "I clear log expr \"ein:log-all-buffer-name\"") (Then "I ctrl-c-ctrl-c")) nil))) - nil 40000 2000))) + nil 80000 2000))) (When "^I wait for cell to execute$" (lambda () @@ -420,6 +425,10 @@ (lambda (variable value) (set (intern variable) (eval (car (read-from-string value)))))) +(When "^I eval \\(.+\\)$" + (lambda (expr) + (eval (car (read-from-string expr))))) + (When "^I connect to default notebook" (lambda () (ein:connect-to-notebook-buffer (car (ein:notebook-opened-buffer-names @@ -483,3 +492,17 @@ (mapcan (lambda (prop) (not (get-text-property (point) (intern prop)))) (split-string properties ","))))) + +(When "^jedi completions should contain \"\\(.+\\)\"$" + (lambda (str) + (let ((completions (jedi:ac-direct-matches))) + (should (some #'(lambda (x) + (string= x str)) + completions))))) + +(When "^jedi completion environment$" + (lambda () + (require 'jedi) + (jedi:install-server-block) + (add-hook 'python-mode-hook 'jedi:setup) + (setq jedi:complete-on-dot t))) diff --git a/features/undo.feature b/features/undo.feature index f98a40026..cca4a3440 100644 --- a/features/undo.feature +++ b/features/undo.feature @@ -1,3 +1,4 @@ + @undo Scenario: Undo turned off Given I disable "ein:worksheet-enable-undo" @@ -353,3 +354,15 @@ Scenario: Split and merge don't break undo And I undo again And I undo again Then the cursor should be at point "93" + +@undo, @issue-630 +Scenario: Regression case for issue #630. + Given I enable "ein:worksheet-enable-undo" + Given new python notebook + And I type "while True:" + And I press "RET" + and I type "print(1)" + And I press "M-RET" + And I switch to buffer like "*Messages*" + Then I should not see "ein: [info] WS action ([wrong-type-argument sequencep t])" + Then I should not see "ein: [info] WS action ([error ein:worksheet--unshift-undo-list" diff --git a/lisp/ein-ac.el b/lisp/ein-ac.el index 6b62c5b6f..5bc10d230 100644 --- a/lisp/ein-ac.el +++ b/lisp/ein-ac.el @@ -25,7 +25,6 @@ ;;; Code: -(eval-when-compile (require 'cl)) (require 'auto-complete) (require 'ein-core) @@ -71,10 +70,10 @@ When this option is enabled, cached omni completion is available." "Return matched candidates in CHUNK-LIST." (let* ((start (ein:ac-chunk-beginning))) (when start - (loop with prefix = (buffer-substring start (point)) - for cc in chunk-list - when (string-prefix-p prefix cc) - collect cc)))) + (cl-loop with prefix = (buffer-substring start (point)) + for cc in chunk-list + when (string-prefix-p prefix cc) + collect cc)))) ;;; AC Source @@ -86,18 +85,7 @@ When this option is enabled, cached omni completion is available." (defun ein:ac-direct-get-matches () (ein:ac-chunk-candidates-from-list ein:ac-direct-matches)) -(ac-define-source ein-direct - '((candidates . ein:ac-direct-get-matches) - (requires . 0) - (prefix . ein:ac-chunk-beginning) - (symbol . "s"))) -(ac-define-source ein-async - '((candidates . ein:ac-direct-get-matches) - (requires . 0) - (prefix . ein:ac-chunk-beginning) - (init . ein:ac-request-in-background) - (symbol . "c"))) (define-obsolete-function-alias 'ac-complete-ein-cached 'ac-complete-ein-async "0.2.1") @@ -105,9 +93,9 @@ When this option is enabled, cached omni completion is available." "0.2.1") (defun ein:ac-request-in-background () - (cl-ecase ein:completion-backend + (cl-case ein:completion-backend (ein:use-ac-backend (ein:aif (ein:get-kernel) - (ein:completer-complete + (ein:completer-complete it (list :complete_reply (cons (lambda (_ content __) @@ -131,7 +119,7 @@ Call this function before calling `auto-complete'." (when matches (setq ein:ac-direct-matches matches))) ; let-binding won't work -(defun* ein:completer-finish-completing-ac +(cl-defun ein:completer-finish-completing-ac (matched-text matches &key (expand ac-expand-on-auto-complete) @@ -247,7 +235,22 @@ Specifying non-`nil' to SUPERPACK enables richer auto-completion (when superpack (ein:ac-superpack))) -(ein:ac-config ein:use-auto-complete-superpack) +(defun ein:ac-install-backend () + (ac-define-source ein-direct + '((candidates . ein:ac-direct-get-matches) + (requires . 0) + (prefix . ein:ac-chunk-beginning) + (symbol . "s"))) + + (ac-define-source ein-async + '((candidates . ein:ac-direct-get-matches) + (requires . 0) + (prefix . ein:ac-chunk-beginning) + (init . ein:ac-request-in-background) + (symbol . "c"))) + + (ein:ac-config ein:use-auto-complete-superpack)) + (provide 'ein-ac) ;;; ein-ac.el ends here diff --git a/lisp/ein-cell-edit.el b/lisp/ein-cell-edit.el index edf234035..7ca855403 100644 --- a/lisp/ein-cell-edit.el +++ b/lisp/ein-cell-edit.el @@ -38,6 +38,8 @@ (defvar ein:src--overlay nil) (defvar ein:src--saved-window-config nil) +(declare-function ein:notebook--get-nb-or-error "ein-notebook" ()) + (defvar ein:edit-cell-mode-map (let ((map (make-sparse-keymap))) (define-key map "\C-c'" 'ein:edit-cell-exit) @@ -166,9 +168,10 @@ previous value." (defun ein:get-mode-for-kernel (kernelspec) (if (null kernelspec) 'python ;; FIXME - (ein:case-equal (ein:$kernelspec-language kernelspec) - (("julia" "python" "R") (intern (ein:$kernelspec-language kernelspec))) - (t 'python)))) + (intern (ein:$kernelspec-language kernelspec)))) + ;; (ein:case-equal (ein:$kernelspec-language kernelspec) + ;; (("julia" "python" "R") ) + ;; (t 'python)))) (defun ein:edit-src-continue (e) (interactive "e") @@ -238,7 +241,7 @@ appropriate language major mode. Functionality is very similar to (t (funcall ein:raw-cell-default-edit-mode))) (if raw-cell-p (funcall ein:raw-cell-default-edit-mode) - (case (ein:get-mode-for-kernel (ein:$notebook-kernelspec notebook)) + (cl-case (ein:get-mode-for-kernel (ein:$notebook-kernelspec notebook)) (julia (julia-mode)) (python (python-mode)) (R (R-mode)))))) diff --git a/lisp/ein-cell-output.el b/lisp/ein-cell-output.el index a0fcf62f6..a2edd3efa 100644 --- a/lisp/ein-cell-output.el +++ b/lisp/ein-cell-output.el @@ -59,9 +59,9 @@ (data . (,data))))) (defun ein:maybe-get-output-mime-data (output) - (loop for type in '(:svg :png :jpeg :html :latex :javascript :text) - if (plist-get output type) - collecting (cons (ein:output-property type) (plist-get output type)))) + (cl-loop for type in '(:svg :png :jpeg :html :latex :javascript :text) + if (plist-get output type) + collecting (cons (ein:output-property type) (plist-get output type)))) (defun ein:cell-display-data-output-to-json (output) (let ((data (or (ein:maybe-get-output-mime-data output) @@ -71,9 +71,8 @@ (metadata . ,(make-hash-table))))) (defun ein:find-and-make-outputs (output-plist) - (loop for prop in ein:output-type-map - when (plist-get output-plist (cdr prop)) - collect (list (cdr prop) (plist-get output-plist (cdr prop))))) + (cl-loop for prop in ein:output-type-map + when (plist-get output-plist (cdr prop)) + collect (list (cdr prop) (plist-get output-plist (cdr prop))))) (provide 'ein-cell-output) - diff --git a/lisp/ein-cell.el b/lisp/ein-cell.el index 0f3cc0b11..c5b1a0df6 100644 --- a/lisp/ein-cell.el +++ b/lisp/ein-cell.el @@ -33,7 +33,6 @@ ;;; Code: -(eval-when-compile (require 'cl)) (require 'ansi-color) (require 'comint) (require 'ein-core) @@ -198,12 +197,27 @@ a number will limit the number of lines in a cell output." (condition-case-unless-debug err (let* ((img (apply #'create-image args))) (if ein:slice-image - (destructuring-bind (&optional rows cols) + (cl-destructuring-bind (&optional rows cols) (when (listp ein:slice-image) ein:slice-image) (insert-sliced-image img "." nil (or rows 20) cols)) (insert-image img "."))) (error (ein:log 'warn "Could not insert image: %s" err) nil))) +(defun ein:cell--markdown-heading-p (cell) + "Check if cell is behaving like a heading cell. + +Returns true if cell is a markdown cell, has at least one # character at the start of the line and is only one line long (i.e. does not contain any newline characters)." + (and (ein:markdowncell-p cell) + (string-match "^#+ " (ein:cell-get-text cell)) + (= 0 (seq-count #'(lambda (c) + (char-equal c ?\n)) + (ein:cell-get-text cell))))) + +(cl-defmethod ein:cell--markdown-heading-level ((cell ein:markdowncell)) + (progn + (string-match "^#+" (ein:cell-get-text cell)) + (match-end 0))) + ;;; Cell factory @@ -228,6 +242,7 @@ a number will limit the number of lines in a cell output." (defun ein:cell-from-type (type &rest args) (apply (ein:cell-class-from-type type) args)) + (defun ein:cell--determine-cell-type (json-data) (let ((base-type (plist-get json-data :cell_type)) (metadata (plist-get json-data :metadata))) @@ -277,7 +292,7 @@ a number will limit the number of lines in a cell output." (cl-defmethod ein:cell-convert ((cell ein:basecell) type) (let ((new (ein:cell-from-type type))) ;; copy attributes - (loop for k in '(read-only ewoc) + (cl-loop for k in '(read-only ewoc) do (setf (slot-value new k) (slot-value cell k))) ;; copy input (setf (slot-value new 'input) (if (ein:cell-active-p cell) @@ -310,16 +325,15 @@ a number will limit the number of lines in a cell output." "Convert CELL to TYPE and redraw corresponding ewoc nodes." (let ((new (ein:cell-convert cell type))) ;; copy element attribute - (loop for k in (slot-value new 'element-names) - with old-element = (slot-value cell 'element) - do (progn - (setf (slot-value new 'element) - (plist-put (slot-value new 'element) k - (plist-get old-element k))))) + (cl-loop for k in (slot-value new 'element-names) + with old-element = (slot-value cell 'element) + do (setf (slot-value new 'element) + (plist-put (slot-value new 'element) k + (plist-get old-element k)))) ;; setting ewoc nodes - (loop for en in (ein:cell-all-element cell) - for node = (ewoc-data en) - do (setf (ein:$node-data node) new)) + (cl-loop for en in (ein:cell-all-element cell) + for node = (ewoc-data en) + do (setf (ein:$node-data node) new)) (let ((inhibit-read-only t) (buffer-undo-list t)) ; disable undo recording ;; delete ewoc nodes that is not copied @@ -327,25 +341,25 @@ a number will limit the number of lines in a cell output." #'ewoc-delete (slot-value new 'ewoc) (apply #'append - (loop for name in (slot-value cell 'element-names) - unless (memq name (slot-value new 'element-names)) - collect (let ((ens (ein:cell-element-get cell name))) - (if (listp ens) ens (list ens)))))) + (cl-loop for name in (slot-value cell 'element-names) + unless (memq name (slot-value new 'element-names)) + collect (let ((ens (ein:cell-element-get cell name))) + (if (listp ens) ens (list ens)))))) ;; draw ewoc node - (loop with ewoc = (slot-value new 'ewoc) - for en in (ein:cell-all-element new) - do (ewoc-invalidate ewoc en))) + (cl-loop with ewoc = (slot-value new 'ewoc) + for en in (ein:cell-all-element new) + do (ewoc-invalidate ewoc en))) new)) (cl-defmethod ein:cell-change-level ((cell ein:headingcell) level) - (assert (integerp level)) + (cl-assert (integerp level)) (let ((inhibit-read-only t) (buffer-undo-list t)) ; disable undo recording (setf (slot-value cell 'level) level) ;; draw ewoc node - (loop with ewoc = (slot-value cell 'ewoc) - for en in (ein:cell-all-element cell) - do (ewoc-invalidate ewoc en)))) + (cl-loop with ewoc = (slot-value cell 'ewoc) + for en in (ein:cell-all-element cell) + do (ewoc-invalidate ewoc en)))) ;;; Getter/setter @@ -368,9 +382,9 @@ a number will limit the number of lines in a cell output." (let ((element (slot-value cell 'element))) (if index (progn - (assert (eql prop :output)) + (cl-assert (eql prop :output)) (nth index (plist-get element prop))) - (case prop + (cl-case prop (:after-input (ein:aif (nth 0 (plist-get element :output)) it @@ -386,7 +400,7 @@ a number will limit the number of lines in a cell output." (cl-defmethod ein:cell-element-get ((cell ein:textcell) prop &rest args) (let ((element (slot-value cell 'element))) - (case prop + (cl-case prop (:after-input (plist-get element :footer)) (:before-input (plist-get element :prompt)) (t (cl-call-next-method))))) @@ -421,8 +435,8 @@ Return language name as a string or `nil' when not defined. (list :prompt (funcall make-node 'prompt) :input (funcall make-node 'input) - :output (loop for i from 0 below num-outputs - collect (funcall make-node 'output i)) + :output (cl-loop for i from 0 below num-outputs + collect (funcall make-node 'output i)) :footer (funcall make-node 'footer)))) (cl-defmethod ein:cell-enter-last ((cell ein:basecell)) @@ -467,7 +481,7 @@ Return language name as a string or `nil' when not defined. other-cell)) (defun ein:cell-pp (path data) - (case (car path) + (cl-case (car path) (prompt (ein:cell-insert-prompt data)) (input (ein:cell-insert-input data)) (output (ein:cell-insert-output (cadr path) data)) @@ -713,7 +727,7 @@ PROP is a name of cell element. Default is `:input'. (unless relpos (setq relpos 0)) (unless prop (setq prop :input)) (ewoc-goto-node (slot-value cell 'ewoc) (ein:cell-element-get cell prop)) - (let ((offset (case prop + (let ((offset (cl-case prop ((:input :before-output) 1) (:after-input -1) (t 0)))) @@ -729,7 +743,7 @@ PROP is a name of cell element. Default is `:input'. (unless prop (setq prop :input)) (let ((goal-column nil)) (ewoc-goto-node (slot-value cell 'ewoc) (ein:cell-element-get cell prop))) - (let ((offset (case prop + (let ((offset (cl-case prop ((:input :before-output) 1) (:after-input -1) (t 0)))) @@ -752,7 +766,7 @@ If END is non-`nil', return the location of next element." (unless elm (setq elm :prompt)) (let ((element (slot-value cell 'element))) (when end - (setq elm (case elm + (setq elm (cl-case elm (:prompt :input) (:input :after-input) (:output :after-output))) @@ -761,7 +775,7 @@ If END is non-`nil', return the location of next element." (setq elm :prompt))) (if cell (ewoc-location (ein:cell-element-get cell elm)) - (assert end) + (cl-assert end) (point-max)))) (cl-defmethod ein:cell-buffer ((cell ein:basecell)) @@ -843,7 +857,7 @@ If END is non-`nil', return the location of next element." (when (or (null old-tb) (null new-tb) - (not (equalp new-tb old-tb))) + (not (cl-equalp new-tb old-tb))) (ein:cell-actually-append-output cell json dynamic)) (setf (slot-value cell 'traceback) new-tb))) @@ -977,35 +991,35 @@ prettified text thus be used instead of HTML type." (defun ein:cell-append-mime-type (json dynamic) (when (plist-get json :data) (setq json (plist-get json :data))) ;; For nbformat v4 support. - (loop - for key in (cond - ((functionp ein:output-type-preference) - (funcall ein:output-type-preference json)) - (t ein:output-type-preference)) - for type = (intern (format ":%s" key)) ; something like `:text' - for value = (plist-get json type) ; FIXME: optimize - when (plist-member json type) - return - (case key - ;; NOTE: Normally `javascript' and `html' will not be inserted as - ;; they come out after `text'. Maybe it is better to inform user - ;; when one of them is inserted. - ((javascript text/javascript) - (when dynamic - (ein:log 'info (concat "ein:cell-append-mime-type does not support " - "dynamic javascript. got: %s") value)) - (ein:insert-read-only (plist-get json type))) - (emacs-lisp - (when dynamic - (ein:cell-safe-read-eval-insert (plist-get json type)))) - ((html text/html) - (funcall (ein:output-area-get-html-renderer) (plist-get json type))) - ((latex text/latex text text/plain) - (ein:insert-read-only (plist-get json type))) - ((svg image/svg+xml) - (ein:insert-image value (ein:fix-mime-type key) t)) - ((png image/png jpeg image/jpeg) - (ein:insert-image (base64-decode-string value) (ein:fix-mime-type key) t))))) + (cl-loop + for key in (cond + ((functionp ein:output-type-preference) + (funcall ein:output-type-preference json)) + (t ein:output-type-preference)) + for type = (intern (format ":%s" key)) ; something like `:text' + for value = (plist-get json type) ; FIXME: optimize + when (plist-member json type) + return + (cl-case key + ;; NOTE: Normally `javascript' and `html' will not be inserted as + ;; they come out after `text'. Maybe it is better to inform user + ;; when one of them is inserted. + ((javascript text/javascript) + (when dynamic + (ein:log 'info (concat "ein:cell-append-mime-type does not support " + "dynamic javascript. got: %s") value)) + (ein:insert-read-only (plist-get json type))) + (emacs-lisp + (when dynamic + (ein:cell-safe-read-eval-insert (plist-get json type)))) + ((html text/html) + (funcall (ein:output-area-get-html-renderer) (plist-get json type))) + ((latex text/latex text text/plain) + (ein:insert-read-only (ansi-color-apply (plist-get json type)))) + ((svg image/svg+xml) + (ein:insert-image value (ein:fix-mime-type key) t)) + ((png image/png jpeg image/jpeg) + (ein:insert-image (base64-decode-string value) (ein:fix-mime-type key) t))))) (defun ein:cell-append-text (data &rest properties) ;; escape ANSI in plaintext: @@ -1066,54 +1080,54 @@ prettified text thus be used instead of HTML type." (plist-put output :metadata (make-hash-table))) (setq renamed-outputs (append renamed-outputs - (list (let ((ocopy (copy-list output)) + (list (let ((ocopy (cl-copy-list output)) (new-output '())) - (loop while ocopy - do (let ((prop (pop ocopy)) - (value (pop ocopy))) - (ein:log 'debug "Checking property %s for output type '%s'" - prop otype) - (cond - ((equal prop :stream) (progn (push value new-output) - (push :name new-output))) - - ((and (or (equal otype "display_data") - (equal otype "execute_result")) - (ein:output-property-p prop)) - (let ((new-prop (cdr (ein:output-property-p prop)))) - (if (plist-member new-output :data) - (setq new-output (plist-put new-output :data - (append (plist-get new-output :data) - (list new-prop (list value)) - ))) - (push (list new-prop (list value)) new-output) - (push :data new-output)) - )) - - ((and (equal otype "display_data") - (equal prop :text)) - (ein:log 'debug "SAVE-NOTEBOOK: Skipping unnecessary :text data.")) - - ;; ((and (equal otype "execute_result") - ;; (ein:output-property-p prop) - ;; ;; (or (equal prop :text) - ;; ;; (equal prop :html) - ;; ;; (equal prop :latex)) - ;; ) - ;; (ein:log 'debug "Fixing execute_result (%s?)." otype) - ;; (let ((new-prop (cdr (ein:output-property-p prop)))) - ;; (push (list new-prop (list value)) new-output) - ;; (push :data new-output))) - - - ((and (equal otype "execute_result") - (equal prop :prompt_number)) - (ein:log 'debug "SAVE-NOTEBOOK: Fixing prompt_number property.") - (push value new-output) - (push :execution_count new-output)) - - (t (progn (push value new-output) (push prop new-output))))) - finally return new-output)))) + (cl-loop while ocopy + do (let ((prop (pop ocopy)) + (value (pop ocopy))) + (ein:log 'debug "Checking property %s for output type '%s'" + prop otype) + (cond + ((equal prop :stream) (progn (push value new-output) + (push :name new-output))) + + ((and (or (equal otype "display_data") + (equal otype "execute_result")) + (ein:output-property-p prop)) + (let ((new-prop (cdr (ein:output-property-p prop)))) + (if (plist-member new-output :data) + (setq new-output (plist-put new-output :data + (append (plist-get new-output :data) + (list new-prop (list value)) + ))) + (push (list new-prop (list value)) new-output) + (push :data new-output)) + )) + + ((and (equal otype "display_data") + (equal prop :text)) + (ein:log 'debug "SAVE-NOTEBOOK: Skipping unnecessary :text data.")) + + ;; ((and (equal otype "execute_result") + ;; (ein:output-property-p prop) + ;; ;; (or (equal prop :text) + ;; ;; (equal prop :html) + ;; ;; (equal prop :latex)) + ;; ) + ;; (ein:log 'debug "Fixing execute_result (%s?)." otype) + ;; (let ((new-prop (cdr (ein:output-property-p prop)))) + ;; (push (list new-prop (list value)) new-output) + ;; (push :data new-output))) + + + ((and (equal otype "execute_result") + (equal prop :prompt_number)) + (ein:log 'debug "SAVE-NOTEBOOK: Fixing prompt_number property.") + (push value new-output) + (push :execution_count new-output)) + + (t (progn (push value new-output) (push prop new-output))))) + finally return new-output)))) )))) `((source . ,(ein:cell-get-text cell)) (cell_type . "code") @@ -1245,17 +1259,17 @@ prettified text thus be used instead of HTML type." (defun ein:output-area-convert-mime-types (json data) - (loop for (prop . mime) in '((:text . :text/plain) - (:html . :text/html) - (:svg . :image/svg+xml) - (:png . :image/png) - (:jpeg . :image/jpeg) - (:latex . :text/latex) - (:json . :application/json) - (:javascript . :application/javascript) - (:emacs-lisp . :application/emacs-lisp)) - when (plist-member data mime) - do (plist-put json prop (plist-get data mime))) + (cl-loop for (prop . mime) in '((:text . :text/plain) + (:html . :text/html) + (:svg . :image/svg+xml) + (:png . :image/png) + (:jpeg . :image/jpeg) + (:latex . :text/latex) + (:json . :application/json) + (:javascript . :application/javascript) + (:emacs-lisp . :application/emacs-lisp)) + when (plist-member data mime) + do (plist-put json prop (plist-get data mime))) json) @@ -1274,23 +1288,23 @@ prettified text thus be used instead of HTML type." (cl-defmethod ein:cell-has-image-ouput-p ((cell ein:codecell)) "Return `t' if given cell has image output, `nil' otherwise." - (loop for out in (slot-value cell 'outputs) - when (or (plist-member out :svg) - (plist-member out :image/svg+xml) - (plist-member out :png) - (plist-member out :image/png) - (plist-member out :jpeg) - (plist-member out :image/jpeg)) - return t)) + (cl-loop for out in (slot-value cell 'outputs) + when (or (plist-member out :svg) + (plist-member out :image/svg+xml) + (plist-member out :png) + (plist-member out :image/png) + (plist-member out :jpeg) + (plist-member out :image/jpeg)) + return t)) (cl-defmethod ein:cell-has-image-ouput-p ((cell ein:textcell)) nil) (cl-defmethod ein:cell-get-tb-data ((cell ein:codecell)) - (loop for out in (slot-value cell 'outputs) - when (and (plist-get out :traceback) - (member (plist-get out :output_type) '("pyerr" "error"))) - return (plist-get out :traceback))) + (cl-loop for out in (slot-value cell 'outputs) + when (and (plist-get out :traceback) + (member (plist-get out :output_type) '("pyerr" "error"))) + return (plist-get out :traceback))) (provide 'ein-cell) diff --git a/lisp/ein-classes.el b/lisp/ein-classes.el index 2006cf78b..393899da3 100644 --- a/lisp/ein-classes.el +++ b/lisp/ein-classes.el @@ -25,7 +25,9 @@ ;;; Content (require 'eieio) -(defstruct ein:$content +(require 'ein-utils) + +(cl-defstruct ein:$content "Content returned from the Jupyter notebook server: `ein:$content-url-or-port' URL or port of Jupyter server. @@ -85,7 +87,7 @@ ;;; Websockets -(defstruct ein:$websocket +(cl-defstruct ein:$websocket "A wrapper object of `websocket'. `ein:$websocket-ws' : an instance returned by `websocket-open' @@ -98,7 +100,7 @@ ;;; Notebook -(defstruct ein:$notebook +(cl-defstruct ein:$notebook "Hold notebook variables. `ein:$notebook-url-or-port' @@ -202,7 +204,7 @@ ;;; Kernel -(defstruct ein:$kernelspec +(cl-defstruct ein:$kernelspec "Kernel specification as return by the Jupyter notebook server. `ein:$kernelspec-name' : string @@ -229,7 +231,7 @@ ;; FIXME: Rewrite `ein:$kernel' using `defclass'. It should ease ;; testing since I can mock I/O using method overriding. -(defstruct ein:$kernel +(cl-defstruct ein:$kernel "Should perhaps be named ein:$session. We glom session and kernel as defined by the server as just ein:$kernel in the client. " diff --git a/lisp/ein-company.el b/lisp/ein-company.el index 4bef496a5..41504f056 100644 --- a/lisp/ein-company.el +++ b/lisp/ein-company.el @@ -26,7 +26,6 @@ ;;; Code: -(eval-when-compile (require 'cl)) (require 'deferred) (require 'ein-completer) (require 'company nil t) @@ -55,7 +54,7 @@ (ein:completions--prepare-matches prefix fetcher replies)))))) (defun ein:completions--prepare-matches (prefix fetcher replies) - (destructuring-bind + (cl-destructuring-bind ((&key matches cursor_start cursor_end &allow-other-keys) ; :complete_reply _metadata) replies @@ -93,7 +92,7 @@ (ein:aif cached it (unless (ein:company--punctuation-check (thing-at-point 'line) (current-column)) - (case ein:completion-backend + (cl-case ein:completion-backend (t (cons :async (lambda (cb) @@ -101,7 +100,7 @@ (defun ein:company--punctuation-check (thing col) (or (string-match "[[:nonascii:]]" thing) - (let ((query (ein:trim-right (subseq thing 0 col) "[\n]"))) + (let ((query (ein:trim-right (cl-subseq thing 0 col) "[\n]"))) (string-match "[]()\",[{}'=: ]$" query (- col 2))))) diff --git a/lisp/ein-completer.el b/lisp/ein-completer.el index 1bc8e1393..c1945fd2b 100644 --- a/lisp/ein-completer.el +++ b/lisp/ein-completer.el @@ -28,8 +28,6 @@ (declare-function ac-cursor-on-diable-face-p "auto-complete") -(eval-when-compile (require 'cl)) - (require 'ein-core) (require 'ein-log) (require 'ein-subpackages) @@ -83,10 +81,23 @@ (list :complete_reply (cons #'ein:completer-finish-completing '(:expand nil))) #'ignore)) - (ein:kernel-complete kernel - (thing-at-point 'line) - (current-column) - callbacks errback)) + (multiple-value-bind (code pos) (ein:get-completion-context (ein:$kernel-api-version kernel)) + (ein:log 'debug (format "EIN:COMPLETER-COMPLETE Code block: %s at position :%s" code pos)) + (ein:kernel-complete kernel + code ;; (thing-at-point 'line) + pos ;; (current-column) + callbacks errback))) + +(defun ein:get-completion-context (api-version) + (cond ((< api-version 5) + (values (thing-at-point 'line) (current-column))) + ((and (ein:get-kernel) (ein:get-cell-at-point)) + (let* ((cell (ein:get-cell-at-point)) + (code (ein:cell-get-text cell)) + (beg (ein:cell-input-pos-min cell))) + (values code (- (point) beg)))) + ((ein:get-kernel) + (values (buffer-string) (1- (point)))))) ;;; Retrieving Python Object Info (defun ein:completions--reset-oinfo-cache (kernel) @@ -97,45 +108,60 @@ (ein:completions--reset-oinfo-cache kernel)) (defun ein:completions-get-cached (partial oinfo-cache) - (loop for candidate being the hash-keys of oinfo-cache - when (string-prefix-p partial candidate) - collect candidate)) + (cl-loop for candidate being the hash-keys of oinfo-cache + when (string-prefix-p partial candidate) + collect candidate)) -(defun ein:completions--get-oinfo (obj) +(defun ein:completions--get-oinfo (objs) (let ((d (deferred:new #'identity)) (kernel (ein:get-kernel))) - (if (ein:kernel-live-p kernel) - (ein:kernel-execute - kernel - (format "__ein_print_object_info_for(__ein_maybe_undefined_object(r\"%s\", locals()))" obj) - (list - :output `(,(lambda (d* &rest args) (deferred:callback-post d* args)) . ,d))) - (deferred:callback-post d "kernel not live")) + (ein:case-equal (ein:kernel-language kernel) + (("python") + (if (ein:kernel-live-p kernel) + (ein:kernel-execute + kernel + (format "__ein_generate_oinfo_data(%s, locals())" objs) + (list + :output `(,(lambda (d* &rest args) (deferred:callback-post d* args)) . ,d))) + (deferred:callback-post d "kernel not live")))) d)) -(defun ein:completions--build-oinfo-cache (objs) - (let ((kernel (ein:get-kernel))) - (dolist (o (-non-nil objs)) - (deferred:$ - (deferred:next - (lambda () - (ein:completions--get-oinfo (ein:trim o "\\s-\\|\n\\|\\.")))) - (deferred:nextc it - (lambda (output) - (if (stringp output) - (ein:display-warning output :error) - (ein:completions--prepare-oinfo output o kernel)))))))) - -(defun ein:completions--prepare-oinfo (output obj kernel) +(defvar ein:oinfo-chunk-size 50) + +(defun ein:completions--build-oinfo-cache (objects) + (cl-labels ((object-string (o) + (format "'%s'" (ein:trim o "\\s-\\|\n\\|\\."))) + (to-ostrings (objs) + (s-join ", " (-map #'(lambda (x) (object-string x)) + objs))) + (do-completions (ostrings kernel) + (deferred:$ + (deferred:next + (lambda () + (ein:completions--get-oinfo ostrings))) + (deferred:nextc it + (lambda (output) + (if (stringp output) + (ein:display-warning output :error) + (ein:completions--prepare-oinfo output objects kernel))))))) + (if (< (length objects) ein:oinfo-chunk-size) + (do-completions (format "[%s]" (to-ostrings (-non-nil objects))) (ein:get-kernel)) + (dolist (chunk (-partition-all ein:oinfo-chunk-size (-non-nil objects))) + (do-completions (format "[%s]" (to-ostrings chunk)) (ein:get-kernel)))))) + + +(defun ein:completions--prepare-oinfo (output objs kernel) (condition-case err - (destructuring-bind (msg-type content _) output + (cl-destructuring-bind (msg-type content _) output (ein:case-equal msg-type (("stream" "display_data" "pyout" "execute_result") (ein:aif (plist-get content :text) - (let ((oinfo (ein:json-read-from-string it))) - (unless (string= (plist-get oinfo :string_form) "None") - (setf (gethash obj (ein:$kernel-oinfo-cache kernel)) - oinfo))))) + (let ((all-oinfo (ein:json-read-from-string it))) + (cl-loop for oinfo in all-oinfo + for obj in objs + doing (unless (string= (plist-get oinfo :string_form) "None") + (setf (gethash obj (ein:$kernel-oinfo-cache kernel)) + oinfo)))))) (("error" "pyerr") (ein:log 'verbose "ein:completions--prepare-oinfo: %s" (plist-get content :traceback))))) @@ -143,8 +169,7 @@ (ein:log 'verbose "ein:completions--prepare-oinfo: [%s]" (error-message-string err)) (let (eval-expression-print-length eval-expression-print-level) - (prin1 output #'external-debugging-output)) - (setf (gethash obj (ein:$kernel-oinfo-cache kernel)) :json-false)))) + (prin1 output #'external-debugging-output))))) ;;; Support for Eldoc diff --git a/lisp/ein-connect.el b/lisp/ein-connect.el index 598d828d9..3120a1c7e 100644 --- a/lisp/ein-connect.el +++ b/lisp/ein-connect.el @@ -1,4 +1,4 @@ -;;; ein-connect.el --- Connect external buffers to IPython +;;; ein-connect.el --- Connect external buffers to IPython -*- lexical-binding: t -*- ;; Copyright (C) 2012- Takafumi Arakaki @@ -49,7 +49,7 @@ of OPTION: no : Do not save buffer." (if (not (buffer-modified-p)) t - (case option + (cl-case option (ask (when (y-or-n-p "Save buffer? ") (save-buffer) t)) @@ -172,10 +172,10 @@ notebooks." (defun ein:connect-to-notebook (nbpath &optional buffer no-reconnection) "Connect any buffer to notebook and its kernel." (interactive (list (ein:notebooklist-ask-path "notebook"))) - (multiple-value-bind (url-or-port path) (ein:notebooklist-parse-nbpath nbpath) + (cl-multiple-value-bind (url-or-port path) (ein:notebooklist-parse-nbpath nbpath) (ein:notebook-open url-or-port path nil - (apply-partially - (lambda (buffer* no-reconnection* notebook created) + (apply-partially + (lambda (buffer* no-reconnection* notebook _created) (ein:connect-buffer-to-notebook notebook buffer* no-reconnection*)) (or buffer (current-buffer)) no-reconnection)))) @@ -217,7 +217,7 @@ notebooks." "Evaluate the whole buffer. Note that this will run the code inside the ``if __name__ == \"__main__\":`` block." (interactive) - (lexical-let ((b (current-buffer))) + (let ((b (current-buffer))) (deferred:$ (deferred:next (lambda () @@ -307,11 +307,11 @@ See also: `ein:connect-run-buffer', `ein:connect-eval-buffer'." ;;; Auto-execution (defun ein:connect-assert-connected () - (assert (ein:connect-p ein:%connect%) nil - "Current buffer (%s) is not connected to IPython notebook." - (buffer-name)) - (assert (ein:notebook-live-p (slot-value ein:%connect% 'notebook)) nil - "Connected notebook is not live (probably already closed).")) + (cl-assert (ein:connect-p ein:%connect%) nil + "Current buffer (%s) is not connected to IPython notebook." + (buffer-name)) + (cl-assert (ein:notebook-live-p (slot-value ein:%connect% 'notebook)) nil + "Connected notebook is not live (probably already closed).")) (defun ein:connect-execute-autoexec-cells () "Call `ein:notebook-execute-autoexec-cells' via `after-save-hook'." @@ -398,7 +398,7 @@ notebook." :lighter (:eval (ein:connect-mode-get-lighter)) :keymap ein:connect-mode-map :group 'ein - (case ein:completion-backend + (cl-case ein:completion-backend (ein:use-ac-backend (define-key ein:connect-mode-map "." 'ein:ac-dot-complete) (auto-complete-mode)) diff --git a/lisp/ein-contents-api.el b/lisp/ein-contents-api.el index 47f754656..df99da66f 100644 --- a/lisp/ein-contents-api.el +++ b/lisp/ein-contents-api.el @@ -1,4 +1,4 @@ -;;; ein-contents-api.el --- Interface to Jupyter's Contents API +;;; ein-contents-api.el --- Interface to Jupyter's Contents API -*- lexical-binding: t -*- ;; Copyright (C) 2015 - John Miller @@ -30,7 +30,6 @@ ;;; Code: -(require 'cl) (require 'ein-core) (require 'ein-classes) (require 'ein-utils) @@ -92,15 +91,16 @@ global setting. For global setting and more information, see :success (apply-partially #'ein:content-query-contents--success url-or-port path callback) :error (apply-partially #'ein:content-query-contents--error url-or-port path callback errback iteration))) -(defun* ein:content-query-contents--complete (url-or-port path - &key data symbol-status response - &allow-other-keys - &aux (resp-string (format "STATUS: %s DATA: %s" (request-response-status-code response) data))) +(cl-defun ein:content-query-contents--complete (_url-or-port _path + &key data _symbol-status response + &allow-other-keys + &aux (resp-string (format "STATUS: %s DATA: %s" (request-response-status-code response) data))) (ein:log 'debug "ein:query-contents--complete %s" resp-string)) -(defun* ein:content-query-contents--error (url-or-port path callback errback iteration &key symbol-status response error-thrown data &allow-other-keys) +(cl-defun ein:content-query-contents--error (url-or-port path callback errback iteration + &key symbol-status response error-thrown data &allow-other-keys) (let ((status-code (request-response-status-code response))) ; may be nil! - (case status-code + (cl-case status-code (404 (ein:log 'error "ein:content-query-contents--error %s %s" status-code (plist-get data :message)) (when errback (funcall errback url-or-port status-code))) @@ -125,23 +125,23 @@ global setting. For global setting and more information, see ;; TODO: This is one place to check for redirects - update the url slot if so. ;; Will need to pass the response object and check either request-response-history ;; or request-response-url. -(defun* ein:content-query-contents--success (url-or-port path callback - &key data symbol-status response - &allow-other-keys) +(cl-defun ein:content-query-contents--success (url-or-port path callback + &key data _symbol-status response &allow-other-keys) (let (content) (if (< (ein:notebook-version-numeric url-or-port) 3) (setq content (ein:new-content-legacy url-or-port path data)) (setq content (ein:new-content url-or-port path data))) - (ein:aif response - (setf (ein:$content-url-or-port content) (ein:get-response-redirect it))) + (if (and response + (> (length (request-response-history response)) 0)) + (setf (ein:$content-url-or-port content) (ein:get-response-redirect response))) (when callback (funcall callback content)))) (defun ein:fix-legacy-content-data (data) (if (listp (car data)) - (loop for item in data - collecting - (ein:fix-legacy-content-data item)) + (cl-loop for item in data + collecting + (ein:fix-legacy-content-data item)) (if (string= (plist-get data :path) "") (plist-put data :path (plist-get data :name)) (plist-put data :path (format "%s/%s" (plist-get data :path) (plist-get data :name)))))) @@ -217,44 +217,42 @@ global setting. For global setting and more information, see (defun ein:content-query-hierarchy* (url-or-port path callback sessions depth content) "Returns list (tree) of content objects. CALLBACK accepts tree." - (lexical-let* ((url-or-port url-or-port) - (path path) - (callback callback) - (items (ein:$content-raw-content content)) - (directories (if (< depth ein:content-query-max-depth) - (loop for item in items - with result - until (>= (length result) ein:content-query-max-branch) - if (string= "directory" (plist-get item :type)) - collect (ein:new-content url-or-port path item) - into result - end - finally return result))) - (others (loop for item in items - with c0 - if (not (string= "directory" (plist-get item :type))) - do (setf c0 (ein:new-content url-or-port path item)) - (setf (ein:$content-session-p c0) - (gethash (ein:$content-path c0) sessions)) - and collect c0 - end))) + (let* ((url-or-port url-or-port) + (path path) + (callback callback) + (items (ein:$content-raw-content content)) + (directories (if (< depth ein:content-query-max-depth) + (cl-loop for item in items + with result = nil + until (>= (length result) ein:content-query-max-branch) + do (if (string= "directory" (plist-get item :type)) + (setf result (append result (list (ein:new-content url-or-port path item))))) + finally return result ) )) + (others (cl-loop for item in items + with c0 + if (not (string= "directory" (plist-get item :type))) + do (setf c0 (ein:new-content url-or-port path item) + (ein:$content-session-p c0) + (gethash (ein:$content-path c0) sessions)) + and collect c0 + end))) (deferred:$ (apply #'deferred:parallel - (loop for c0 in directories - collect - (lexical-let ((c0 c0) - (d0 (deferred:new #'identity))) - (ein:content-query-contents - url-or-port - (ein:$content-path c0) - (apply-partially #'ein:content-query-hierarchy* - url-or-port - (ein:$content-path c0) - (lambda (tree) - (deferred:callback-post d0 (cons c0 tree))) - sessions (1+ depth)) - (lambda (&rest ignore) (deferred:callback-post d0 (cons c0 nil)))) - d0))) + (cl-loop for c0 in directories + collect + (let ((c0 c0) + (d0 (deferred:new #'identity))) + (ein:content-query-contents + url-or-port + (ein:$content-path c0) + (apply-partially #'ein:content-query-hierarchy* + url-or-port + (ein:$content-path c0) + (lambda (tree) + (deferred:callback-post d0 (cons c0 tree))) + sessions (1+ depth)) + (lambda (&rest _ignore) (deferred:callback-post d0 (cons c0 nil)))) + d0))) (deferred:nextc it (lambda (tree) (let ((result (append others tree))) @@ -272,7 +270,7 @@ global setting. For global setting and more information, see url-or-port* "" callback* sessions 0) - (lambda (&rest ignore) + (lambda (&rest _ignore) (when callback* (funcall callback* nil))))) url-or-port callback) callback)) @@ -303,12 +301,12 @@ global setting. For global setting and more information, see :error (apply-partially #'ein:content-save-error (ein:content-url content) errcb errcbargs)) (ein:content-save-legacy content callback cbargs))) -(defun* ein:content-save-success (callback cbargs &key status response &allow-other-keys) +(cl-defun ein:content-save-success (callback cbargs &key _status _response &allow-other-keys) ;;(ein:log 'verbose "Saving content successful with status %s" status) (when callback (apply callback cbargs))) -(defun* ein:content-save-error (url errcb errcbargs &key response data &allow-other-keys) +(cl-defun ein:content-save-error (url errcb errcbargs &key response data &allow-other-keys) (ein:log 'error "Content save %s failed %s %s." url (request-response-error-thrown response) (plist-get data :message)) @@ -332,7 +330,7 @@ global setting. For global setting and more information, see :success (apply-partially #'update-content-path-legacy content callback cbargs) :error (apply-partially #'ein:content-rename-error new-path)))) -(defun* update-content-path-legacy (content callback cbargs &key data &allow-other-keys) +(cl-defun update-content-path-legacy (content callback cbargs &key data &allow-other-keys) (setf (ein:$content-path content) (ein:trim-left (format "%s/%s" (plist-get data :path) (plist-get data :name)) "/") (ein:$content-name content) (plist-get data :name) @@ -360,19 +358,19 @@ global setting. For global setting and more information, see :data (json-encode `((path . ,new-path))) :complete #'ein:session-rename--complete)) -(defun* ein:session-rename--complete (&key data response symbol-status - &allow-other-keys - &aux (resp-string (format "STATUS: %s DATA: %s" (request-response-status-code response) data))) +(cl-defun ein:session-rename--complete (&key data response _symbol-status + &allow-other-keys + &aux (resp-string (format "STATUS: %s DATA: %s" (request-response-status-code response) data))) (ein:log 'debug "ein:session-rename--complete %s" resp-string)) -(defun* update-content-path (content callback cbargs &key data &allow-other-keys) +(cl-defun update-content-path (content callback cbargs &key data &allow-other-keys) (setf (ein:$content-path content) (plist-get data :path) (ein:$content-name content) (plist-get data :name) (ein:$content-last-modified content) (plist-get data :last_modified)) (when callback (apply callback cbargs))) -(defun* ein:content-rename-error (path &key response data &allow-other-keys) +(cl-defun ein:content-rename-error (path &key response data &allow-other-keys) (ein:log 'error "Renaming content %s failed %s %s." path (request-response-error-thrown response) (plist-get data :message))) @@ -398,7 +396,7 @@ global setting. For global setting and more information, see :error (apply-partially #'ein:content-query-sessions--error url-or-port callback errback iteration) :sync ein:force-sync)) -(defun* ein:content-query-sessions--success (url-or-port callback &key data &allow-other-keys) +(cl-defun ein:content-query-sessions--success (url-or-port callback &key data &allow-other-keys) (cl-flet ((read-name (nb-json) (if (< (ein:notebook-version-numeric url-or-port) 3) (if (string= (plist-get nb-json :path) "") @@ -410,9 +408,8 @@ global setting. For global setting and more information, see (setf (gethash (read-name (plist-get s :notebook)) session-hash) (cons (plist-get s :id) (plist-get s :kernel))))))) -(defun* ein:content-query-sessions--error (url-or-port callback errback iteration - &key response error-thrown - &allow-other-keys) +(cl-defun ein:content-query-sessions--error (url-or-port callback errback iteration + &key response error-thrown &allow-other-keys) (if (< iteration (if noninteractive 6 3)) (progn (ein:log 'verbose "Retry sessions #%s in response to %s" iteration (request-response-status-code response)) @@ -421,10 +418,9 @@ global setting. For global setting and more information, see (ein:log 'error "ein:content-query-sessions--error %s: ERROR %s DATA %s" url-or-port (car error-thrown) (cdr error-thrown)) (when errback (funcall errback nil)))) -(defun* ein:content-query-sessions--complete (url-or-port callback - &key data response - &allow-other-keys - &aux (resp-string (format "STATUS: %s DATA: %s" (request-response-status-code response) data))) +(cl-defun ein:content-query-sessions--complete (_url-or-port _callback + &key data response &allow-other-keys + &aux (resp-string (format "STATUS: %s DATA: %s" (request-response-status-code response) data))) (ein:log 'debug "ein:query-sessions--complete %s" resp-string)) @@ -481,14 +477,14 @@ global setting. For global setting and more information, see (apply callback cbargs)) :error (apply-partially #'ein:content-query-checkpoints-error content)))) -(defun* ein:content-query-checkpoints-success (content cb cbargs &key data status response &allow-other-keys) +(cl-defun ein:content-query-checkpoints-success (content cb cbargs &key data _status _response &allow-other-keys) (unless (listp (car data)) (setq data (list data))) (setf (ein:$content-checkpoints content) data) (when cb (apply cb content cbargs))) -(defun* ein:content-query-checkpoints-error (content &key symbol-status response &allow-other-keys) +(cl-defun ein:content-query-checkpoints-error (_content &key symbol-status response &allow-other-keys) (ein:log 'error "Content checkpoint operation failed with status %s (%s)." symbol-status response)) @@ -512,7 +508,7 @@ and content format (one of json, text, or base64)." (defun ein:content-upload (path uploaded-file-path &optional url-or-port) - (multiple-value-bind (name type format contents) (ein:get-local-file uploaded-file-path) + (cl-multiple-value-bind (name type format contents) (ein:get-local-file uploaded-file-path) (let* ((content (make-ein:$content :url-or-port (or url-or-port (ein:default-url-or-port)) :name name :path (concat path "/" name) @@ -530,11 +526,11 @@ and content format (one of json, text, or base64)." :headers '(("Content-Type" . "application/json")) :timeout ein:content-query-timeout :data (json-encode data) - :success (lexical-let ((uploaded-file-path uploaded-file-path)) - #'(lambda (&rest -ignore-) (message "File %s succesfully uploaded." uploaded-file-path))) + :success (let ((uploaded-file-path uploaded-file-path)) + #'(lambda (&rest _ignore) (message "File %s succesfully uploaded." uploaded-file-path))) :error (apply-partially #'ein:content-upload-error uploaded-file-path))))) -(cl-defun ein:content-upload-error (path &key symbol-status response &allow-other-keys) +(cl-defun ein:content-upload-error (path &key symbol-status _response &allow-other-keys) (ein:display-warning (format "Could not upload %s. Failed with status %s" path symbol-status))) (provide 'ein-contents-api) diff --git a/lisp/ein-core.el b/lisp/ein-core.el index 9bca8b5da..8f6c76f40 100644 --- a/lisp/ein-core.el +++ b/lisp/ein-core.el @@ -1,4 +1,4 @@ -;;; ein-core.el --- EIN core +;;; ein-core.el --- EIN core -*- lexical-binding: t -*- ;; Copyright (C) 2012 Takafumi Arakaki @@ -26,8 +26,6 @@ ;;; Code: -(eval-when-compile (require 'cl)) - ;; Optional dependency on tramp: (declare-function tramp-make-tramp-file-name "tramp") (declare-function tramp-file-name-localname "tramp") @@ -37,6 +35,9 @@ (require 'ein) ; get autoloaded functions into namespace (require 'ein-utils) +(defvar ein:force-sync) ; defcustom in ein-contents-api which requires this file +(defvar ein:content-query-timeout) ; likewise + (defgroup ein nil "IPython notebook client in Emacs" :group 'applications @@ -176,9 +177,8 @@ the source is in git repository) or elpa version." (replace-regexp-in-string "[ ]" "-" name) name)) -(defun* ein:query-kernelspecs--success (url-or-port callback - &key data symbol-status response - &allow-other-keys) +(cl-defun ein:query-kernelspecs--success (url-or-port callback + &key data _symbol-status _response &allow-other-keys) (let ((ks (list :default (plist-get data :default))) (specs (ein:plist-iter (plist-get data :kernelspecs)))) (setf (gethash url-or-port *ein:kernelspecs*) @@ -196,9 +196,8 @@ the source is in git repository) or elpa version." ks)))))) (when callback (funcall callback))) -(defun* ein:query-kernelspecs--error (url-or-port callback iteration - &key response error-thrown - &allow-other-keys) +(cl-defun ein:query-kernelspecs--error (url-or-port callback iteration + &key response error-thrown &allow-other-keys) (if (< iteration 3) (progn (ein:log 'verbose "Retry kernelspecs #%s in response to %s" iteration (request-response-status-code response)) @@ -207,9 +206,8 @@ the source is in git repository) or elpa version." "ein:query-kernelspecs--error %s: ERROR %s DATA %s" url-or-port (car error-thrown) (cdr error-thrown)) (when callback (funcall callback)))) -(defun* ein:query-kernelspecs--complete (url-or-port &key data response - &allow-other-keys - &aux (resp-string (format "STATUS: %s DATA: %s" (request-response-status-code response) data))) +(cl-defun ein:query-kernelspecs--complete (_url-or-port &key data response &allow-other-keys + &aux (resp-string (format "STATUS: %s DATA: %s" (request-response-status-code response) data))) (ein:log 'debug "ein:query-kernelspecs--complete %s" resp-string)) (defsubst ein:notebook-version-numeric (url-or-port) @@ -230,14 +228,13 @@ the source is in git repository) or elpa version." :sync ein:force-sync :complete (apply-partially #'ein:query-notebook-version--complete url-or-port callback))) -(defun* ein:query-notebook-version--complete (url-or-port callback - &key data response - &allow-other-keys - &aux (resp-string (format "STATUS: %s DATA: %s" (request-response-status-code response) data))) +(cl-defun ein:query-notebook-version--complete (url-or-port callback + &key data response &allow-other-keys + &aux (resp-string (format "STATUS: %s DATA: %s" (request-response-status-code response) data))) (ein:log 'debug "ein:query-notebook-version--complete %s" resp-string) (ein:aif (plist-get data :version) (setf (gethash url-or-port *ein:notebook-version*) it) - (case (request-response-status-code response) + (cl-case (request-response-status-code response) (404 (ein:log 'warn "notebook version api not implemented") (setf (gethash url-or-port *ein:notebook-version*) "2.0.0")) (t (ein:log 'warn "notebook version currently unknowable")))) @@ -296,13 +293,13 @@ the host named MY-HOSTNAME. Adapted from `slime-create-filename-translator'." (require 'tramp) - (lexical-let ((remote-host remote-host) - (username (or username (user-login-name)))) + (let ((remote-host remote-host) + (username (or username (user-login-name)))) (list (lambda (emacs-filename) (tramp-file-name-localname (tramp-dissect-file-name emacs-filename))) (lambda (python-filename) - (ein:make-tramp-file-name username remote-host python-filename))))) + (ein:make-tramp-file-name username remote-host python-filename))))) @@ -322,9 +319,9 @@ as `defgeneric' in EIEIO, but it takes no argument. Actual implementation is chosen based on context (buffer, point, etc.). This helps writing generic commands which requires same object but can operate in different contexts." - (loop for func in func-list - if (and (functionp func) (funcall func)) - return it)) + (cl-loop for func in func-list + if (and (functionp func) (funcall func)) + return it)) (defun ein:get-url-or-port () (ein:generic-getter '(ein:get-url-or-port--notebooklist diff --git a/lisp/ein-dev.el b/lisp/ein-dev.el index a94e6babe..b1ea334c4 100644 --- a/lisp/ein-dev.el +++ b/lisp/ein-dev.el @@ -1,4 +1,4 @@ -;;; ein-dev.el --- Development tools +;;; ein-dev.el --- Development tools -*- lexical-binding: t -*- ;; Copyright (C) 2012- Takafumi Arakaki @@ -25,7 +25,6 @@ ;;; Code: -(eval-when-compile (require 'cl)) (declare-function rst-shift-region "rst") (require 'ein-notebook) @@ -65,13 +64,13 @@ (load "ein-notebook") ; ... but make sure it will be defined first. (ein:load-files "^ein-.*\\.el$")) -(defun* ein:dev-require-all (&key (ignore-p #'ignore)) - (loop for f in (directory-files ein:source-dir nil "^ein-.*\\.el$") - unless (or (equal f "ein-pkg.el") - (equal f "ein-autoloads.el") - (equal f "ein-smartrep.el") - (funcall ignore-p f)) - do (require (intern (file-name-sans-extension f)) nil t)) +(cl-defun ein:dev-require-all (&key (ignore-p #'ignore)) + (cl-loop for f in (directory-files ein:source-dir nil "^ein-.*\\.el$") + unless (or (equal f "ein-pkg.el") + (equal f "ein-autoloads.el") + (equal f "ein-smartrep.el") + (funcall ignore-p f)) + do (require (intern (file-name-sans-extension f)) nil t)) ;; For `widget-button-press': (require 'wid-edit nil t)) @@ -123,11 +122,11 @@ callback (`websocket-callback-debug-on-error') is enabled." ;; (setq deferred:debug-on-signal t) ;; (setq deferred:debug t) (setq request-log-level (quote debug)) - (lexical-let ((curl-trace (concat temporary-file-directory "curl-trace"))) + (let ((curl-trace (concat temporary-file-directory "curl-trace"))) (nconc request-curl-options `("--trace-ascii" ,curl-trace)) - (add-function :after + (add-function :after (symbol-function 'request--curl-callback) - (lambda (&rest args) + (lambda (&rest _args) (if (file-readable-p curl-trace) (with-temp-buffer (insert-file-contents curl-trace) @@ -208,9 +207,9 @@ callback (`websocket-callback-debug-on-error') is enabled." (defun ein:dev-sys-info--lib (name) (let* ((libsym (intern-soft name)) - (version-var (loop for fmt in '("%s-version" "%s:version") - if (intern-soft (format fmt name)) - return it)) + (version-var (cl-loop for fmt in '("%s-version" "%s:version") + if (intern-soft (format fmt name)) + return it)) (version (symbol-value version-var))) (list :name name :path (ein:aand (locate-library name) (abbreviate-file-name it)) @@ -219,9 +218,9 @@ callback (`websocket-callback-debug-on-error') is enabled." :version version))) (defun ein:dev-dump-vars (names) - (loop for var in names - collect (intern (format ":%s" var)) - collect (symbol-value (intern (format "ein:%s" var))))) + (cl-loop for var in names + collect (intern (format ":%s" var)) + collect (symbol-value (intern (format "ein:%s" var))))) (defun ein:dev-stdout-program (command args) "Safely call COMMAND with ARGS and return its stdout." @@ -232,10 +231,10 @@ callback (`websocket-callback-debug-on-error') is enabled." (buffer-string)))) (defsubst ein:dev-packages () - (lexical-let (result) + (let (result) (cl-letf (((symbol-function 'define-package) (lambda (&rest args) - (setq result (mapcar (lambda (x) (symbol-name (first x))) (nth 3 args)))))) + (setq result (mapcar (lambda (x) (symbol-name (car x))) (nth 3 args)))))) (load "ein-pkg") result))) diff --git a/lisp/ein-events.el b/lisp/ein-events.el index 3207bd2fa..932c0c4e3 100644 --- a/lisp/ein-events.el +++ b/lisp/ein-events.el @@ -25,7 +25,6 @@ ;;; Code: -(eval-when-compile (require 'cl)) (require 'eieio) (require 'ein-core) @@ -52,7 +51,7 @@ When EVENT-TYPE is triggered on the event handler EVENTS, CALLBACK is called. CALLBACK must take two arguments: ARG as the first argument and DATA, which is passed via `ein:events-trigger', as the second." - (assert (symbolp event-type) t "%s not symbol" event-type) + (cl-assert (symbolp event-type) t "%s not symbol" event-type) (let* ((table (slot-value events 'callbacks)) (cbs (gethash event-type table))) (push (cons callback arg) cbs) diff --git a/lisp/ein-file.el b/lisp/ein-file.el index faa6147f3..f51dc71c1 100644 --- a/lisp/ein-file.el +++ b/lisp/ein-file.el @@ -40,6 +40,22 @@ (ein:notebooklist-parse-nbpath (ein:notebooklist-ask-path "file"))) (ein:content-query-contents url-or-port path #'ein:file-open-finish nil)) +(defun ein:file-delete (url-or-port path) + (ein:query-singleton-ajax + (list 'file-delete url-or-port path) + (ein:content-url* url-or-port path) + :type "DELETE" + :timeout ein:content-query-timeout + :parser #'ein:json-read + :sync ein:force-sync + :success (lexical-let ((path path)) + #'(lambda (&rest ignore) (ein:notebooklist-reload) + (message "Successful deleted file: %s" path))) + :error (lexical-let ((path path)) + #'(lambda (&rest ignore) (ein:notebooklist-reload) + (message "Delete file %s failed." path))) + )) + (defun ein:file-open-finish (content) (with-current-buffer (get-buffer-create (ein:file-buffer-name (ein:$content-url-or-port content) (ein:$content-path content))) @@ -65,4 +81,3 @@ t)) (provide 'ein-file) - diff --git a/lisp/ein-helm.el b/lisp/ein-helm.el index d3c5c072f..65a5c6321 100644 --- a/lisp/ein-helm.el +++ b/lisp/ein-helm.el @@ -25,8 +25,6 @@ ;;; Code: -(eval-when-compile (require 'cl)) - (declare-function anything-other-buffer "anything") (declare-function helm-other-buffer "helm") diff --git a/lisp/ein-hy.el b/lisp/ein-hy.el index a08ceb0d5..ab2d04343 100644 --- a/lisp/ein-hy.el +++ b/lisp/ein-hy.el @@ -23,7 +23,6 @@ ;;; Code: -(require 'cl) (require 'ein-classes) (cl-defmethod ein:cell-insert-prompt ((cell ein:hy-codecell)) diff --git a/lisp/ein-inspector.el b/lisp/ein-inspector.el index ad24fe50d..432954e4c 100644 --- a/lisp/ein-inspector.el +++ b/lisp/ein-inspector.el @@ -40,7 +40,7 @@ (list kernel object))))) (defun ein:prepare-inspector (packed _msg-type content _metadata) - (destructuring-bind (_kernel oname) + (cl-destructuring-bind (_kernel oname) packed (ein:aif (or (plist-get content :text) (plist-get content :data)) (let ((oinfo (ein:json-read-from-string it))) @@ -89,11 +89,11 @@ (let ((inhibit-read-only t)) (erase-buffer)) (remove-overlays) - (lexical-let* ((type (plist-get oinfo :type)) - (repr (plist-get oinfo :repr)) - (sfile (plist-get oinfo :source_file)) - (slines (last (plist-get oinfo :source_lines))) - (info-str (format "%s = {%s} %s" name type repr))) + (let* ((type (plist-get oinfo :type)) + (repr (plist-get oinfo :repr)) + (sfile (plist-get oinfo :source_file)) + (slines (last (plist-get oinfo :source_lines))) + (info-str (format "%s = {%s} %s" name type repr))) (if sfile (widget-create 'link :notify diff --git a/lisp/ein-ipdb.el b/lisp/ein-ipdb.el index 43eb15d97..a8edf3c22 100644 --- a/lisp/ein-ipdb.el +++ b/lisp/ein-ipdb.el @@ -30,12 +30,22 @@ (defvar ein:ipdb-buffer-active-kernel nil) (defvar ein:ipdb-buffer-prompt nil) -(defstruct ein:$ipdb-session +(cl-defstruct ein:$ipdb-session buffer notebook-buffer kernel current-payload) +(defun ein:ipbd () + "Convenience function that will launch the ipython debugger, +assuming there is an active kernel associated with the current +buffer. For more information see the %debug magic documentation +in ipython." + (interactive) + (ein:shared-output-eval-string (ein:get-kernel) + "%debug" + nil)) + (defun ein:find-or-create-ipdb-session (kernel &optional buffer) (ein:aif (gethash (ein:$kernel-kernel-id kernel) *ein:ipdb-sessions*) it @@ -104,7 +114,7 @@ (remhash ein:ipdb-buffer-active-kernel *ein:ipdb-sessions*)))) (defun ein:ipdb--handle-iopub-reply (kernel packet) - (destructuring-bind + (cl-destructuring-bind (&key content metadata parent_header header &allow-other-keys) (ein:json-read-from-string packet) (let ((msg-type (plist-get header :msg_type))) @@ -128,7 +138,7 @@ (defun ein:ipdb-input-sender (proc input) (with-current-buffer (process-buffer proc) - (assert (not (null ein:ipdb-buffer-active-kernel)) t "No active kernel associated with this buffer %s.") + (cl-assert (not (null ein:ipdb-buffer-active-kernel)) t "No active kernel associated with this buffer %s.") (let* ((session (gethash ein:ipdb-buffer-active-kernel *ein:ipdb-sessions*)) (buffer-read-only nil) (kernel (ein:$ipdb-session-kernel session)) diff --git a/lisp/ein-jupyter.el b/lisp/ein-jupyter.el index 29cafc959..d6865a0ea 100644 --- a/lisp/ein-jupyter.el +++ b/lisp/ein-jupyter.el @@ -1,4 +1,4 @@ -;;; ein-jupyter.el --- Manage the jupyter notebook server +;;; ein-jupyter.el --- Manage the jupyter notebook server -*- lexical-binding: t -*- ;; Copyright (C) 2017 John M. Miller @@ -82,16 +82,16 @@ Changing this to `jupyter-notebook' requires customizing `ein:jupyter-server-use (condition-case err (mapcar (lambda (x) `(const :tag ,(cdr x) ,(car x))) - (loop - for (k . spec) in - (alist-get - 'kernelspecs - (let ((json-object-type 'alist)) - (json-read-from-string - (shell-command-to-string - (format "%s kernelspec list --json" - ein:jupyter-default-server-command))))) - collect `(,k . ,(alist-get 'display_name (alist-get 'spec spec))))) + (cl-loop + for (k . spec) in + (alist-get + 'kernelspecs + (let ((json-object-type 'alist)) + (json-read-from-string + (shell-command-to-string + (format "%s kernelspec list --json" + ein:jupyter-default-server-command))))) + collect `(,k . ,(alist-get 'display_name (alist-get 'spec spec))))) (error (ein:log 'warn "ein:jupyter-default-kernel: %s" err) '((string :tag "Ask")))))) @@ -127,7 +127,7 @@ Changing this to `jupyter-notebook' requires customizing `ein:jupyter-server-use (re-search-backward (format "Process %s" *ein:jupyter-server-process-name*) nil "") ;; important if we start-stop-start (when (re-search-forward "\\([[:alnum:]]+\\) is\\( now\\)? running" nil t) - (let ((hub-p (search "jupyterhub" (downcase (match-string 1))))) + (let ((hub-p (cl-search "jupyterhub" (downcase (match-string 1))))) (when (re-search-forward "\\(https?://[^:]*:[0-9]+\\)\\(?:/\\?token=\\([[:alnum:]]+\\)\\)?" nil t) (let ((raw-url (match-string 1)) (token (or (match-string 2) (and (not hub-p) "")))) @@ -144,7 +144,7 @@ call to `ein:notebooklist-login' and once authenticated open the notebooklist bu via a call to `ein:notebooklist-open'." (interactive) (when (ein:jupyter-server-process) - (multiple-value-bind (url-or-port password) (ein:jupyter-server-conn-info) + (cl-multiple-value-bind (url-or-port _password) (ein:jupyter-server-conn-info) (ein:notebooklist-login url-or-port callback)))) (defsubst ein:set-process-sentinel (proc url-or-port) @@ -163,7 +163,7 @@ our singleton jupyter server process here." ;;;###autoload (defun ein:jupyter-server-start (server-cmd-path notebook-directory - &optional no-login-p login-callback port) + &optional no-login-p login-callback port) "Start SERVER-CMD_PATH with `--notebook-dir' NOTEBOOK-DIRECTORY. Login after connection established unless NO-LOGIN-P is set. LOGIN-CALLBACK takes two arguments, the buffer created by ein:notebooklist-open--finish, and the url-or-port argument of ein:notebooklist-open*. This command opens an asynchronous process running the jupyter @@ -193,8 +193,8 @@ the log of the running jupyter server." (read-directory-name "Notebook directory: " (or *ein:last-jupyter-directory* ein:jupyter-default-notebook-directory)))) - (list server-cmd-path notebook-directory nil (lambda (buffer url-or-port) - (pop-to-buffer buffer))))) + (list server-cmd-path notebook-directory nil #'(lambda (buffer _url-or-port) + (pop-to-buffer buffer))))) (unless (and (stringp server-cmd-path) (file-exists-p server-cmd-path) (file-executable-p server-cmd-path)) @@ -215,18 +215,18 @@ the log of the running jupyter server." "--port-retries" "0"))))) (when (eql system-type 'windows-nt) (accept-process-output proc (/ ein:jupyter-server-run-timeout 1000))) - (loop repeat 30 - until (car (ein:jupyter-server-conn-info ein:jupyter-server-buffer-name)) - do (sleep-for 0 500) - finally do - (unless (car (ein:jupyter-server-conn-info ein:jupyter-server-buffer-name)) - (ein:log 'warn "Jupyter server failed to start, cancelling operation") - (ein:jupyter-server-stop t))) + (cl-loop repeat 30 + until (car (ein:jupyter-server-conn-info ein:jupyter-server-buffer-name)) + do (sleep-for 0 500) + finally do + (unless (car (ein:jupyter-server-conn-info ein:jupyter-server-buffer-name)) + (ein:log 'warn "Jupyter server failed to start, cancelling operation") + (ein:jupyter-server-stop t))) (when (and (not no-login-p) (ein:jupyter-server-process)) (unless login-callback (setq login-callback #'ignore)) - (add-function :after login-callback - (apply-partially (lambda (proc* buffer url-or-port) + (add-function :after (var login-callback) + (apply-partially (lambda (proc* _buffer url-or-port) (ein:set-process-sentinel proc* url-or-port)) proc)) (ein:jupyter-server-login-and-open login-callback)))) @@ -248,20 +248,20 @@ the log of the running jupyter server." ;;;###autoload (defun ein:jupyter-server-stop (&optional force log) (interactive) - (ein:and-let* ((url-or-port (first (ein:jupyter-server-conn-info))) - (_ok (or force (y-or-n-p "Stop server and close notebooks?")))) + (ein:and-let* ((url-or-port (car (ein:jupyter-server-conn-info))) + (ok (or force (y-or-n-p "Stop server and close notebooks?")))) (ein:notebook-close-notebooks t) - (loop repeat 10 - do (ein:query-running-process-table) - until (zerop (hash-table-count ein:query-running-process-table)) - do (sleep-for 0 500)) + (cl-loop repeat 10 + do (ein:query-running-process-table) + until (zerop (hash-table-count ein:query-running-process-table)) + do (sleep-for 0 500)) (if (eq system-type 'windows-nt) (progn (ein:undocumented-shutdown url-or-port) (ein:aif (ein:jupyter-server-process) (delete-process it))) - (lexical-let* ((proc (ein:jupyter-server-process)) - (pid (process-id proc))) + (let* ((proc (ein:jupyter-server-process)) + (pid (process-id proc))) (ein:log 'info "Signaled %s with pid %s" proc pid) (signal-process pid 15) (run-at-time 2 nil diff --git a/lisp/ein-jupyterhub.el b/lisp/ein-jupyterhub.el index 481cb8ef7..b0e695f53 100644 --- a/lisp/ein-jupyterhub.el +++ b/lisp/ein-jupyterhub.el @@ -36,14 +36,14 @@ (defvar *ein:jupyterhub-connections* (make-hash-table :test #'equal)) -(defstruct ein:$jh-conn +(cl-defstruct ein:$jh-conn "Data representing a connection to a jupyterhub server." url-or-port version user token) -(defstruct ein:$jh-user +(cl-defstruct ein:$jh-user "A jupyterhub user, per https://jupyterhub.readthedocs.io/en/latest/_static/rest-api/index.html#/definitions/User" name admin @@ -77,7 +77,7 @@ (ein:websocket-store-cookie c host-port (car (url-path-and-query parsed-url)) securep)))) -(defun* ein:jupyterhub--login-complete (dobj conn &key response &allow-other-keys) +(cl-defun ein:jupyterhub--login-complete (dobj conn &key response &allow-other-keys) (deferred:callback-post dobj (list conn response))) (defmacro ein:jupyterhub--add-header (header) @@ -110,6 +110,17 @@ (apply ,cb (request-response-data (plist-get args :response)) ,cbargs)) my-settings))) +(defsubst ein:jupyterhub--query-login (callback username password conn) + (ein:jupyterhub-query + (ein:$jh-conn-url-or-port conn) + (ein:url (ein:$jh-conn-url-or-port conn) "hub/login") + #'ein:jupyterhub--receive-login + `(,callback ,username ,password ,conn) + ;; :type "POST" ;; no type here else redirect will use POST + :parser #'ignore + :data `(("username" . ,username) + ("password" . ,password)))) + (defun ein:jupyterhub--receive-version (data url-or-port callback username password) (let ((conn (make-ein:$jh-conn :url-or-port url-or-port @@ -134,29 +145,22 @@ (ein:jupyterhub-user-path (ein:$jh-conn-url-or-port conn)) nil nil callback)))) +(defsubst ein:jupyterhub--query-user (callback username password conn iteration) + (ein:jupyterhub-query + (ein:$jh-conn-url-or-port conn) + (ein:jupyterhub-api-path (ein:$jh-conn-url-or-port conn) "users" username) + #'ein:jupyterhub--receive-user + `(,callback ,username ,password ,conn ,iteration) + :type "GET" + :parser #'ein:json-read)) + (defun ein:jupyterhub--receive-login (_data callback username password conn) (ein:jupyterhub--store-cookies conn) (ein:jupyterhub--query-user callback username password conn 0)) -(defun ein:jupyterhub--receive-token (data callback username password conn) - (setf (ein:$jh-conn-token conn) (plist-get data :token)) - (ein:jupyterhub--query-server callback username password conn)) - (defun ein:jupyterhub--receive-server (_data callback username password conn) (ein:jupyterhub--query-user callback username password conn 1)) -(defun ein:jupyterhub--query-token (callback username password conn) - (ein:jupyterhub-query - (ein:$jh-conn-url-or-port conn) - (ein:jupyterhub-api-path (ein:$jh-conn-url-or-port conn) - "authorizations/token") - #'ein:jupyterhub--receive-token - `(,callback ,username ,password ,conn) - :type "POST" - :data (json-encode `((:username . ,username) - (:password . ,password))) - :parser #'ein:json-read)) - (defsubst ein:jupyterhub--query-server (callback username password conn) (ein:jupyterhub-query (ein:$jh-conn-url-or-port conn) @@ -167,25 +171,21 @@ :type "POST" :parser #'ein:json-read)) -(defsubst ein:jupyterhub--query-user (callback username password conn iteration) - (ein:jupyterhub-query - (ein:$jh-conn-url-or-port conn) - (ein:jupyterhub-api-path (ein:$jh-conn-url-or-port conn) "users" username) - #'ein:jupyterhub--receive-user - `(,callback ,username ,password ,conn ,iteration) - :type "GET" - :parser #'ein:json-read)) +(defun ein:jupyterhub--receive-token (data callback username password conn) + (setf (ein:$jh-conn-token conn) (plist-get data :token)) + (ein:jupyterhub--query-server callback username password conn)) -(defsubst ein:jupyterhub--query-login (callback username password conn) +(defun ein:jupyterhub--query-token (callback username password conn) (ein:jupyterhub-query (ein:$jh-conn-url-or-port conn) - (ein:url (ein:$jh-conn-url-or-port conn) "hub/login") - #'ein:jupyterhub--receive-login + (ein:jupyterhub-api-path (ein:$jh-conn-url-or-port conn) + "authorizations/token") + #'ein:jupyterhub--receive-token `(,callback ,username ,password ,conn) - ;; :type "POST" ;; no type here else redirect will use POST - :parser #'ignore - :data `(("username" . ,username) - ("password" . ,password)))) + :type "POST" + :data (json-encode `((:username . ,username) + (:password . ,password))) + :parser #'ein:json-read)) (defsubst ein:jupyterhub--query-version (url-or-port callback username password) (ein:jupyterhub-query @@ -201,8 +201,8 @@ "Log on to a jupyterhub server using PAM authentication. Requires jupyterhub version 0.8 or greater. CALLBACK takes two arguments, the resulting buffer and the singleuser url-or-port" (interactive (let ((url-or-port (ein:notebooklist-ask-url-or-port)) (pam-plist (ein:notebooklist-ask-user-pw-pair "User" "Password"))) - (loop for (user pw) on pam-plist by (function cddr) - return (list url-or-port (symbol-name user) pw (lambda (buffer _url-or-port) (pop-to-buffer buffer)))))) + (cl-loop for (user pw) on pam-plist by (function cddr) + return (list url-or-port (symbol-name user) pw (lambda (buffer _url-or-port) (pop-to-buffer buffer)))))) (ein:jupyterhub--query-version url-or-port callback username password)) (provide 'ein-jupyterhub) diff --git a/lisp/ein-kernel.el b/lisp/ein-kernel.el index 93c310b8b..9f5ef911a 100644 --- a/lisp/ein-kernel.el +++ b/lisp/ein-kernel.el @@ -1,4 +1,4 @@ -;;; ein-kernel.el --- Communicate with IPython notebook server +;;; ein-kernel.el --- Communicate with IPython notebook server -*- lexical-binding: t -*- ;; Copyright (C) 2012- Takafumi Arakaki @@ -28,7 +28,6 @@ ;;; Code: -(eval-when-compile (require 'cl)) (require 'ansi-color) (require 'ein-core) @@ -86,6 +85,9 @@ "Destructor for `ein:$kernel'." (ein:kernel-disconnect kernel)) +(defun ein:kernel-language (kernel) + "Return a string naming the language used by kernel `kernel'. Typical return values might be 'python', or 'julia', or 'R' (among others)." + (ein:$kernelspec-language (ein:$kernel-kernelspec kernel))) (defun ein:kernel--get-msg (kernel msg-type content) (list @@ -100,7 +102,7 @@ :content content :parent_header (make-hash-table))) -(defun* ein:kernel-session-p (kernel callback &optional iteration) +(cl-defun ein:kernel-session-p (kernel callback &optional iteration) "Don't make any changes on the server side. CALLBACK with arity 2, kernel and a boolean whether session exists on server." (unless iteration (setq iteration 0)) @@ -115,13 +117,12 @@ :success (apply-partially #'ein:kernel-session-p--success kernel session-id callback) :error (apply-partially #'ein:kernel-session-p--error kernel callback iteration)))) -(defun* ein:kernel-session-p--complete (session-id &key data response - &allow-other-keys - &aux (resp-string (format "STATUS: %s DATA: %s" (request-response-status-code response) data))) +(cl-defun ein:kernel-session-p--complete (_session-id &key data response &allow-other-keys + &aux (resp-string (format "STATUS: %s DATA: %s" (request-response-status-code response) data))) (ein:log 'debug "ein:kernel-session-p--complete %s" resp-string)) -(defun* ein:kernel-session-p--error (kernel callback iteration &key error-thrown symbol-status data &allow-other-keys) - (if (ein:aand (plist-get data :message) (search "not found" it)) +(cl-defun ein:kernel-session-p--error (kernel callback iteration &key error-thrown _symbol-status data &allow-other-keys) + (if (ein:aand (plist-get data :message) (cl-search "not found" it)) (when callback (funcall callback kernel nil)) (let* ((max-tries 3) (tries-left (1- (- max-tries iteration)))) @@ -130,13 +131,13 @@ (if (> tries-left 0) (ein:kernel-session-p kernel callback (1+ iteration)))))) -(defun* ein:kernel-session-p--success (kernel session-id callback &key data &allow-other-keys) +(cl-defun ein:kernel-session-p--success (kernel session-id callback &key data &allow-other-keys) (let ((session-p (equal (plist-get data :id) session-id))) (ein:log 'verbose "ein:kernel-session-p--success: session-id=%s session-p=%s" session-id session-p) (when callback (funcall callback kernel session-p)))) -(defun* ein:kernel-restart-session (kernel) +(cl-defun ein:kernel-restart-session (kernel) "Server side delete of KERNEL session and subsequent restart with all new state" (ein:kernel-delete-session kernel @@ -147,7 +148,7 @@ (ein:events-trigger (ein:$kernel-events kernel) 'status_restarted.Kernel)))))) -(defun* ein:kernel-retrieve-session (kernel &optional iteration callback) +(cl-defun ein:kernel-retrieve-session (kernel &optional iteration callback) "Formerly ein:kernel-start, but that was misnomer because 1. the server really starts a session (and an accompanying kernel), and 2. it may not even start a session if one exists for the same path. If 'picking up from where we last left off', that is, we restart emacs and reconnect to same server, jupyter will hand us back the original, still running session. @@ -188,12 +189,11 @@ CALLBACK of arity 1, the kernel. :success (apply-partially #'ein:kernel-retrieve-session--success kernel callback) :error (apply-partially #'ein:kernel-retrieve-session--error kernel iteration callback))))) -(defun* ein:kernel-retrieve-session--complete (kernel callback &key data response - &allow-other-keys - &aux (resp-string (format "STATUS: %s DATA: %s" (request-response-status-code response) data))) +(cl-defun ein:kernel-retrieve-session--complete (_kernel _callback &key data response &allow-other-keys + &aux (resp-string (format "STATUS: %s DATA: %s" (request-response-status-code response) data))) (ein:log 'debug "ein:kernel-retrieve-session--complete %s" resp-string)) -(defun* ein:kernel-retrieve-session--error (kernel iteration callback &key error-thrown symbol-status &allow-other-keys) +(cl-defun ein:kernel-retrieve-session--error (kernel iteration callback &key error-thrown _symbol-status &allow-other-keys) (let* ((max-tries 3) (tries-left (1- (- max-tries iteration)))) (ein:log 'verbose "ein:kernel-retrieve-session--error [%s], %s tries left" @@ -202,11 +202,11 @@ CALLBACK of arity 1, the kernel. (if (> tries-left 0) (ein:kernel-retrieve-session kernel (1+ iteration) callback)))) -(defun* ein:kernel-retrieve-session--success (kernel callback &key data &allow-other-keys) +(cl-defun ein:kernel-retrieve-session--success (kernel callback &key data &allow-other-keys) (let ((session-id (plist-get data :id))) (if (plist-get data :kernel) (setq data (plist-get data :kernel))) - (destructuring-bind (&key id &allow-other-keys) data + (cl-destructuring-bind (&key id &allow-other-keys) data (ein:log 'verbose "ein:kernel-retrieve-session--success: kernel-id=%s session-id=%s" id session-id) (setf (ein:$kernel-kernel-id kernel) id) @@ -256,7 +256,7 @@ See https://github.com/ipython/ipython/pull/3307" (let ((cookie (ein:query-get-cookie host "/"))) (ein:websocket-send channel cookie))) -(defun ein:kernel--handle-websocket-reply (kernel ws frame) +(defun ein:kernel--handle-websocket-reply (kernel _ws frame) (ein:and-let* ((packet (websocket-frame-payload frame)) (channel (plist-get (ein:json-read-from-string packet) :channel))) (cond ((string-equal channel "iopub") @@ -299,7 +299,7 @@ See https://github.com/ipython/ipython/pull/3307" (error "Api version %s unsupported" (ein:$kernel-api-version kernel))) (t (ein:start-single-websocket kernel callback)))) -(defun ein:kernel-on-connect (kernel content -metadata-not-used-) +(defun ein:kernel-on-connect (_kernel _content _metadata) (ein:log 'info "Kernel connect_request_reply received.")) (defun ein:kernel-run-after-start-hook (kernel) @@ -352,7 +352,7 @@ CONTENT and METADATA are given by `object_info_reply' message. `object_info_reply' message is documented here: http://ipython.org/ipython-doc/dev/development/messaging.html#object-information " - (assert (ein:kernel-live-p kernel) nil "object_info_reply: Kernel is not active.") + (cl-assert (ein:kernel-live-p kernel) nil "object_info_reply: Kernel is not active.") (when objname (if (<= (ein:$kernel-api-version kernel) 2) (error "Api version %s unsupported" (ein:$kernel-api-version kernel))) @@ -372,13 +372,13 @@ http://ipython.org/ipython-doc/dev/development/messaging.html#object-information (ein:websocket-send-shell-channel kernel msg) (ein:kernel-set-callbacks-for-msg kernel msg-id callbacks)))) -(defun* ein:kernel-execute (kernel code &optional callbacks - &key - (silent t) - (store-history t) - (user-expressions (make-hash-table)) - (allow-stdin t) - (stop-on-error nil)) +(cl-defun ein:kernel-execute (kernel code &optional callbacks + &key + (silent t) + (store-history t) + (user-expressions (make-hash-table)) + (allow-stdin t) + (stop-on-error nil)) "Execute CODE on KERNEL. When calling this method pass a CALLBACKS structure of the form: @@ -438,7 +438,7 @@ Sample implementations ;; call signature becomes something like: ;; (funcall FUNCTION [ARG ...] CONTENT METADATA) - (assert (ein:kernel-live-p kernel) nil "execute_reply: Kernel is not active.") + (cl-assert (ein:kernel-live-p kernel) nil "execute_reply: Kernel is not active.") (let* ((content (list :code code :silent (or silent json-false) @@ -488,7 +488,7 @@ http://ipython.org/ipython-doc/dev/development/messaging.html#complete :cursor_pos cursor-pos))) (msg (ein:kernel--get-msg kernel "complete_request" content)) (msg-id (plist-get (plist-get msg :header) :msg_id))) - (assert (ein:kernel-live-p kernel) nil "kernel not live") + (cl-assert (ein:kernel-live-p kernel) nil "kernel not live") (ein:websocket-send-shell-channel kernel msg) (ein:kernel-set-callbacks-for-msg kernel msg-id callbacks) msg-id) @@ -496,17 +496,17 @@ http://ipython.org/ipython-doc/dev/development/messaging.html#complete (ein:display-warning (error-message-string err) :error))))) -(defun* ein:kernel-history-request (kernel callbacks - &key - (output nil) - (raw t) - (hist-access-type "tail") - session - start - stop - (n 10) - pattern - unique) +(cl-defun ein:kernel-history-request (kernel callbacks + &key + (output nil) + (raw t) + (hist-access-type "tail") + session + start + stop + (n 10) + pattern + unique) "Request execution history to KERNEL. When calling this method pass a CALLBACKS structure of the form: @@ -527,7 +527,7 @@ Relevant Python code: * :py:method:`IPython.zmq.ipkernel.Kernel.history_request` * :py:class:`IPython.core.history.HistoryAccessor` " - (assert (ein:kernel-live-p kernel) nil "history_reply: Kernel is not active.") + (cl-assert (ein:kernel-live-p kernel) nil "history_reply: Kernel is not active.") (let* ((content (list :output (ein:json-any-to-bool output) :raw (ein:json-any-to-bool raw) @@ -566,7 +566,7 @@ Example:: (ein:get-kernel) '(:kernel_connect_reply (message . \"CONTENT: %S\\nMETADATA: %S\"))) " - ;(assert (ein:kernel-live-p kernel) nil "connect_reply: Kernel is not active.") + ;(cl-assert (ein:kernel-live-p kernel) nil "connect_reply: Kernel is not active.") (let* ((msg (ein:kernel--get-msg kernel "connect_request" (make-hash-table))) (msg-id (plist-get (plist-get msg :header) :msg_id))) (ein:websocket-send-shell-channel kernel msg) @@ -595,7 +595,7 @@ Example:: (ein:get-kernel) '(:kernel_info_reply (message . \"CONTENT: %S\\nMETADATA: %S\"))) " - (assert (ein:kernel-live-p kernel) nil "kernel_info_reply: Kernel is not active.") + (cl-assert (ein:kernel-live-p kernel) nil "kernel_info_reply: Kernel is not active.") (ein:log 'debug "EIN:KERNEL-KERNEL-INFO-REQUEST: Sending request.") (let* ((msg (ein:kernel--get-msg kernel "kernel_info_request" nil)) (msg-id (plist-get (plist-get msg :header) :msg_id))) @@ -612,7 +612,7 @@ Example:: (ein:$kernel-kernel-url kernel) "interrupt") :type "POST" - :success (lambda (&rest ignore) + :success (lambda (&rest _ignore) (ein:log 'info "Sent interruption command."))))) (defun ein:kernel-delete--from-session-id (url session-id &optional callback) @@ -645,19 +645,16 @@ We need this to have proper behavior for the 'Stop' command in the ein:notebookl :error (apply-partially #'ein:kernel-delete-session--error session-id callback) :success (apply-partially #'ein:kernel-delete-session--success session-id callback)))) -(defun* ein:kernel-delete-session--error (session-id callback - &key response error-thrown - &allow-other-keys) +(cl-defun ein:kernel-delete-session--error (session-id _callback + &key _response error-thrown &allow-other-keys) (ein:log 'error "ein:kernel-delete-session--error %s: ERROR %s DATA %s" session-id (car error-thrown) (cdr error-thrown))) -(defun* ein:kernel-delete-session--success (session-id callback &key data symbol-status response - &allow-other-keys) +(cl-defun ein:kernel-delete-session--success (session-id _callback &key _data _symbol-status _response &allow-other-keys) (ein:log 'verbose "ein:kernel-delete-session--success: %s deleted" session-id)) -(defun* ein:kernel-delete-session--complete (kernel session-id callback &key data response - &allow-other-keys - &aux (resp-string (format "STATUS: %s DATA: %s" (request-response-status-code response) data))) +(cl-defun ein:kernel-delete-session--complete (kernel _session-id callback &key data response &allow-other-keys + &aux (resp-string (format "STATUS: %s DATA: %s" (request-response-status-code response) data))) (ein:log 'debug "ein:kernel-delete-session--complete %s" resp-string) (ein:kernel-disconnect kernel) (when callback (funcall callback kernel))) @@ -672,8 +669,8 @@ We need this to have proper behavior for the 'Stop' command in the ein:notebookl (defun ein:kernel--handle-stdin-reply (kernel packet) (setf (ein:$kernel-stdin-activep kernel) t) - (destructuring-bind - (&key header parent_header metadata content &allow-other-keys) + (cl-destructuring-bind + (&key header _parent_header _metadata content &allow-other-keys) (ein:json-read-from-string packet) (let ((msg-type (plist-get header :msg_type)) (msg-id (plist-get header :msg_id)) @@ -696,7 +693,7 @@ We need this to have proper behavior for the 'Stop' command in the ein:notebookl (setf (ein:$kernel-stdin-activep kernel) nil)))))))))) (defun ein:kernel--handle-shell-reply (kernel packet) - (destructuring-bind + (cl-destructuring-bind (&key header content metadata parent_header &allow-other-keys) (ein:json-read-from-string packet) (let* ((msg-type (plist-get header :msg_type)) @@ -717,31 +714,31 @@ We need this to have proper behavior for the 'Stop' command in the ein:notebookl (ein:events-trigger events 'execution_count.Kernel it)))))))) (defun ein:kernel--handle-payload (kernel callbacks payload) - (loop with events = (ein:$kernel-events kernel) - for p in payload - for text = (or (plist-get p :text) - (plist-get (plist-get p :data) - :text/plain)) - for source = (plist-get p :source) - if (member source '("IPython.kernel.zmq.page.page" - "IPython.zmq.page.page" - "page")) - do (when (not (equal (ein:trim text) "")) - (ein:events-trigger - events 'open_with_text.Pager (list :text text))) - else if - (member - source - '("IPython.kernel.zmq.zmqshell.ZMQInteractiveShell.set_next_input" - "IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input" - "set_next_input")) - do (let ((cb (plist-get callbacks :set_next_input))) - (when cb (ein:funcall-packed cb text))))) + (cl-loop with events = (ein:$kernel-events kernel) + for p in payload + for text = (or (plist-get p :text) + (plist-get (plist-get p :data) + :text/plain)) + for source = (plist-get p :source) + if (member source '("IPython.kernel.zmq.page.page" + "IPython.zmq.page.page" + "page")) + do (when (not (equal (ein:trim text) "")) + (ein:events-trigger + events 'open_with_text.Pager (list :text text))) + else if + (member + source + '("IPython.kernel.zmq.zmqshell.ZMQInteractiveShell.set_next_input" + "IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input" + "set_next_input")) + do (let ((cb (plist-get callbacks :set_next_input))) + (when cb (ein:funcall-packed cb text))))) (defun ein:kernel--handle-iopub-reply (kernel packet) (if (ein:$kernel-stdin-activep kernel) (ein:ipdb--handle-iopub-reply kernel packet) - (destructuring-bind + (cl-destructuring-bind (&key content metadata parent_header header &allow-other-keys) (ein:json-read-from-string packet) (let* ((msg-type (plist-get header :msg_type)) @@ -816,7 +813,7 @@ as a string and the rest of the argument is the optional ARGS." (ein:kernel-execute kernel code - (list :output (cons (lambda (packed msg-type content -metadata-not-used-) + (list :output (cons (lambda (packed msg-type content _metadata) (let ((func (car packed)) (args (cdr packed))) (when (equal msg-type "stream") @@ -824,7 +821,7 @@ as a string and the rest of the argument is the optional ARGS." (apply func it args))))) (cons func args))))) -(defun* ein:kernel-history-request-synchronously +(cl-defun ein:kernel-history-request-synchronously (kernel &rest args &key (timeout 0.5) (tick-time 0.05) &allow-other-keys) "Send the history request and wait TIMEOUT seconds. Return a list (CONTENT METADATA). @@ -832,21 +829,21 @@ This function checks the request reply every TICK-TIME seconds. See `ein:kernel-history-request' for other usable options." ;; As `result' and `finished' are set in callback, make sure they ;; won't be trapped in other let-bindings. - (lexical-let (result finished) + (let (result finished) (apply #'ein:kernel-history-request kernel (list :history_reply - (cons (lambda (-ignore- content metadata) + (cons (lambda (_ignore content metadata) (setq result (list content metadata)) (setq finished t)) nil)) args) - (loop repeat (floor (/ timeout tick-time)) - do (sit-for tick-time) - when finished - return t - finally (error "Timeout")) + (cl-loop repeat (floor (/ timeout tick-time)) + do (sit-for tick-time) + when finished + return t + finally (error "Timeout")) result)) (defun ein:kernel-history-search-synchronously (kernel pattern &rest args) diff --git a/lisp/ein-kernelinfo.el b/lisp/ein-kernelinfo.el index bb87e46d2..d1a5bc128 100644 --- a/lisp/ein-kernelinfo.el +++ b/lisp/ein-kernelinfo.el @@ -25,7 +25,6 @@ ;;; Code: -(eval-when-compile (require 'cl)) (require 'ein-kernel) (require 'eieio) diff --git a/lisp/ein-log.el b/lisp/ein-log.el index 391399dda..ba3d526dc 100644 --- a/lisp/ein-log.el +++ b/lisp/ein-log.el @@ -25,7 +25,6 @@ ;;; Code: -(eval-when-compile (require 'cl)) (require 'ein-core) @@ -56,10 +55,10 @@ (setq ein:log-message-level (ein:log-level-name-to-int level))) (defun ein:log-level-int-to-name (int) - (loop for (n . i) in ein:log-level-def - when (>= int i) - return n - finally 'error)) + (cl-loop for (n . i) in ein:log-level-def + when (>= int i) + return n + finally 'error)) (defun ein:log-level-name-to-int (name) (cdr (assq name ein:log-level-def))) @@ -119,4 +118,3 @@ Otherwise, return result of last form in BODY." (provide 'ein-log) ;;; ein-log.el ends here - diff --git a/lisp/ein-multilang-fontify.el b/lisp/ein-multilang-fontify.el index 99f625000..08d00d7e8 100644 --- a/lisp/ein-multilang-fontify.el +++ b/lisp/ein-multilang-fontify.el @@ -60,14 +60,14 @@ Modified version of `org-src-get-lang-mode'." (unless (eq major-mode lang-mode) (funcall lang-mode)) (font-lock-fontify-buffer) (setq pos (point-min)) - (loop for next = (next-single-property-change pos 'face nil (point-max)) - do (put-text-property - ;; `font-lock-face' property is used instead of `font'. - ;; This is the only difference from org-src. - (+ start (1- pos)) (+ start next) 'font-lock-face - (get-text-property pos 'face) orig-buffer) - do (setq pos next) - until (eq pos (point-max)))) + (cl-loop for next = (next-single-property-change pos 'face nil (point-max)) + do (put-text-property + ;; `font-lock-face' property is used instead of `font'. + ;; This is the only difference from org-src. + (+ start (1- pos)) (+ start next) 'font-lock-face + (get-text-property pos 'face) orig-buffer) + do (setq pos next) + until (eq pos (point-max)))) (add-text-properties start end '(font-lock-fontified t fontified t font-lock-multiline t)) diff --git a/lisp/ein-multilang.el b/lisp/ein-multilang.el index aedebbc24..277e50e4b 100644 --- a/lisp/ein-multilang.el +++ b/lisp/ein-multilang.el @@ -26,7 +26,6 @@ ;;; Code: -(eval-when-compile (require 'cl)) (eval-when-compile (defvar markdown-mode-map)) (require 'ein-worksheet) @@ -34,11 +33,16 @@ (require 'python) (require 'ess-r-mode nil t) (require 'ess-custom nil t) +(require 'clojure-mode nil t) (require 'julia-mode nil t) +(require 'haskell-mode nil t) +(require 'hy-mode nil t) +(require 'cc-mode) (declare-function ess-indent-line "ess") (declare-function ess-r-eldoc-function "ess-r-completion") (declare-function ess-setq-vars-local "ess-utils") +(declare-function haskell-indentation-indent-line "haskell-indentation") (defun ein:ml-fontify (limit) "Fontify next input area comes after the current point then @@ -143,6 +147,20 @@ This function may raise an error." (set-syntax-table python-mode-syntax-table) (set-keymap-parent ein:notebook-multilang-mode-map python-mode-map)) +(defun ein:ml-lang-setup-clojure () + "Minimally different than the the python setup" + (when (featurep 'clojure-mode) + (setq-local mode-name "EIN[Clj]") + (setq-local comment-start "; ") + (setq-local comment-start-skip ";+\\s-*") + (setq-local parse-sexp-lookup-properties t) + (setq-local indent-line-function + (apply-partially #'ein:ml-indent-line-function #'clojure-indent-line)) + (setq-local indent-region-function + (apply-partially #'ein:ml-indent-region #'clojure-indent-region)) + (set-syntax-table clojure-mode-syntax-table) + (set-keymap-parent ein:notebook-multilang-mode-map clojure-mode-map))) + (defun ein:ml-lang-setup-julia () (when (featurep 'julia-mode) (setq-local mode-name "EIN[julia]") @@ -174,11 +192,69 @@ This function may raise an error." (when (boundp 'ess-r-mode-map) (set-keymap-parent ein:notebook-multilang-mode-map ess-r-mode-map)))) +(defun ein:ml-lang-setup-haskell () + (when (featurep 'haskell-mode) + (setq-local mode-name "EIN[haskell]") + (setq-local comment-start "-- ") + ;; (setq-local comment-start-skip "--\\s-*") + (when (boundp 'haskell-indentation-indent-line) + (setq-local indent-line-function + (apply-partially #'ein:ml-indent-line-function #'haskell-indentation-indent-line))) + (when (boundp 'haskell-mode-syntax-table) + (set-syntax-table haskell-mode-syntax-table)) + (when (boundp 'haskell-mode-map) + (set-keymap-parent ein:notebook-multilang-mode-map haskell-mode-map)))) + +(defun ein:ml-lang-setup-hy () + (when (featurep 'hy-mode) + (setq-local mode-name "EIN[hy]") + (hy-mode--setup-font-lock) + (hy-mode--setup-syntax) + (hy-mode--support-smartparens) + (set-keymap-parent ein:notebook-multilang-mode-map hy-mode-map))) + +(defun ein:ml-lang-setup-c++ () + (when (featurep 'c++-mode) + (setq-local mode-name "EIN[c++]") + (setq-local comment-start "// ") + (setq-local indent-line-function + (apply-partially #'ein:ml-indent-line-function #'c-indent-line)) + (set-syntax-table c++-mode-syntax-table) + (set-keymap-parent ein:notebook-multilang-mode-map c++-mode-map))) + +(defun ein:ml-lang-setup-c () + (when (featurep 'c-mode) + (setq-local mode-name "EIN[c]") + (setq-local comment-start "/* ") + (setq-local comment-end " */") + (setq-local indent-line-function + (apply-partially #'ein:ml-indent-line-function #'c-indent-line)) + (set-syntax-table c-mode-syntax-table) + (set-keymap-parent ein:notebook-multilang-mode-map c-mode-map))) + +(defun ein:ml-lang-setup-C++11 () + (ein:ml-lang-setup-c++)) + +(defun ein:ml-lang-setup-C++14 () + (ein:ml-lang-setup-c++)) + +(defun ein:ml-lang-setup-C++17 () + (ein:ml-lang-setup-c++)) + +(defun ein:ml-lang-setup-generic () + (setq-local mode-name "EIN[unknown]") + (setq-local indent-line-function + (apply-partially #'ein:ml-indent-line-function #'indent-relative)) + (set-syntax-table prog-mode-syntax-table) + (set-keymap-parent ein:notebook-multilang-mode-map prog-mode-map)) + (defun ein:ml-lang-setup (kernelspec) (let ((setup-func (intern (concat "ein:ml-lang-setup-" (ein:$kernelspec-language kernelspec))))) (if (fboundp setup-func) (funcall setup-func) - (error "ein:ml-lang-setup: kernelspec language '%s' unsupported" (ein:$kernelspec-language kernelspec))))) + (warn "ein:ml-lang-setup: unknown kernelspec language '%s', multilang support disabled." + (ein:$kernelspec-language kernelspec)) + (ein:ml-lang-setup-generic)))) ;; (defun ein:ml-lang-setup-markdown () ;; "Use `markdown-mode-map'. NOTE: This function is not used now." @@ -191,15 +267,15 @@ This function may raise an error." "Parent modes for `ein:notebook-multilang-mode' to register in yasnippet.") (defun ein:ml-setup-yasnippet () - (loop for define-parents in '(yas/define-parents - yas--define-parents) - when (fboundp define-parents) - do (ignore-errors - ;; `let' is for workaround the bug in yasnippet - (let ((mode-sym 'ein:notebook-multilang-mode)) - (funcall define-parents - mode-sym - ein:ml-yasnippet-parents))))) + (cl-loop for define-parents in '(yas/define-parents + yas--define-parents) + when (fboundp define-parents) + do (ignore-errors + ;; `let' is for workaround the bug in yasnippet + (let ((mode-sym 'ein:notebook-multilang-mode)) + (funcall define-parents + mode-sym + ein:ml-yasnippet-parents))))) (eval-after-load "yasnippet" '(ein:ml-setup-yasnippet)) diff --git a/lisp/ein-node.el b/lisp/ein-node.el index fbcf7d341..a55fc8109 100644 --- a/lisp/ein-node.el +++ b/lisp/ein-node.el @@ -25,13 +25,12 @@ ;;; Code: -(eval-when-compile (require 'cl)) (require 'ewoc) (require 'ein-core) -(defstruct ein:$node +(cl-defstruct ein:$node path ; list of path data ; actual data class ; list @@ -52,15 +51,15 @@ (memq class (ein:$node-class node))) (defun ein:node-filter (ewoc-node-list &rest args) - (loop for (key . class) in (ein:plist-iter args) - do (setq ewoc-node-list - (loop for ewoc-node in ewoc-node-list - for node = (ewoc-data ewoc-node) - when (case key - (:is (ein:node-has-class node class)) - (:not (not (ein:node-has-class node class))) - (t (error "%s is not supported" key))) - collect ewoc-node))) + (cl-loop for (key . class) in (ein:plist-iter args) + do (setq ewoc-node-list + (cl-loop for ewoc-node in ewoc-node-list + for node = (ewoc-data ewoc-node) + when (cl-case key + (:is (ein:node-has-class node class)) + (:not (not (ein:node-has-class node class))) + (t (error "%s is not supported" key))) + collect ewoc-node))) ewoc-node-list) (provide 'ein-node) diff --git a/lisp/ein-notebook.el b/lisp/ein-notebook.el index eef00d5b8..e1f6e4a15 100644 --- a/lisp/ein-notebook.el +++ b/lisp/ein-notebook.el @@ -1,4 +1,4 @@ -;;; ein-notebook.el --- Notebook module +;;; ein-notebook.el --- Notebook module -*- lexical-binding: t -*- ;; Copyright (C) 2012- Takafumi Arakaki @@ -33,7 +33,6 @@ ;;; Code: -(eval-when-compile (require 'cl)) (eval-when-compile (require 'auto-complete)) (require 'ewoc) @@ -141,12 +140,12 @@ a function ) :group 'ein) -(defun ein:notebook-cell-has-image-output-p (-ignore- cell) +(defun ein:notebook-cell-has-image-output-p (_ignore cell) (ein:cell-has-image-ouput-p cell)) (defun ein:notebook-discard-output-p (notebook cell) "Return non-`nil' if the output must be discarded, otherwise save." - (case ein:notebook-discard-output-on-save + (cl-case ein:notebook-discard-output-on-save (no nil) (yes t) (t (funcall ein:notebook-discard-output-on-save notebook cell)))) @@ -224,12 +223,12 @@ Current buffer for these functions is set to the notebook buffer.") (let ((kernelspec (cond ((ein:$kernelspec-p pre-kernelspec) pre-kernelspec) ((consp pre-kernelspec) - (loop for (name ks) on (ein:need-kernelspecs url-or-port) by 'cddr - when (and (ein:$kernelspec-p ks) - (string= (cdr pre-kernelspec) - (cl-struct-slot-value - 'ein:$kernelspec (car pre-kernelspec) ks))) - return ks)) + (cl-loop for (_name ks) on (ein:need-kernelspecs url-or-port) by 'cddr + when (and (ein:$kernelspec-p ks) + (string= (cdr pre-kernelspec) + (cl-struct-slot-value + 'ein:$kernelspec (car pre-kernelspec) ks))) + return ks)) (t (ein:get-kernelspec url-or-port pre-kernelspec))))) (apply #'make-ein:$notebook :url-or-port url-or-port @@ -247,8 +246,8 @@ Current buffer for these functions is set to the notebook buffer.") (defun ein:notebook-close-worksheet (notebook ws) "Close worksheet WS in NOTEBOOK. If WS is the last worksheet, call notebook destructor `ein:notebook-del'." - (symbol-macrolet ((worksheets (ein:$notebook-worksheets notebook)) - (scratchsheets (ein:$notebook-scratchsheets notebook))) + (cl-symbol-macrolet ((worksheets (ein:$notebook-worksheets notebook)) + (scratchsheets (ein:$notebook-scratchsheets notebook))) (cond ((ein:worksheet-p ws) (ein:worksheet-save-cells ws t)) (t (setq scratchsheets (delq ws scratchsheets)))) @@ -286,10 +285,10 @@ combo must match exactly these url/port you used format (defun ein:notebook-buffer (notebook) "Return first buffer in NOTEBOOK's worksheets." - (loop for ws in (append (ein:$notebook-worksheets notebook) - (ein:$notebook-scratchsheets notebook)) - if (ein:worksheet-buffer ws) - return it)) + (cl-loop for ws in (append (ein:$notebook-worksheets notebook) + (ein:$notebook-scratchsheets notebook)) + if (ein:worksheet-buffer ws) + return it)) (defun ein:notebook-buffer-list (notebook) "Return the buffers associated with NOTEBOOK's kernel. @@ -356,7 +355,7 @@ will be updated with kernel's cwd." (defun ein:notebook-open-or-create (url-or-port path &optional kernelspec callback no-pop) "Same as `ein:notebook-open' but create PATH if not found." - (let ((if-not-found (lambda (contents status-code) ))) + (let ((if-not-found (lambda (_contents _status-code) ))) (ein:notebook-open url-or-port path kernelspec callback if-not-found no-pop))) ;;;###autoload @@ -383,7 +382,7 @@ where `created' indicates a new notebook or an existing one. (unless errback (setq errback #'ignore)) (let* ((pending-key (cons url-or-port path)) (pending-p (gethash pending-key *ein:notebook--pending-query*)) - (pending-clear (apply-partially (lambda (pending-key* &rest args) + (pending-clear (apply-partially (lambda (pending-key* &rest _args) (remhash pending-key* *ein:notebook--pending-query*)) pending-key)) @@ -402,7 +401,7 @@ where `created' indicates a new notebook or an existing one. (when (or (not pending-p) (y-or-n-p (format "Notebook %s pending open! Retry? " path))) (setf (gethash pending-key *ein:notebook--pending-query*) t) - (add-function :before errback pending-clear) + (add-function :before (var errback) pending-clear) (ein:content-query-contents url-or-port path (apply-partially #'ein:notebook-open--callback notebook callback0 (not no-pop)) @@ -411,7 +410,7 @@ where `created' indicates a new notebook or an existing one. (defun ein:notebook-open--callback (notebook callback0 q-checkpoints content) (ein:log 'verbose "Opened notebook %s" (ein:$notebook-notebook-path notebook)) - (let ((notebook-path (ein:$notebook-notebook-path notebook))) + (let ((_notebook-path (ein:$notebook-notebook-path notebook))) (ein:gc-prepare-operation) (setf (ein:$notebook-api-version notebook) (ein:$content-notebook-version content) (ein:$notebook-notebook-name notebook) (ein:$content-name content)) @@ -426,7 +425,7 @@ where `created' indicates a new notebook or an existing one. ;; because ein:notification-bind-events only gets called after worksheet's ;; buffer local notification widget is instantiated (ein:kernel-retrieve-session (ein:$notebook-kernel notebook) nil - (apply-partially (lambda (callback0* name* kernel) + (apply-partially (lambda (callback0* name* _kernel) (funcall callback0*) (ein:log 'info "Notebook %s is ready" name*)) callback0 @@ -457,9 +456,9 @@ See https://github.com/ipython/ipython/pull/1934 for the purpose of minor mode." ;; See `Notebook.prototype.load_notebook_success' ;; at IPython/frontend/html/notebook/static/js/notebook.js - (destructuring-bind (&key nbformat orig_nbformat - nbformat_minor orig_nbformat_minor - &allow-other-keys) + (cl-destructuring-bind (&key nbformat orig_nbformat + nbformat_minor orig_nbformat_minor + &allow-other-keys) data (cond ((ein:notebook--different-number nbformat orig_nbformat) @@ -546,6 +545,8 @@ notebook buffer." 'ein:notebook-show-in-shared-output 'ein:shared-output-show-code-cell-at-point "0.1.2") +(eval-when-compile (defvar outline-regexp)) +(declare-function px-preview "px" ()) (defsubst ein:notebook-toggle-latex-fragment () (interactive) (cond (ein:polymode (ein:display-warning "ein:notebook-toggle-latex-fragment: delegate to markdown-mode")) @@ -566,10 +567,10 @@ notebook buffer." (defun ein:list-available-kernels (url-or-port) (let ((kernelspecs (ein:need-kernelspecs url-or-port))) (if kernelspecs - (sort (loop for (key spec) on (ein:plist-exclude kernelspecs '(:default)) by 'cddr - collecting (cons (ein:$kernelspec-name spec) - (ein:$kernelspec-display-name spec))) - (lambda (c1 c2) (string< (cdr c1) (cdr c2))))))) + (cl-sort (cl-loop for (_key spec) on (ein:plist-exclude kernelspecs '(:default)) by 'cddr + collecting (cons (ein:$kernelspec-name spec) + (ein:$kernelspec-display-name spec))) + #'string< :key #'cdr)))) (defun ein:notebook-switch-kernel (notebook kernel-name) "Change the kernel for a running notebook. If not called from a @@ -592,11 +593,11 @@ notebook buffer then the user will be prompted to select an opened notebook." (ein:$kernelspec-name kernelspec) (ein:$kernelspec-spec kernelspec)))) (ein:notebook-save-notebook notebook #'ein:notebook-kill-kernel-then-close-command (list notebook)) - (loop repeat 10 - until (null (ein:$kernel-websocket (ein:$notebook-kernel notebook))) - do (sleep-for 0 500) - finally return (ein:notebook-open (ein:$notebook-url-or-port notebook) - (ein:$notebook-notebook-path notebook))))) + (cl-loop repeat 10 + until (null (ein:$kernel-websocket (ein:$notebook-kernel notebook))) + do (sleep-for 0 500) + finally return (ein:notebook-open (ein:$notebook-url-or-port notebook) + (ein:$notebook-notebook-path notebook))))) (defun ein:notebook-install-kernel (notebook) (let* ((base-url (concat ein:base-kernel-url "kernels")) @@ -620,8 +621,8 @@ notebook buffer then the user will be prompted to select an opened notebook." "Delete session on server side. Start new session." (interactive) (ein:aif ein:%notebook% - (if (y-or-n-p "Are you sure? ") - (ein:kernel-restart-session (ein:$notebook-kernel it))) + (when (y-or-n-p "Are you sure you want to restart this session? ") + (ein:kernel-restart-session (ein:$notebook-kernel it))) (message "Not in notebook buffer!"))) (define-obsolete-function-alias @@ -670,8 +671,7 @@ This is equivalent to do ``C-c`` in the console program." (> (length name) 0) (not (string-match "[\\/\\\\:]" name)))) -(defun* ein:notebook--worksheet-new (notebook - &optional (func #'ein:worksheet-new)) +(cl-defun ein:notebook--worksheet-new (notebook &optional (func #'ein:worksheet-new)) (funcall func (ein:$notebook-nbformat notebook) (ein:notebook-name-getter notebook) @@ -733,7 +733,7 @@ This is equivalent to do ``C-c`` in the console program." (lambda (ws) (ein:notebook-worksheet-move-next ein:%notebook% ws)) )) -(defun ein:notebook-set-buffer-file-name-maybe (notebook) +(defun ein:notebook-set-buffer-file-name-maybe (_notebook) (ein:log 'warn "This function is deprecated. Who could be calling me?")) ;; (defun ein:notebook-set-buffer-file-name-maybe (notebook) @@ -744,7 +744,7 @@ This is equivalent to do ``C-c`` in the console program." ;; notebook ;; (lambda (data notebook buffer) ;; (with-current-buffer buffer -;; (destructuring-bind (&key project &allow-other-keys) +;; (cl-destructuring-bind (&key project &allow-other-keys) ;; data ;; (setq buffer-file-name ;; (expand-file-name @@ -754,8 +754,8 @@ This is equivalent to do ``C-c`` in the console program." ;; (list notebook (current-buffer))))) (defun ein:notebook-from-json (notebook data) - (destructuring-bind (&key metadata nbformat nbformat_minor - &allow-other-keys) + (cl-destructuring-bind (&key metadata nbformat nbformat_minor + &allow-other-keys) data (setf (ein:$notebook-metadata notebook) metadata) (setf (ein:$notebook-nbformat notebook) nbformat) @@ -766,8 +766,7 @@ This is equivalent to do ``C-c`` in the console program." (4 (ein:read-nbformat4-worksheets notebook data)) (t (ein:log 'error "nbformat version %s unsupported" (ein:$notebook-nbformat notebook))))) - (ein:notebook--worksheet-render notebook - (first (ein:$notebook-worksheets notebook))) + (ein:notebook--worksheet-render notebook (car (ein:$notebook-worksheets notebook))) notebook) (defun ein:read-nbformat3-worksheets (notebook data) @@ -797,7 +796,7 @@ This is equivalent to do ``C-c`` in the console program." (defun ein:notebook-to-json (notebook) "Return json-ready alist." (let ((data - (case (ein:$notebook-nbformat notebook) + (cl-case (ein:$notebook-nbformat notebook) (3 (ein:write-nbformat3-worksheets notebook)) (4 (ein:write-nbformat4-worksheets notebook)) (t (ein:log 'error "nbformat version %s unsupported" @@ -829,9 +828,9 @@ This is equivalent to do ``C-c`` in the console program." (plist-put spec :name name))) (defun ein:write-nbformat4-worksheets (notebook) - (let ((all-cells (loop for ws in (ein:$notebook-worksheets notebook) - for i from 0 - append (ein:worksheet-to-nb4-json ws i)))) + (let ((all-cells (cl-loop for ws in (ein:$notebook-worksheets notebook) + for i from 0 + append (ein:worksheet-to-nb4-json ws i)))) ;; should be in notebook constructor, not here (ein:aif (ein:$notebook-kernelspec notebook) (setf (ein:$notebook-metadata notebook) @@ -857,7 +856,7 @@ This is equivalent to do ``C-c`` in the console program." (ein:$notebook-notebook-name notebook)))) (ein:log 'error "ein:notebook-save-notebook: notebook %s has no buffer!" buf) (setf (ewoc--buffer (ein:worksheet--ewoc - (first (ein:$notebook-worksheets notebook)))) + (car (ein:$notebook-worksheets notebook)))) (get-buffer buf)))) (condition-case err (with-current-buffer (ein:notebook-buffer notebook) @@ -902,8 +901,7 @@ This is equivalent to do ``C-c`` in the console program." ;; This should save the latest WS0. To do so, WS0 at the point (2) ;; must be cached in the worksheet slot `:saved-cells'. -(defun* ein:notebook-save-notebook-error (notebook &key symbol-status - &allow-other-keys) +(cl-defun ein:notebook-save-notebook-error (notebook &key symbol-status &allow-other-keys) (if (eq symbol-status 'user-cancel) (ein:log 'info "Cancel saving notebook.") (ein:log 'info "Failed to save notebook!") @@ -941,7 +939,7 @@ NAME is any non-empty string that does not contain '/' or '\\'. (list (ein:$notebook-url-or-port ein:%notebook%) path)))) -(defun* ein:notebook-rename-success (notebook content) +(cl-defun ein:notebook-rename-success (notebook content) (ein:notebook-remove-opened-notebook notebook) (ein:notebook-set-notebook-name notebook (ein:$content-name content)) (setf (ein:$notebook-notebook-path notebook) (ein:$content-path content)) @@ -958,8 +956,7 @@ NAME is any non-empty string that does not contain '/' or '\\'. (defmacro ein:notebook-avoid-recursion (&rest body) `(let ((kill-buffer-query-functions - (remove-if (lambda (x) (eq 'ein:notebook-kill-buffer-query x)) - kill-buffer-query-functions))) + (remove 'ein:notebook-kill-buffer-query kill-buffer-query-functions))) ,@body)) (defun ein:notebook-kill-notebook-buffers (notebook) @@ -991,14 +988,14 @@ NAME is any non-empty string that does not contain '/' or '\\'. (if (and (ein:notebook-modified-p notebook) (not (ob-ein-anonymous-p (ein:$notebook-notebook-path notebook)))) (if (y-or-n-p (format "Save %s?" (ein:$notebook-notebook-name notebook))) - (lexical-let ((success-positive 0)) - (add-function :before callback0 (lambda () (setq success-positive 1))) + (let ((success-positive 0)) + (add-function :before (var callback0) (lambda () (setq success-positive 1))) (ein:notebook-save-notebook notebook callback0 nil (lambda () (setq success-positive -1))) - (loop repeat 10 - until (not (zerop success-positive)) - do (sleep-for 0 200) - finally return (> success-positive 0))) + (cl-loop repeat 10 + until (not (zerop success-positive)) + do (sleep-for 0 200) + finally return (> success-positive 0))) (when (ein:worksheet-p ein:%worksheet%) (ein:worksheet-dont-save-cells ein:%worksheet%)) ;; TODO de-obfuscate (funcall callback0) @@ -1008,10 +1005,10 @@ NAME is any non-empty string that does not contain '/' or '\\'. (defun ein:notebook-close (notebook &optional callback &rest cbargs) (interactive (list (ein:notebook--get-nb-or-error))) - (lexical-let* ((notebook (or notebook (ein:notebook--get-nb-or-error))) - (callback0 (apply-partially #'ein:notebook-kill-notebook-buffers notebook))) + (let* ((notebook (or notebook (ein:notebook--get-nb-or-error))) + (callback0 (apply-partially #'ein:notebook-kill-notebook-buffers notebook))) (when callback - (add-function :after callback0 + (add-function :after (var callback0) (apply #'apply-partially callback cbargs))) (ein:notebook-ask-save notebook callback0))) @@ -1022,12 +1019,12 @@ as usual." (interactive (list (ein:notebook--get-nb-or-error))) (let ((kernel (ein:$notebook-kernel notebook)) (callback1 (apply-partially - (lambda (notebook* kernel) + (lambda (notebook* _kernel) (ein:notebook-close notebook*)) notebook))) (if (ein:kernel-live-p kernel) (ein:message-whir "Ending session" - (add-function :before callback1 done-callback) + (add-function :before (var callback1) done-callback) (ein:kernel-delete-session kernel callback1)) (funcall callback1 nil)))) @@ -1047,10 +1044,10 @@ notebook worksheets." (if (ein:$notebook-q-checkpoints notebook) (ein:content-create-checkpoint (ein:fast-content-from-notebook notebook) - (lexical-let ((notebook notebook)) + (let ((notebook notebook)) #'(lambda (content) (ein:log 'verbose "Checkpoint %s for %s generated." - (plist-get (first (ein:$content-checkpoints content)) :id) + (plist-get (car (ein:$content-checkpoints content)) :id) (ein:$notebook-notebook-name notebook)) (setf (ein:$notebook-checkpoints notebook) (ein:$content-checkpoints content))))))) @@ -1058,13 +1055,13 @@ notebook worksheets." (defun ein:notebook-list-checkpoint-ids (notebook) (unless (ein:$notebook-checkpoints notebook) (ein:content-query-checkpoints (ein:fast-content-from-notebook notebook) - (lexical-let ((notebook notebook)) + (let ((notebook notebook)) #'(lambda (content) (setf (ein:$notebook-checkpoints notebook) (ein:$content-checkpoints content))))) (sleep-for 0.5)) - (loop for cp in (ein:$notebook-checkpoints notebook) - collecting (plist-get cp :last_modified))) + (cl-loop for cp in (ein:$notebook-checkpoints notebook) + collecting (plist-get cp :last_modified))) (defun ein:notebook-restore-to-checkpoint (notebook checkpoint) "Restore notebook to previous checkpoint saved on the Jupyter @@ -1149,7 +1146,7 @@ See also `ein:notebook-worksheet-open-next-or-first' and (when show (funcall show (ein:worksheet-buffer prev))))) -(defun* ein:notebook-worksheet--render-maybe +(cl-defun ein:notebook-worksheet--render-maybe (notebook ws &optional (adj "next")) "Render worksheet WS of NOTEBOOK if it does not have buffer. ADJ is a adjective to describe worksheet to be rendered." @@ -1159,7 +1156,7 @@ ADJ is a adjective to describe worksheet to be rendered." (ein:notebook--worksheet-render notebook ws) (ein:log 'verbose "Rendering %s worksheet... Done." adj))) -(defun* ein:notebook-worksheet--open-new +(cl-defun ein:notebook-worksheet--open-new (notebook new &optional (adj "next") show) "Open (possibly new) worksheet NEW of NOTEBOOK with SHOW function. ADJ is a adjective to describe worksheet to be opened. @@ -1167,7 +1164,7 @@ SHOW is a function to be called with worksheet buffer if given." (when new (ein:notebook-worksheet--render-maybe notebook new adj)) (when show - (assert (ein:worksheet-p new) nil "No %s worksheet." adj) + (cl-assert (ein:worksheet-p new) nil "No %s worksheet." adj) (funcall show (ein:worksheet-buffer new)))) (defun ein:notebook-worksheet-open-next (notebook ws &optional show) @@ -1185,10 +1182,10 @@ given." #'switch-to-buffer)) (let ((next (if (ein:scratchsheet-p ws) (car (ein:$notebook-worksheets notebook)) - (loop with worksheets = (ein:$notebook-worksheets notebook) - for current in worksheets - for next in (cdr worksheets) - when (eq current ws) return next)))) + (cl-loop with worksheets = (ein:$notebook-worksheets notebook) + for current in worksheets + for next in (cdr worksheets) + when (eq current ws) return next)))) (ein:notebook-worksheet--open-new notebook next "next" show) next)) @@ -1200,8 +1197,8 @@ See also `ein:notebook-worksheet-open-next'." #'switch-to-buffer)) (let ((prev (if (ein:scratchsheet-p ws) (car (last (ein:$notebook-worksheets notebook))) - (loop for (prev current) on (ein:$notebook-worksheets notebook) - when (eq current ws) return prev)))) + (cl-loop for (prev current) on (ein:$notebook-worksheets notebook) + when (eq current ws) return prev)))) (ein:notebook-worksheet--open-new notebook prev "previous" show) prev)) @@ -1213,7 +1210,7 @@ See also `ein:notebook-worksheet-open-next'." (defmacro ein:notebook-worksheet--defun-open-nth (n) "Define a command to open N-th (one-origin) worksheet." - (assert (and (integerp n) (> n 0)) t "Bad nth worksheet n=%s" n) + (cl-assert (and (integerp n) (> n 0)) t "Bad nth worksheet n=%s" n) (let ((func (intern (format "ein:notebook-worksheet-open-%sth" n)))) `(defun ,func (notebook &optional show) ,(format "Open %d-th worksheet." n) @@ -1223,8 +1220,8 @@ See also `ein:notebook-worksheet-open-next'." (defmacro ein:notebook-worksheet--defun-all-open-nth (min max) `(progn - ,@(loop for n from min to max - collect `(ein:notebook-worksheet--defun-open-nth ,n)))) + ,@(cl-loop for n from min to max + collect `(ein:notebook-worksheet--defun-open-nth ,n)))) (ein:notebook-worksheet--defun-all-open-nth 1 8) @@ -1247,7 +1244,7 @@ See also `ein:notebook-worksheet-open-next'." (funcall show (ein:worksheet-buffer new))) new)) -(defun* ein:notebook-worksheet-insert-next +(cl-defun ein:notebook-worksheet-insert-next (notebook ws &optional (render t) (show #'switch-to-buffer)) "Insert a new worksheet after this worksheet and open it. See also `ein:notebook-worksheet-insert-prev'. @@ -1261,7 +1258,7 @@ See also `ein:notebook-worksheet-insert-prev'. (ein:notebook-worksheet-insert-new notebook ws render show #'ein:list-insert-after)) -(defun* ein:notebook-worksheet-insert-prev +(cl-defun ein:notebook-worksheet-insert-prev (notebook ws &optional (render t) (show #'switch-to-buffer)) "Insert a new worksheet before this worksheet and open it. See also `ein:notebook-worksheet-insert-next'." @@ -1276,10 +1273,9 @@ When used as a lisp function, delete worksheet WS from NOTEBOOk." (interactive (list (ein:notebook--get-nb-or-error) (ein:worksheet--get-ws-or-error) t)) - (when confirm - (unless (y-or-n-p - "Really remove this worksheet? There is no undo.") - (error "Quit deleting the current worksheet."))) + (when (and confirm + (not (y-or-n-p "Really remove this worksheet? There is no undo."))) + (error "Quit deleting the current worksheet.")) (setf (ein:$notebook-worksheets notebook) (delq ws (ein:$notebook-worksheets notebook))) (setf (ein:$notebook-dirty notebook) t) @@ -1289,7 +1285,7 @@ When used as a lisp function, delete worksheet WS from NOTEBOOk." "Switch the current worksheet with the previous one." (interactive (list (ein:notebook--get-nb-or-error) (ein:worksheet--get-ws-or-error))) - (assert (ein:worksheet-p ws) nil "Not worksheet.") + (cl-assert (ein:worksheet-p ws) nil "Not worksheet.") (setf (ein:$notebook-worksheets notebook) (ein:list-move-left (ein:$notebook-worksheets notebook) ws))) @@ -1297,18 +1293,18 @@ When used as a lisp function, delete worksheet WS from NOTEBOOk." "Switch the current worksheet with the previous one." (interactive (list (ein:notebook--get-nb-or-error) (ein:worksheet--get-ws-or-error))) - (assert (ein:worksheet-p ws) nil "Not worksheet.") + (cl-assert (ein:worksheet-p ws) nil "Not worksheet.") (setf (ein:$notebook-worksheets notebook) (ein:list-move-right (ein:$notebook-worksheets notebook) ws))) -(defun* ein:notebook-worksheet-index +(cl-defun ein:notebook-worksheet-index (&optional (notebook ein:%notebook%) (ws ein:%worksheet%)) "Return an index of the worksheet WS in NOTEBOOK." - (loop for i from 0 - for ith-ws in (ein:$notebook-worksheets notebook) - when (eq ith-ws ws) - return i)) + (cl-loop for i from 0 + for ith-ws in (ein:$notebook-worksheets notebook) + when (eq ith-ws ws) + return i)) ;;; Scratch sheet @@ -1414,9 +1410,9 @@ associated with current buffer (if any)." (and (ein:$notebook-p notebook) (ein:notebook-live-p notebook) (or (ein:$notebook-dirty notebook) - (loop for ws in (ein:$notebook-worksheets notebook) - when (ein:worksheet-modified-p ws) - return t)))) + (cl-loop for ws in (ein:$notebook-worksheets notebook) + when (ein:worksheet-modified-p ws) + return t)))) ;;; Notebook mode @@ -1460,9 +1456,9 @@ Use simple `python-mode' based notebook mode when MuMaMo is not installed:: (defun ein:notebook-choose-mode () "Return usable (defined) notebook mode." (autoload 'ein:notebook-multilang-mode "ein-multilang") - (loop for mode in ein:notebook-modes - if (functionp mode) - return mode)) + (cl-loop for mode in ein:notebook-modes + if (functionp mode) + return mode)) (defvar ein:notebook-mode-map (make-sparse-keymap)) @@ -1591,8 +1587,12 @@ Use simple `python-mode' based notebook mode when MuMaMo is not installed:: ("Execute cell and insert below" ein:worksheet-execute-cell-and-insert-below :active (ein:worksheet-at-codecell-p)) - ("Execute all" - ein:worksheet-execute-all-cell) + ("Execute all cells" + ein:worksheet-execute-all-cells) + ("Execute all cells above" + ein:worksheet-execute-all-cells-above) + ("Execute all cells below" + ein:worksheet-execute-all-cells-below) ("Turn on auto execution flag" ein:worksheet-turn-on-autoexec :active (ein:worksheet-at-codecell-p)) ("Evaluate code in minibuffer" ein:shared-output-eval-string) @@ -1657,11 +1657,11 @@ Use simple `python-mode' based notebook mode when MuMaMo is not installed:: "---" ,@(ein:generate-menu (append - (loop for n from 1 to 8 - collect - (list - (format "Open %d-th worksheet" n) - (intern (format "ein:notebook-worksheet-open-%sth" n)))) + (cl-loop for n from 1 to 8 + collect + (list + (format "Open %d-th worksheet" n) + (intern (format "ein:notebook-worksheet-open-%sth" n)))) '(("Open last worksheet" ein:notebook-worksheet-open-last))))) ;; Misc: ,@(ein:generate-menu @@ -1694,8 +1694,9 @@ watch the fireworks!" ;; It is executed after toggling the mode, and before running MODE-hook. (when ein:notebook-mode - (case ein:completion-backend + (cl-case ein:completion-backend (ein:use-ac-backend + (ein:ac-install-backend) (ein:notebook--define-key ein:notebook-mode-map "." 'ein:notebook-ac-dot-complete) (auto-complete-mode)) (ein:use-company-backend @@ -1763,7 +1764,7 @@ the first argument and CBARGS as the rest of arguments." :notebook-id (ein:html-get-data-in-body-tag "data-notebook-id"))) :success - (apply-partially (function* + (apply-partially (cl-function (lambda (callback cbargs &key data &allow-other-keys) (apply callback data cbargs))) callback cbargs)))) @@ -1782,6 +1783,18 @@ the first argument and CBARGS as the rest of arguments." (add-hook 'kill-emacs-query-functions 'ein:notebook-close-notebooks t) +;;;###autoload +(defalias 'ein:exit 'ein:quit) + +;;;###autoload +(defun ein:quit (&optional force) + "Close all notebooks and servers." + (interactive "P") + (ein:notebook-close-notebooks force) + (when (featurep 'ein-jupyter) + (ein:jupyter-server-stop force))) ; autoloaded + + (defun ein:notebook-kill-buffer-callback () "Call notebook destructor. This function is called via `kill-buffer-hook'." ;; TODO - it remains a bug that neither `ein:notebook-kill-buffer-callback' @@ -1795,11 +1808,11 @@ the first argument and CBARGS as the rest of arguments." "Add \"notebook destructor\" to `kill-buffer-hook'." (add-hook 'kill-buffer-hook 'ein:notebook-kill-buffer-callback nil t)) -(lexical-let* ((the-mode (ein:notebook-choose-mode)) - (incompatible-func (lambda () - (when (boundp 'undo-tree-incompatible-major-modes) - (nconc undo-tree-incompatible-major-modes - (list the-mode)))))) +(let* ((the-mode (ein:notebook-choose-mode)) + (incompatible-func (lambda () + (when (boundp 'undo-tree-incompatible-major-modes) + (nconc undo-tree-incompatible-major-modes + (list the-mode)))))) (unless (funcall incompatible-func) (with-eval-after-load 'undo-tree (funcall incompatible-func)))) diff --git a/lisp/ein-notebooklist.el b/lisp/ein-notebooklist.el index 4bb363668..643d963ca 100644 --- a/lisp/ein-notebooklist.el +++ b/lisp/ein-notebooklist.el @@ -1,4 +1,4 @@ -;;; ein-notebooklist.el --- Notebook list buffer +;;; ein-notebooklist.el --- Notebook list buffer -*- lexical-binding: t -*- ;; Copyright (C) 2018- John M. Miller @@ -25,7 +25,6 @@ ;;; Code: -(eval-when-compile (require 'cl)) (require 'widget) (require 'cus-edit) @@ -73,7 +72,7 @@ is opened at first time.:: :type 'hook :group 'ein) -(defstruct ein:$notebooklist +(cl-defstruct ein:$notebooklist "Hold notebooklist variables. `ein:$notebooklist-url-or-port' @@ -119,12 +118,12 @@ is opened at first time.:: `(widget-create 'menu-choice :tag ,tag :value ,custom-var - :notify (lambda (widget &rest ignore) + :notify (lambda (widget &rest _ignore) (run-at-time 1 nil #'ein:notebooklist-reload) (setq ,custom-var (widget-value widget))) ,@(mapcar (lambda (const) - `'(item :tag ,(third const) :value ,(fourth const))) - (rest (custom-variable-type custom-var))))) + `'(item :tag ,(cl-third const) :value ,(cl-fourth const))) + (cl-rest (custom-variable-type custom-var))))) (define-obsolete-variable-alias 'ein:notebooklist 'ein:%notebooklist% "0.1.2") @@ -174,40 +173,40 @@ This function adds NBLIST to `ein:notebooklist-map'." (get-buffer-create (format ein:notebooklist-buffer-name-template url-or-port))) +(defvar ein:jupyter-default-server-command) +(defun ein:jupyter-notebook-list (caller) + "Return list of local notebooks as JSON." + (condition-case err + (mapcar #'ein:json-read-from-string + (process-lines ein:jupyter-default-server-command + "notebook" "list" "--json")) + ;; often there is no local jupyter installation + (error (ein:log 'info "ein:jupyter-notebook-list(%s): %s" caller err) + nil))) + (defun ein:crib-token (url-or-port) "Shell out to jupyter for its credentials knowledge. Return list of (PASSWORD TOKEN)." - (ein:aif (loop for line in (condition-case err - (process-lines ein:jupyter-default-server-command - "notebook" "list" "--json") - ;; often there is no local jupyter installation - (error (ein:log 'info "ein:crib-token: %s" err) nil)) - with token0 - with password0 - when (destructuring-bind - (&key password url token &allow-other-keys) - (ein:json-read-from-string line) - (prog1 (equal (ein:url url) url-or-port) - (setq password0 password) ;; t or :json-false - (setq token0 token))) - return (list password0 token0)) + (ein:aif (cl-loop for json in (ein:jupyter-notebook-list 'ein:crib-token) + with token0 + with password0 + when (cl-destructuring-bind (&key password url token &allow-other-keys) json + (prog1 (equal (ein:url url) url-or-port) + (setq password0 password) ;; t or :json-false + (setq token0 token))) + return (list password0 token0)) it (list nil nil))) (defun ein:crib-running-servers () "Shell out to jupyter for running servers." - (loop for line in (condition-case err - (process-lines ein:jupyter-default-server-command - "notebook" "list" "--json") - (error (ein:log 'info "ein:crib-running-servers: %s" err) - nil)) - collecting (destructuring-bind - (&key url &allow-other-keys) - (ein:json-read-from-string line) (ein:url url)))) + (cl-loop for json in (ein:jupyter-notebook-list 'ein:crib-running-servers) + collecting (cl-destructuring-bind (&key url &allow-other-keys) json + (ein:url url)))) (defun ein:notebooklist-token-or-password (url-or-port) "Return token or password (jupyter requires one or the other but not both) for URL-OR-PORT. Empty string token means all authentication disabled. Nil means don't know." - (multiple-value-bind (password-p token) (ein:crib-token url-or-port) + (cl-multiple-value-bind (password-p token) (ein:crib-token url-or-port) (autoload 'ein:jupyter-server-conn-info "ein-jupyter") - (multiple-value-bind (my-url-or-port my-token) (ein:jupyter-server-conn-info) + (cl-multiple-value-bind (my-url-or-port my-token) (ein:jupyter-server-conn-info) (cond ((eq password-p t) (read-passwd (format "Password for %s: " url-or-port))) ((and (stringp token) (eql password-p :json-false)) token) ((equal url-or-port my-url-or-port) my-token) @@ -241,11 +240,11 @@ TODO: going to maintain jupyterhub hooks here " (unless path (setq path "")) (setq url-or-port (ein:url url-or-port)) ;; should work towards not needing this - (lexical-let* ((url-or-port url-or-port) - (path path) - (success (apply-partially #'ein:notebooklist-open--finish - url-or-port restore-point-p callback)) - (failure errback)) + (let* ((url-or-port url-or-port) + (path path) + (success (apply-partially #'ein:notebooklist-open--finish + url-or-port restore-point-p callback)) + (failure errback)) (if (and (not resync) (ein:notebooklist-list-get url-or-port)) (ein:content-query-contents url-or-port path success failure) (ein:query-notebook-version @@ -295,7 +294,7 @@ automatically be called during calls to `ein:notebooklist-open`." (unless ein:notebooklist--keepalive-timer (message "Enabling notebooklist keepalive...") (let ((success - (lambda (content) + (lambda (_content) (ein:log 'info "Refreshing notebooklist connection."))) (refresh-time (* ein:notebooklist-keepalive-refresh-time 60 60))) (setq ein:notebooklist--keepalive-timer @@ -335,9 +334,8 @@ automatically be called during calls to `ein:notebooklist-open`." (funcall callback (current-buffer) url-or-port))) (current-buffer)))) -(defun* ein:notebooklist-open-error (url-or-port path - &key error-thrown - &allow-other-keys) +(cl-defun ein:notebooklist-open-error (url-or-port path + &key error-thrown &allow-other-keys) (ein:log 'error "ein:notebooklist-open-error %s: ERROR %s DATA %s" (concat (file-name-as-directory url-or-port) path) (car error-thrown) (cdr error-thrown))) @@ -392,14 +390,12 @@ This function is called via `ein:notebook-after-rename-hook'." :success (apply-partially #'ein:notebooklist-new-notebook-success url-or-port kernelspec path callback no-pop)))) -(defun* ein:notebooklist-new-notebook-success (url-or-port - kernelspec - path - callback - no-pop - &key - data - &allow-other-keys) +(cl-defun ein:notebooklist-new-notebook-success (url-or-port + kernelspec + path + callback + no-pop + &key data &allow-other-keys) (let ((nbname (plist-get data :name)) (nbpath (plist-get data :path))) (when (< (ein:notebook-version-numeric url-or-port) 3) @@ -409,8 +405,8 @@ This function is called via `ein:notebook-after-rename-hook'." (ein:notebook-open url-or-port nbpath kernelspec callback nil no-pop) (ein:notebooklist-open* url-or-port path nil t))) -(defun* ein:notebooklist-new-notebook-error - (url-or-port kernelspec path callback no-pop retry +(cl-defun ein:notebooklist-new-notebook-error + (url-or-port kernelspec _path callback no-pop retry &key symbol-status error-thrown &allow-other-keys) (let ((notice (format "ein:notebooklist-new-notebook-error: %s %s" symbol-status error-thrown))) @@ -436,7 +432,7 @@ This function is called via `ein:notebook-after-rename-hook'." (list url-or-port kernelspec name))) (unless callback (setq callback #'ignore)) - (add-function :before callback + (add-function :before (var callback) (apply-partially (lambda (name* notebook _created) (with-current-buffer (ein:notebook-buffer notebook) @@ -450,10 +446,10 @@ This function is called via `ein:notebook-after-rename-hook'." (defun ein:notebooklist-delete-notebook (path &optional callback) "CALLBACK with no arguments, e.g., semaphore" - (lexical-let* ((path path) - (notebooklist ein:%notebooklist%) - (callback callback) - (url-or-port (ein:$notebooklist-url-or-port notebooklist))) + (let* ((path path) + (notebooklist ein:%notebooklist%) + (callback callback) + (url-or-port (ein:$notebooklist-url-or-port notebooklist))) (unless callback (setq callback (lambda () (ein:notebooklist-reload notebooklist)))) (ein:query-singleton-ajax (list 'notebooklist-delete-notebook (ein:url url-or-port path)) @@ -462,10 +458,9 @@ This function is called via `ein:notebook-after-rename-hook'." :type "DELETE" :complete (apply-partially #'ein:notebooklist-delete-notebook--complete (ein:url url-or-port path) callback)))) -(defun* ein:notebooklist-delete-notebook--complete (url callback - &key data response symbol-status - &allow-other-keys - &aux (resp-string (format "STATUS: %s DATA: %s" (request-response-status-code response) data))) +(cl-defun ein:notebooklist-delete-notebook--complete (_url callback + &key data response _symbol-status &allow-other-keys + &aux (resp-string (format "STATUS: %s DATA: %s" (request-response-status-code response) data))) (ein:log 'debug "ein:notebooklist-delete-notebook--complete %s" resp-string) (when callback (funcall callback))) @@ -478,7 +473,7 @@ This function is called via `ein:notebook-after-rename-hook'." (setf current-path (concat current-path "/" p) pairs (append pairs (list (cons p current-path))))))) -(defun* ein:nblist--sort-group (group by-param order) +(cl-defun ein:nblist--sort-group (group by-param order) (sort group #'(lambda (x y) (cond ((eql order :ascending) (string-lessp (plist-get x by-param) @@ -504,19 +499,19 @@ This function is called via `ein:notebook-after-rename-hook'." sort-order))) (-concat dirs nbs files))) -(defun render-header-ipy2 (&rest args) +(defun render-header-ipy2 (&rest _args) "Render the header (for ipython2)." ;; Create notebook list (widget-insert (format "IPython %s Notebook list\n\n" (ein:$notebooklist-api-version ein:%notebooklist%))) (let ((breadcrumbs (generate-breadcrumbs (ein:$notebooklist-path ein:%notebooklist%)))) (dolist (p breadcrumbs) - (lexical-let ((name (car p)) - (path (cdr p))) + (let ((name (car p)) + (path (cdr p))) (widget-insert " | ") (widget-create 'link - :notify (lambda (&rest ignore) + :notify (lambda (&rest _ignore) (ein:notebooklist-login (ein:$notebooklist-url-or-port ein:%notebooklist%) path)) name))) @@ -524,25 +519,26 @@ This function is called via `ein:notebook-after-rename-hook'." (widget-create 'link - :notify (lambda (&rest ignore) (ein:notebooklist-new-notebook + :notify (lambda (&rest _ignore) (ein:notebooklist-new-notebook (ein:$notebooklist-url-or-port ein:%notebooklist%) nil)) "New Notebook") (widget-insert " ") (widget-create 'link - :notify (lambda (&rest ignore) (ein:notebooklist-reload nil t)) + :notify (lambda (&rest _ignore) (ein:notebooklist-reload nil t)) "Reload List") (widget-insert " ") (widget-create 'link - :notify (lambda (&rest ignore) + :notify (lambda (&rest _ignore) (browse-url (ein:url (ein:$notebooklist-url-or-port ein:%notebooklist%)))) "Open In Browser") (widget-insert "\n")) -(defun render-header* (url-or-port &rest args) +(defvar ein:jupyter-default-kernel) +(defun render-header* (url-or-port &rest _args) "Render the header (for ipython>=3)." (with-current-buffer (ein:notebooklist-get-buffer url-or-port) (widget-insert @@ -550,45 +546,45 @@ This function is called via `ein:notebook-after-rename-hook'." (let ((breadcrumbs (generate-breadcrumbs (ein:$notebooklist-path ein:%notebooklist%)))) (dolist (p breadcrumbs) - (lexical-let ((url-or-port url-or-port) - (name (car p)) - (path (cdr p))) + (let ((url-or-port url-or-port) + (name (car p)) + (path (cdr p))) (widget-insert " | ") (widget-create 'link - :notify (lambda (&rest ignore) + :notify (lambda (&rest _ignore) (ein:notebooklist-open* url-or-port path nil nil - (lambda (buffer url-or-port) + (lambda (buffer _url-or-port) (pop-to-buffer buffer)))) name))) (widget-insert " |\n\n")) - (lexical-let* ((url-or-port url-or-port) - (kernels (ein:list-available-kernels url-or-port))) + (let* ((url-or-port url-or-port) + (kernels (ein:list-available-kernels url-or-port))) (if (null ein:%notebooklist-new-kernel%) (setq ein:%notebooklist-new-kernel% (ein:get-kernelspec url-or-port (caar kernels)))) (widget-create 'link - :notify (lambda (&rest ignore) (ein:notebooklist-new-notebook - url-or-port - ein:%notebooklist-new-kernel%)) + :notify (lambda (&rest _ignore) + (ein:notebooklist-new-notebook url-or-port + ein:%notebooklist-new-kernel%)) "New Notebook") (widget-insert " ") (widget-create 'link - :notify (lambda (&rest ignore) (ein:notebooklist-reload nil t)) + :notify (lambda (&rest _ignore) (ein:notebooklist-reload nil t)) "Resync") (widget-insert " ") (widget-create 'link - :notify (lambda (&rest ignore) + :notify (lambda (&rest _ignore) (browse-url (ein:url url-or-port))) "Open In Browser") (widget-insert "\n\nCreate New Notebooks Using Kernel:\n") (let* ((radio-widget (widget-create 'radio-button-choice :value (and ein:%notebooklist-new-kernel% (ein:$kernelspec-name ein:%notebooklist-new-kernel%)) - :notify (lambda (widget &rest ignore) + :notify (lambda (widget &rest _ignore) (setq ein:%notebooklist-new-kernel% (ein:get-kernelspec url-or-port (widget-value widget))) (message "New notebooks will be started using the %s kernel." @@ -607,32 +603,32 @@ This function is called via `ein:notebook-after-rename-hook'." (symbol-name ein:jupyter-default-kernel)))) (widget-insert "\n")))))) -(defun render-opened-notebooks (url-or-port &rest args) +(defun render-opened-notebooks (url-or-port &rest _args) "Render the opened notebooks section (for ipython>=3)." ;; Opened Notebooks Section (with-current-buffer (ein:notebooklist-get-buffer url-or-port) (widget-insert "\n---------- All Opened Notebooks ----------\n\n") - (loop for buffer in (ein:notebook-opened-buffers) - do (progn (widget-create - 'link - :notify (lexical-let ((buffer buffer)) - (lambda (&rest ignore) - (condition-case err - (switch-to-buffer buffer) - (error - (message "%S" err) - (ein:notebooklist-reload))))) - "Open") - (widget-create - 'link - :notify (lexical-let ((buffer buffer)) - (lambda (&rest ignore) - (if (buffer-live-p buffer) - (kill-buffer buffer)) - (run-at-time 1 nil #'ein:notebooklist-reload))) - "Close") - (widget-insert " : " (buffer-name buffer)) - (widget-insert "\n"))))) + (cl-loop for buffer in (ein:notebook-opened-buffers) + do (progn (widget-create + 'link + :notify (let ((buffer buffer)) + (lambda (&rest _ignore) + (condition-case err + (switch-to-buffer buffer) + (error + (message "%S" err) + (ein:notebooklist-reload))))) + "Open") + (widget-create + 'link + :notify (let ((buffer buffer)) + (lambda (&rest _ignore) + (if (buffer-live-p buffer) + (kill-buffer buffer)) + (run-at-time 1 nil #'ein:notebooklist-reload))) + "Close") + (widget-insert " : " (buffer-name buffer)) + (widget-insert "\n"))))) (defun ein:format-nbitem-data (name last-modified) (let ((dt (date-to-time last-modified))) @@ -646,77 +642,77 @@ This function is called via `ein:notebook-after-rename-hook'." (ein:make-sorting-widget "Sort by" ein:notebooklist-sort-field) (ein:make-sorting-widget "In Order" ein:notebooklist-sort-order) (widget-insert "\n") - (loop for note in (ein:notebooklist--order-data (ein:$notebooklist-data ein:%notebooklist%) - ein:notebooklist-sort-field - ein:notebooklist-sort-order) - for name = (plist-get note :name) - for path = (plist-get note :path) - for last-modified = (plist-get note :last_modified) - for type = (plist-get note :type) - for opened-notebook-maybe = (ein:notebook-get-opened-notebook url-or-port path) - do (widget-insert " ") - if (string= type "directory") - do (progn (widget-create - 'link - :notify (lexical-let ((url-or-port url-or-port) - (name name)) - (lambda (&rest ignore) - ;; each directory creates a whole new notebooklist - (ein:notebooklist-open* url-or-port - (concat (file-name-as-directory - (ein:$notebooklist-path ein:%notebooklist%)) - name) - nil nil - (lambda (buffer url-or-port) (pop-to-buffer buffer))))) - "Dir") - (widget-insert " : " name) - (widget-insert "\n")) - if (and (string= type "file") (> (ein:notebook-version-numeric url-or-port) 2)) - do (progn (widget-create - 'link - :notify (lexical-let ((url-or-port url-or-port) - (path path)) - (lambda (&rest ignore) - (ein:file-open url-or-port path))) - "Open") - (widget-insert " ------ ") + (cl-loop for note in (ein:notebooklist--order-data (ein:$notebooklist-data ein:%notebooklist%) + ein:notebooklist-sort-field + ein:notebooklist-sort-order) + for name = (plist-get note :name) + for path = (plist-get note :path) + for last-modified = (plist-get note :last_modified) + for type = (plist-get note :type) + ;; for opened-notebook-maybe = (ein:notebook-get-opened-notebook url-or-port path) + do (widget-insert " ") + if (string= type "directory") + do (progn (widget-create + 'link + :notify (let ((url-or-port url-or-port) + (name name)) + (lambda (&rest _ignore) + ;; each directory creates a whole new notebooklist + (ein:notebooklist-open* url-or-port + (concat (file-name-as-directory + (ein:$notebooklist-path ein:%notebooklist%)) + name) + nil nil + (lambda (buffer _url-or-port) (pop-to-buffer buffer))))) + "Dir") + (widget-insert " : " name) + (widget-insert "\n")) + if (and (string= type "file") (> (ein:notebook-version-numeric url-or-port) 2)) + do (progn (widget-create + 'link + :notify (let ((url-or-port url-or-port) + (path path)) + (lambda (&rest _ignore) + (ein:file-open url-or-port path))) + "Open") + (widget-insert " ------ ") + (widget-create + 'link + :notify (let ((_path path)) + (lambda (&rest _ignore) + (ein:file-delete url-or-port path))) + "Delete") + (widget-insert " : " (ein:format-nbitem-data name last-modified)) + (widget-insert "\n")) + if (string= type "notebook") + do (progn (widget-create + 'link + :notify (let ((url-or-port url-or-port) + (path path)) + (lambda (&rest _ignore) + (run-at-time 3 nil #'ein:notebooklist-reload) + (ein:notebook-open url-or-port path))) + "Open") + (widget-insert " ") + (if (gethash path sessions) (widget-create 'link - :notify (lexical-let ((path path)) - (lambda (&rest ignore) - (message "[EIN]: NBlist delete file command. Implement me!"))) - "Delete") - (widget-insert " : " (ein:format-nbitem-data name last-modified)) - (widget-insert "\n")) - if (string= type "notebook") - do (progn (widget-create - 'link - :notify (lexical-let ((url-or-port url-or-port) - (path path)) - (lambda (&rest ignore) - (run-at-time 3 nil #'ein:notebooklist-reload) - (ein:notebook-open url-or-port path))) - "Open") - (widget-insert " ") - (if (gethash path sessions) - (widget-create - 'link - :notify (lexical-let ((url url-or-port) - (session (car (gethash path sessions)))) - (lambda (&rest ignore) - (ein:kernel-delete--from-session-id url session #'ein:notebooklist-reload))) - "Stop") - (widget-insert "------")) - (widget-insert " ") - (widget-create - 'link - :notify (lexical-let ((path path)) - (lambda (&rest ignore) - (ein:notebooklist-delete-notebook-ask - path))) - "Delete") - (widget-insert " : " (ein:format-nbitem-data name last-modified)) - (widget-insert "\n"))))) + :notify (let ((url url-or-port) + (session (car (gethash path sessions)))) + (lambda (&rest _ignore) + (ein:kernel-delete--from-session-id url session #'ein:notebooklist-reload))) + "Stop") + (widget-insert "------")) + (widget-insert " ") + (widget-create + 'link + :notify (let ((path path)) + (lambda (&rest _ignore) + (ein:notebooklist-delete-notebook-ask + path))) + "Delete") + (widget-insert " : " (ein:format-nbitem-data name last-modified)) + (widget-insert "\n"))))) (defun ein:notebooklist-render (nb-version &optional restore-point) "Render notebook list widget. @@ -748,25 +744,25 @@ Notebook list data is passed via the buffer local variable (defun ein:notebooklist-list-paths (&optional content-type) "Return all files of CONTENT-TYPE for all sessions" (apply #'append - (loop for nblist in (ein:notebooklist-list) - for url-or-port = (ein:$notebooklist-url-or-port nblist) - collect - (loop for content in (ein:content-need-hierarchy url-or-port) - when (or (null content-type) - (string= (ein:$content-type content) content-type)) - collect (ein:url url-or-port (ein:$content-path content)))))) + (cl-loop for nblist in (ein:notebooklist-list) + for url-or-port = (ein:$notebooklist-url-or-port nblist) + collect + (cl-loop for content in (ein:content-need-hierarchy url-or-port) + when (or (null content-type) + (string= (ein:$content-type content) content-type)) + collect (ein:url url-or-port (ein:$content-path content)))))) (defun ein:notebooklist-parse-nbpath (nbpath) "Return `(,url-or-port ,path) from URL-OR-PORT/PATH" - (loop for url-or-port in (ein:notebooklist-keys) - if (search url-or-port nbpath :end2 (length url-or-port)) - return (list (substring nbpath 0 (length url-or-port)) - (substring nbpath (1+ (length url-or-port)))) - end - finally (ein:display-warning - (format "%s not among: %s" nbpath (ein:notebooklist-keys)) - :error))) + (cl-loop for url-or-port in (ein:notebooklist-keys) + if (cl-search url-or-port nbpath :end2 (length url-or-port)) + return (list (substring nbpath 0 (length url-or-port)) + (substring nbpath (1+ (length url-or-port)))) + end + finally (ein:display-warning + (format "%s not among: %s" nbpath (ein:notebooklist-keys)) + :error))) (defsubst ein:notebooklist-ask-path (&optional content-type) (ein:completing-read (format "Open %s: " content-type) @@ -821,7 +817,7 @@ See also: (defun ein:notebooklist-open (url-or-port callback) "This is now an alias for ein:notebooklist-login" (interactive `(,(ein:notebooklist-ask-url-or-port) - ,(lambda (buffer url-or-port) (pop-to-buffer buffer)))) + ,(lambda (buffer _url-or-port) (pop-to-buffer buffer)))) (ein:notebooklist-login url-or-port callback)) (make-obsolete 'ein:notebooklist-open 'ein:notebooklist-login "0.14.2") @@ -841,17 +837,17 @@ See also: CALLBACK takes two arguments, the buffer created by ein:notebooklist-open--success and the url-or-port argument of ein:notebooklist-open*." (interactive `(,(ein:notebooklist-ask-url-or-port) - ,(lambda (buffer url-or-port) (pop-to-buffer buffer)) + ,(lambda (buffer _url-or-port) (pop-to-buffer buffer)) ,(if current-prefix-arg (ein:notebooklist-ask-user-pw-pair "Cookie name" "Cookie content")))) - (unless callback (setq callback (lambda (buffer url-or-port)))) + (unless callback (setq callback (lambda (_buffer _url-or-port)))) (when cookie-plist (let* ((parsed-url (url-generic-parse-url (file-name-as-directory url-or-port))) (domain (url-host parsed-url)) (securep (string-match "^wss://" url-or-port))) - (loop for (name content) on cookie-plist by (function cddr) - for line = (mapconcat #'identity (list domain "FALSE" (car (url-path-and-query parsed-url)) (if securep "TRUE" "FALSE") "0" (symbol-name name) (concat content "\n")) "\t") - do (write-region line nil (request--curl-cookie-jar) 'append)))) + (cl-loop for (name content) on cookie-plist by (function cddr) + for line = (mapconcat #'identity (list domain "FALSE" (car (url-path-and-query parsed-url)) (if securep "TRUE" "FALSE") "0" (symbol-name name) (concat content "\n")) "\t") + do (write-region line nil (request--curl-cookie-jar) 'append)))) (let ((token (ein:notebooklist-token-or-password url-or-port))) (cond ((null token) ;; don't know @@ -876,16 +872,13 @@ and the url-or-port argument of ein:notebooklist-open*." (request-response--raw-header response)) (funcall errback)) -(defun* ein:notebooklist-login--complete (url-or-port &key data response - &allow-other-keys - &aux (resp-string (format "STATUS: %s DATA: %s" (request-response-status-code response) data))) +(cl-defun ein:notebooklist-login--complete (_url-or-port &key data response &allow-other-keys + &aux (resp-string (format "STATUS: %s DATA: %s" (request-response-status-code response) data))) (ein:log 'debug "ein:notebooklist-login--complete %s" resp-string)) -(defun* ein:notebooklist-login--success (url-or-port callback errback token iteration - &key data response error-thrown - &allow-other-keys - &aux - (response-status (request-response-status-code response))) +(cl-defun ein:notebooklist-login--success (url-or-port callback errback token iteration + &key data response error-thrown &allow-other-keys + &aux (response-status (request-response-status-code response))) (cond ((plist-get data :bad-page) (if (>= iteration 0) (ein:notebooklist-login--error-1 url-or-port error-thrown response errback) @@ -893,21 +886,21 @@ and the url-or-port argument of ein:notebooklist-open*." (ein:notebooklist-login--iteration url-or-port callback errback token (1+ iteration) response-status))) ((request-response-header response "x-jupyterhub-version") (let ((pam-plist (ein:notebooklist-ask-user-pw-pair "User" "Password"))) - (destructuring-bind (user pw) - (loop for (user pw) on pam-plist by (function cddr) - return (list (symbol-name user) pw)) + (cl-destructuring-bind (user pw) + (cl-loop for (user pw) on pam-plist by (function cddr) + return (list (symbol-name user) pw)) (ein:jupyterhub-connect url-or-port user pw callback)))) (t (ein:notebooklist-login--success-1 url-or-port callback errback)))) -(defun* ein:notebooklist-login--error - (url-or-port token callback errback iteration &key - data - symbol-status - response - error-thrown - &allow-other-keys - &aux - (response-status (request-response-status-code response))) +(cl-defun ein:notebooklist-login--error + (url-or-port token callback errback iteration + &key + _data + symbol-status + response + error-thrown + &allow-other-keys + &aux (response-status (request-response-status-code response))) (cond ((and response-status (< iteration 0)) (setq token (read-passwd (format "Password for %s: " url-or-port))) (ein:notebooklist-login--iteration url-or-port callback errback token (1+ iteration) response-status)) @@ -937,30 +930,30 @@ on all the notebooks opened from the current notebooklist." (equal (ein:$notebook-url-or-port nb) (ein:$notebooklist-url-or-port current-nblist)))))) (ein:notebooklist-open* new-url-or-port) - (loop for x upfrom 0 by 1 - until (or (get-buffer (format ein:notebooklist-buffer-name-template new-url-or-port)) - (= x 100)) - do (sit-for 0.1)) + (cl-loop for x upfrom 0 by 1 + until (or (get-buffer (format ein:notebooklist-buffer-name-template new-url-or-port)) + (= x 100)) + do (sit-for 0.1)) (dolist (nb open-nb) (ein:notebook-update-url-or-port new-url-or-port nb)) (kill-buffer (ein:notebooklist-get-buffer old-url)) - (ein:notebooklist-open* new-url-or-port nil nil nil (lambda (buffer url-or-port) + (ein:notebooklist-open* new-url-or-port nil nil nil (lambda (buffer _url-or-port) (pop-to-buffer buffer))))) (defun ein:notebooklist-change-url-port--deferred (new-url-or-port) - (lexical-let* ((current-nblist ein:%notebooklist%) - (old-url (ein:$notebooklist-url-or-port current-nblist)) - (new-url-or-port new-url-or-port) - (open-nb (ein:notebook-opened-notebooks - (lambda (nb) - (equal (ein:$notebook-url-or-port nb) - (ein:$notebooklist-url-or-port current-nblist)))))) + (let* ((current-nblist ein:%notebooklist%) + (old-url (ein:$notebooklist-url-or-port current-nblist)) + (new-url-or-port new-url-or-port) + (open-nb (ein:notebook-opened-notebooks + (lambda (nb) + (equal (ein:$notebook-url-or-port nb) + (ein:$notebooklist-url-or-port current-nblist)))))) (deferred:$ (deferred:next (lambda () (ein:notebooklist-open* new-url-or-port) - (loop until (get-buffer (format ein:notebooklist-buffer-name-template new-url-or-port)) - do (sit-for 0.1)))) + (cl-loop until (get-buffer (format ein:notebooklist-buffer-name-template new-url-or-port)) + do (sit-for 0.1)))) (deferred:nextc it (lambda () (dolist (nb open-nb) @@ -968,7 +961,7 @@ on all the notebooks opened from the current notebooklist." (deferred:nextc it (lambda () (kill-buffer (ein:notebooklist-get-buffer old-url)) - (ein:notebooklist-open* new-url-or-port nil nil nil (lambda (buffer url-or-port) + (ein:notebooklist-open* new-url-or-port nil nil nil (lambda (buffer _url-or-port) (pop-to-buffer buffer)))))))) ;;; Generic getter @@ -1007,7 +1000,7 @@ on all the notebooks opened from the current notebooklist." ("New Notebook (with name)" ein:notebooklist-new-notebook-with-name))))) -(defun ein:notebooklist-revert-wrapper (&optional ignore-auto noconfirm preserve-modes) +(defun ein:notebooklist-revert-wrapper (&optional _ignore-auto _noconfirm _preserve-modes) (ein:notebooklist-reload)) (define-derived-mode ein:notebooklist-mode special-mode "ein:notebooklist" diff --git a/lisp/ein-notification.el b/lisp/ein-notification.el index 56f6686cd..36733b3c9 100644 --- a/lisp/ein-notification.el +++ b/lisp/ein-notification.el @@ -25,7 +25,6 @@ ;;; Code: -(eval-when-compile (require 'cl)) (require 'eieio) (require 'ein-notebook) @@ -66,14 +65,14 @@ S-mouse-1/3 (Shift + left/right click): move this tab to left/right" just set the status (= event-type): (ein:notification-status-set NS EVENT-TYPE) where NS is `:kernel' or `:notebook' slot of NOTIFICATION." - (loop for ns in (list (slot-value notification 'kernel) - (slot-value notification 'notebook)) - for statuses = (mapcar #'car (slot-value ns 's2m)) - do (loop for st in statuses - do (ein:events-on events - st ; = event-type - #'ein:notification--callback - (cons ns st)))) + (cl-loop for ns in (list (slot-value notification 'kernel) + (slot-value notification 'notebook)) + for statuses = (mapcar #'car (slot-value ns 's2m)) + do (cl-loop for st in statuses + do (ein:events-on events + st ; = event-type + #'ein:notification--callback + (cons ns st)))) (ein:events-on events 'notebook_checkpoint_created.Notebook #'ein:notification--fadeout-callback @@ -180,26 +179,26 @@ insert-prev insert-next move-prev move-next)" (ein:join-str " " (append - (loop for i from 1 - for elem in list - if (eq elem current) - collect (propertize - (or (ein:and-let* ((name (funcall get-name elem))) - (format "/%d: %s\\" i name)) - (format "/%d\\" i)) - 'ein:worksheet elem - 'keymap ein:header-line-tab-map - 'help-echo ein:header-line-tab-help - 'mouse-face 'highlight - 'face 'ein:notification-tab-selected) - else - collect (propertize - (format "/%d\\" i) - 'ein:worksheet elem - 'keymap ein:header-line-tab-map - 'help-echo ein:header-line-tab-help - 'mouse-face 'highlight - 'face 'ein:notification-tab-normal)) + (cl-loop for i from 1 + for elem in list + if (eq elem current) + collect (propertize + (or (ein:and-let* ((name (funcall get-name elem))) + (format "/%d: %s\\" i name)) + (format "/%d\\" i)) + 'ein:worksheet elem + 'keymap ein:header-line-tab-map + 'help-echo ein:header-line-tab-help + 'mouse-face 'highlight + 'face 'ein:notification-tab-selected) + else + collect (propertize + (format "/%d\\" i) + 'ein:worksheet elem + 'keymap ein:header-line-tab-map + 'help-echo ein:header-line-tab-help + 'mouse-face 'highlight + 'face 'ein:notification-tab-normal)) (list (propertize "[+]" 'keymap ein:header-line-insert-tab-map @@ -236,7 +235,7 @@ insert-prev insert-next move-prev move-next)" (declare (debug (form &rest form)) (indent 1)) ;; See: (info "(elisp) Click Events") - `(destructuring-bind + `(cl-destructuring-bind (event-type (window pos-or-area (x . y) timestamp object text-pos (col . row) @@ -273,13 +272,13 @@ insert-prev insert-next move-prev move-next)" (defmacro ein:header-line-define-mouse-commands (&rest name-slot-list) `(progn - ,@(loop for (name slot) on name-slot-list by 'cddr - collect - `(defun ,name (key-event) - ,(format "Run slot %s + ,@(cl-loop for (name slot) on name-slot-list by 'cddr + collect + `(defun ,name (key-event) + ,(format "Run slot %s Generated by `ein:header-line-define-mouse-commands'" slot) - (interactive "e") - (ein:header-line-do-slot-function key-event ,slot))))) + (interactive "e") + (ein:header-line-do-slot-function key-event ,slot))))) (ein:header-line-define-mouse-commands ein:header-line-delete-this-tab :delete diff --git a/lisp/ein-org.el b/lisp/ein-org.el index f6a98bc25..e09a43d8e 100644 --- a/lisp/ein-org.el +++ b/lisp/ein-org.el @@ -32,11 +32,8 @@ ;; FIXME: Separate org-unrelated cores from the following code and ;; expose them as API in ein-link.el. -(defun* ein:org-goto-link (notebook created - &key - worksheet-index - search - &allow-other-keys) +(cl-defun ein:org-goto-link (notebook created + &key worksheet-index search &allow-other-keys) (if created (ein:log 'info "Linked notebook did not exist. Created a new one.") (if worksheet-index @@ -53,7 +50,7 @@ This function is to be used for FOLLOW function of `org-add-link-type'." (let ((link (read link-path))) - (destructuring-bind (&key url-or-port name &allow-other-keys) + (cl-destructuring-bind (&key url-or-port name &allow-other-keys) link (ein:notebook-open url-or-port name)))) diff --git a/lisp/ein-output-area.el b/lisp/ein-output-area.el index 6a342178e..69d8bda14 100644 --- a/lisp/ein-output-area.el +++ b/lisp/ein-output-area.el @@ -26,7 +26,6 @@ ;;; Code: -(eval-when-compile (require 'cl)) (require 'xml) (require 'ein-core) @@ -49,10 +48,10 @@ can be handled by the xml module." (defun ein:xml-tree-apply (dom operation) "Apply OPERATION on nodes in DOM. Apply the same OPERATION on the next level children when it returns `nil'." - (loop for child in (xml-node-children dom) - if (and (not (funcall operation child)) - (ein:xml-node-p child)) - do (ein:xml-tree-apply child operation))) + (cl-loop for child in (xml-node-children dom) + if (and (not (funcall operation child)) + (ein:xml-node-p child)) + do (ein:xml-tree-apply child operation))) (defun ein:xml-replace-attributes (dom tag attr replace-p replacer) "Replace value of ATTR of TAG in DOM using REPLACER diff --git a/lisp/ein-pkg.el b/lisp/ein-pkg.el index 466874288..04e9c4833 100644 --- a/lisp/ein-pkg.el +++ b/lisp/ein-pkg.el @@ -2,7 +2,7 @@ "0.16.2" "Emacs IPython Notebook" '((emacs "25") - (websocket "20190620.338") + (websocket "20191017.30") (auto-complete "1.4.0") (request "20190621.1622") (deferred "0.5") diff --git a/lisp/ein-process.el b/lisp/ein-process.el index 6983c0fe9..4b5da986e 100644 --- a/lisp/ein-process.el +++ b/lisp/ein-process.el @@ -25,8 +25,6 @@ ;;; Code: -(eval-when-compile (require 'cl)) - (require 'ein-core) (require 'ein-jupyter) (require 'ein-file) @@ -83,7 +81,7 @@ :type 'string :group 'ein) -(defstruct ein:$process +(cl-defstruct ein:$process "Hold process variables. `ein:$process-pid' : integer @@ -111,48 +109,42 @@ (defun ein:process-suitable-notebook-dir (filename) "Return the uppermost parent dir of DIR that contains ipynb files." (let ((fn (expand-file-name filename))) - (loop with directory = (directory-file-name - (if (file-regular-p fn) - (file-name-directory (directory-file-name fn)) - fn)) - with suitable = directory - until (string= (file-name-nondirectory directory) "") - do (if (directory-files directory nil "\\.ipynb$") - (setq suitable directory)) - (setq directory (directory-file-name (file-name-directory directory))) - finally return suitable))) + (cl-loop with directory = (directory-file-name + (if (file-regular-p fn) + (file-name-directory (directory-file-name fn)) + fn)) + with suitable = directory + until (string= (file-name-nondirectory directory) "") + do (if (directory-files directory nil "\\.ipynb$") + (setq suitable directory)) + (setq directory (directory-file-name (file-name-directory directory))) + finally return suitable))) (defun ein:process-refresh-processes () "Use `jupyter notebook list --json` to populate ein:%processes%" (clrhash ein:%processes%) - (loop for line in (condition-case err - (process-lines ein:jupyter-default-server-command - "notebook" "list" "--json") - ;; often there is no local jupyter installation - (error (ein:log 'info "ein:process-refresh-processes: %s" err) nil)) - do (destructuring-bind - (&key pid url notebook_dir &allow-other-keys) - (ein:json-read-from-string line) - (puthash (directory-file-name notebook_dir) - (make-ein:$process :pid pid - :url (ein:url url) - :dir (directory-file-name notebook_dir)) - ein:%processes%)))) + (cl-loop for json in (ein:jupyter-notebook-list 'ein:process-refresh-processes) + do (cl-destructuring-bind (&key pid url notebook_dir &allow-other-keys) json + (puthash (directory-file-name notebook_dir) + (make-ein:$process :pid pid + :url (ein:url url) + :dir (directory-file-name notebook_dir)) + ein:%processes%)))) (defun ein:process-dir-match (filename) "Return ein:process whose directory is prefix of FILENAME." - (loop for dir in (hash-table-keys ein:%processes%) - when (search dir filename) - return (gethash dir ein:%processes%))) + (cl-loop for dir in (hash-table-keys ein:%processes%) + when (cl-search dir filename) + return (gethash dir ein:%processes%))) (defun ein:process-url-match (url-or-port) "Return ein:process whose url matches URL-OR-PORT." - (loop with parsed-url-or-port = (url-generic-parse-url url-or-port) - for proc in (ein:process-processes) - for parsed-url-proc = (url-generic-parse-url (ein:process-url-or-port proc)) - when (and (string= (url-host parsed-url-or-port) (url-host parsed-url-proc)) - (= (url-port parsed-url-or-port) (url-port parsed-url-proc))) - return proc)) + (cl-loop with parsed-url-or-port = (url-generic-parse-url url-or-port) + for proc in (ein:process-processes) + for parsed-url-proc = (url-generic-parse-url (ein:process-url-or-port proc)) + when (and (string= (url-host parsed-url-or-port) (url-host parsed-url-proc)) + (= (url-port parsed-url-or-port) (url-port parsed-url-proc))) + return proc)) (defsubst ein:process-url-or-port (proc) "Naively construct url-or-port from ein:process PROC's port and ip fields" @@ -160,7 +152,7 @@ (defsubst ein:process-path (proc filename) "Construct path by eliding PROC's dir from filename" - (subseq filename (length (file-name-as-directory (ein:$process-dir proc))))) + (cl-subseq filename (length (file-name-as-directory (ein:$process-dir proc))))) (defun ein:process-open-notebook* (filename callback) "Open FILENAME as a notebook and start a notebook server if necessary. CALLBACK with arity 2 (passed into `ein:notebook-open--callback')." @@ -178,7 +170,7 @@ (ein:notebooklist-login url-or-port callback2))) (let* ((nbdir (read-directory-name "Notebook directory: " (ein:process-suitable-notebook-dir filename))) - (path (subseq filename (length (file-name-as-directory nbdir)))) + (path (cl-subseq filename (length (file-name-as-directory nbdir)))) (callback2 (apply-partially (lambda (path* callback* buffer url-or-port) (pop-to-buffer buffer) (ein:notebook-open url-or-port @@ -192,7 +184,7 @@ current buffer as the only one argument." (interactive) (unless filename (setq filename buffer-file-name)) - (assert filename nil "Not visiting a file") + (cl-assert filename nil "Not visiting a file") (let ((callback2 (apply-partially (lambda (buffer buffer-callback* notebook created &rest args) (when (buffer-live-p buffer) diff --git a/lisp/ein-pytools.el b/lisp/ein-pytools.el index 9607f2bfd..41c7e27c7 100644 --- a/lisp/ein-pytools.el +++ b/lisp/ein-pytools.el @@ -25,14 +25,79 @@ ;;; Code: -(eval-when-compile (require 'cl)) - ;; for `ein:pytools-pandas-to-ses' (declare-function ses-yank-tsf "ses") (declare-function ses-command-hook "ses") (require 'ein-kernel) (require 'ein-notebook) +;; (require 'ein-shared-output) + + +;;; Support tooling in languages other than Python + +(defvar *ein:langtools-db* (make-hash-table) + "Lookup table tool support functions for a given language. Keys +are symbols representing the language of a running +kernel (i.e. (make-symbol (ein:kernelinfo-language )). +Values are an plist of (TOOL-COMMAND-NAME LANG-CODE). TOOL-COMMAND-NAME is a symbol, +LANG-CODE is a string suitable for passing to format.") + + +(defun ein:define-langtool-command (language tool-command code) + (ein:aif (gethash language *ein:langtools-db*) + (plist-put it tool-command code) + (setf (gethash language *ein:langtools-db*) + (list tool-command code)))) + +(defun ein:get-langtool-command (language tool-command) + (plist-get (gethash language *ein:langtools-db*) tool-command)) + +(eval-when-compile + (cl-defmacro ein:make-langtool (language defs) + (let ((expr (cl-loop for d in defs + collecting `(ein:define-langtool-command ',language ',(car d) ,(cdr d))))) + `(progn + ,@expr))) + (cl-defmacro ein:langtool-execute-command (kernel command &key args output) + (let ((lang (cl-gensym)) + (cmd (cl-gensym))) + `(let* ((,lang (intern (ein:$kernelspec-language (ein:$kernel-kernelspec ,kernel)))) + (,cmd (ein:get-langtool-command ,lang ,command))) + (if ,cmd + (ein:kernel-execute + ,kernel + ,(if (listp args) + `(format ,cmd ,@args) + `(format ,cmd ,args)) + ,(if (not (null output)) + `(list :output (cons ,@output)))) + (error "ein-pytools: Langtool command not defined for %s in language %s" ,command ,lang)))))) + + +(ein:make-langtool python + ((get-notebook-dir . "print(__import__('os').getcwd(),end='')") + (add-sys-path . "__import__('sys').path.append('%s')") + (request-tooltip . "__ein_print_object_info_for(%s)") + (request-help . "%s?") + (object-info-request . "__ein_print_object_info_for(%s)") + (find-source . "__ein_find_source('%s')") + (run-doctest . "__ein_run_docstring_examples(%s)") + (set-figure-size . "__ein_set_figure_size('[%s, %s]')") + (set-figure-dpi . "__ein_set_figure_dpi('%s')") + (set-figure-param . "__ein_set_matplotlib_param('%s', '%s', '%s')") + (get-figure-param . "__ein_get_matplotlib_params()") + (export . "__ein_export_nb(r'%s', '%s')") + (tools-file . "ein_remote_safe.py"))) + +(ein:make-langtool hy + ((find-source . "(ein-find-source \"%s\")") + (request-tooltip . "(ein-print-object-info-for \"%s\")") + (object-info-request . "(ein-print-object-info-for \"%s\")") + (tools-file . "ein_hytools.hy"))) + + + (defun ein:goto-file (filename lineno &optional other-window) "Jump to file FILEAME at line LINENO. @@ -61,7 +126,10 @@ If OTHER-WINDOW is non-`nil', open the file in the other window." (defun ein:pytools-load-safely (kernel) (with-temp-buffer - (let ((pytools-file (format "%s/%s" ein:source-dir "ein_remote_safe.py"))) + (let* ((fname (ein:get-langtool-command (intern (ein:$kernelspec-language + (ein:$kernel-kernelspec kernel))) + 'tools-file)) + (pytools-file (format "%s/%s" ein:source-dir fname))) (insert-file-contents pytools-file) (ein:kernel-execute kernel @@ -77,9 +145,7 @@ working." (ein:pytools-load-safely (ein:get-kernel-or-error))) (defun ein:pytools-add-sys-path (kernel) - (ein:kernel-execute - kernel - (format "__import__('sys').path.append('%s')" ein:source-dir))) + (ein:langtool-execute-command kernel 'add-sys-path :args ein:source-dir)) (defun ein:set-buffer-file-name (nb msg-type content -not-used-) (let ((buf (ein:notebook-buffer nb))) @@ -92,15 +158,16 @@ working." (plist-get content :text)))))))) (defun ein:pytools-get-notebook-dir (packed) - (multiple-value-bind (kernel notebook) packed - (ein:kernel-execute - kernel - (format "print(__import__('os').getcwd(),end='')") - (list - :output (cons - #'ein:set-buffer-file-name - notebook))))) - + (cl-multiple-value-bind (kernel notebook) packed + (ein:langtool-execute-command kernel 'get-notebook-dir + :output (#'ein:set-buffer-file-name notebook)))) +;; (ein:kernel-execute +;; kernel +;; (format "print(__import__('os').getcwd(),end='')") +;; (list +;; :output (cons +;; #'ein:set-buffer-file-name +;; notebook))) ;;; Tooltip and help @@ -114,16 +181,16 @@ working." (ein:object-at-point-or-error))) (unless (ein:pytools-magic-func-p func) (if (>= (ein:$kernel-api-version kernel) 3) - (ein:kernel-execute - kernel - (format "__ein_print_object_info_for(%s)" func) - (list - :output (cons - (lambda (name msg-type content -metadata-not-used-) - (ein:case-equal msg-type - (("stream" "display_data") - (ein:pytools-finish-tooltip name (ein:json-read-from-string (plist-get content :text)) nil)))) - func))) + (ein:langtool-execute-command kernel 'object-info-request :args func + :output + ((lambda (name msg-type content -metadata-not-used-) + (ein:case-equal msg-type + (("stream" "display_data") + (ein:pytools-finish-tooltip name + (ein:json-read-from-string + (plist-get content :text)) + nil)))) + func)) (ein:kernel-object-info-request kernel func (list :object_info_reply (cons #'ein:pytools-finish-tooltip nil)))))) @@ -179,7 +246,7 @@ pager buffer. You can explicitly specify the object by selecting it." (defun ein:pytools-jump-to-source-1 (packed msg-type content -metadata-not-used-) (ein:log 'debug "msg-type[[%s]] content[[%s]]" msg-type content) - (destructuring-bind (kernel object other-window notebook) packed + (cl-destructuring-bind (kernel object other-window notebook) packed (ein:log 'debug "object[[%s]] other-window[[%s]]" object other-window) (ein:case-equal msg-type (("stream" "display_data") @@ -187,7 +254,7 @@ pager buffer. You can explicitly specify the object by selecting it." (if (string-match ein:pytools-jump-to-source-not-found-regexp it) (ein:log 'info "Jumping to the source of %s...Not found" object) - (destructuring-bind (filename &optional lineno &rest ignore) + (cl-destructuring-bind (filename &optional lineno &rest ignore) (split-string it "\n") (setq lineno (string-to-number lineno) filename (ein:kernel-filename-from-python kernel filename)) @@ -215,14 +282,18 @@ pager buffer. You can explicitly specify the object by selecting it." (unless (equal (point) (marker-position last)) (push (point-marker) ein:pytools-jump-stack)) (setq ein:pytools-jump-stack (list (point-marker))))) - (ein:kernel-execute - kernel - (format "__ein_find_source('%s')" object) - (list - :output - (cons - #'ein:pytools-jump-to-source-1 - (list kernel object other-window notebook))))) + (ein:langtool-execute-command kernel 'find-source :args object + :output (#'ein:pytools-jump-to-source-1 + (list kernel object other-window notebook)))) + +;; (ein:kernel-execute +;; kernel +;; (format "__ein_find_source('%s')" object) +;; (list +;; :output +;; (cons +;; #'ein:pytools-jump-to-source-1 +;; (list kernel object other-window notebook)))) (defun ein:pytools-find-source (kernel object &optional callback) "Find the file and line where object is defined. @@ -231,24 +302,28 @@ useful for other purposes. If the definition for object can be found and when callback isort specified, the callback will be called with a cons of the filename and line number where object is defined." - (ein:kernel-execute - kernel - (format "__ein_find_source('%s')" object) - (list - :output - (cons - #'ein:pytools-finish-find-source - (list kernel object callback))))) + (ein:langtool-execute-command kernel 'find-source :args object + :output (#'ein:pytools-finish-find-source + (list kernel object callback)))) + +;; (ein:kernel-execute +;; kernel +;; (format "__ein_find_source('%s')" object) +;; (list +;; :output +;; (cons +;; #'ein:pytools-finish-find-source +;; (list kernel object callback)))) (defun ein:pytools-finish-find-source (packed msg-type content -ignored-) - (destructuring-bind (kernel object callback) packed + (cl-destructuring-bind (kernel object callback) packed (if (or (string= msg-type "stream") (string= msg-type "display_data")) (ein:aif (or (plist-get content :text) (plist-get content :data)) (if (string-match ein:pytools-jump-to-source-not-found-regexp it) (ein:log 'info "Source of %s not found" object) - (destructuring-bind (filename &optional lineno &rest ignore) + (cl-destructuring-bind (filename &optional lineno &rest ignore) (split-string it "\n") (if callback (funcall callback @@ -271,8 +346,8 @@ selecting it." (call-interactively #'xref-find-definitions))) (let ((kernel (ein:get-kernel)) (object (ein:object-at-point))) - (assert (ein:kernel-live-p kernel) nil "Kernel is not ready.") - (assert object nil "Object at point not found.") + (cl-assert (ein:kernel-live-p kernel) nil "Kernel is not ready.") + (cl-assert object nil "Object at point not found.") (ein:pytools-jump-to-source kernel object other-window (when ein:propagate-connect (ein:get-notebook)))))) @@ -298,9 +373,11 @@ given, open the last point in the other window." (defun ein:pytools-doctest () "Do the doctest of the object at point." (interactive) - (let ((object (ein:object-at-point))) + (let* ((object (ein:object-at-point)) + (kernel (ein:get-kernel)) + (cmd (ein:get-langtool-command kernel 'run-docstring))) (ein:shared-output-eval-string (ein:get-kernel) - (format "__ein_run_docstring_examples(%s)" object) + (format cmd object) t))) (defun ein:pytools-whos () @@ -318,8 +395,8 @@ You can explicitly specify the object by selecting it. (let ((object (ein:object-at-point))) (when ask (setq object (read-from-minibuffer "class or object: " object))) - (assert (and object (not (equal object ""))) - nil "Object at point not found.") + (cl-assert (and object (not (equal object ""))) + nil "Object at point not found.") (ein:shared-output-eval-string (ein:get-kernel) (format "%%hierarchy %s" object) t))) (defun ein:pytools-pandas-to-ses (dataframe) @@ -380,10 +457,10 @@ Currently EIN/IPython supports exporting to the following formats: "python" "rst" "slides")))) - (let* ((nb (first (ein:notebook-opened-notebooks - #'(lambda (nb) - (equal (buffer-name (ein:notebook-buffer nb)) - buffer))))) + (let* ((nb (car (ein:notebook-opened-notebooks + #'(lambda (nb) + (equal (buffer-name (ein:notebook-buffer nb)) + buffer))))) (json (json-encode (ein:notebook-to-json nb))) (name (format "*ein %s export: %s*" format (ein:$notebook-notebook-name nb))) (buffer (get-buffer-create name))) @@ -411,9 +488,60 @@ Currently EIN/IPython supports exporting to the following formats: (defun ein:pytools-set-figure-size (width height) "Set the default figure size for matplotlib figures. Works by setting `rcParams['figure.figsize']`." (interactive "nWidth: \nnHeight: ") - (ein:shared-output-eval-string (ein:get-kernel) - (format "__ein_set_figure_size(%s,%s)" width height) - nil)) + (let ((kernel (ein:get-kernel))) + (ein:langtool-execute-command kernel 'set-figure-size :args (width height))) + ) +;; (ein:shared-output-eval-string (ein:get-kernel) +;; (format "__ein_set_figure_size('[%s, %s]')" width height) +;; nil) + +(defun ein:pytools-set-figure-dpi (dpi) + "Set the default figure dpi for matplotlib figures. Works by setting `rcParams['figure.figsize']`." + (interactive "nFigure DPI: ") + (let ((kernel (ein:get-kernel))) + (ein:langtool-execute-command kernel 'set-figure-dpi :args dpi))) + +(defun ein:pytools-set-matplotlib-parameter (param value) + "Generically set any matplotlib parameter exposed in the matplotlib.pyplot.rcParams variable. Value is evaluated as a Python expression, so be careful of side effects." + (interactive + (list (completing-read "Parameter: " (ein:pytools--get-matplotlib-params) nil t) + (read-string "Value: " nil))) + (let* ((kernel (ein:get-kernel)) + (split (cl-position ?. param)) + (family (cl-subseq param 0 split)) + (setting (cl-subseq param (1+ split)))) + (ein:langtool-execute-command kernel 'set-figure-param :args (family setting value)))) + +(defun ein:pytools--get-matplotlib-params () + (let* ((kernel (ein:get-kernel)) + (cmd (ein:get-langtool-command kernel 'get-figure-param))) + (ein:shared-output-eval-string (ein:get-kernel) + (format cmd) + nil) + (with-current-buffer (ein:shared-output-create-buffer) + (ein:wait-until #'(lambda () + (slot-value (slot-value *ein:shared-output* :cell) :outputs)) + nil + 5.0) + (let ((outputs (first (slot-value (slot-value *ein:shared-output* :cell) :outputs)))) + (ein:json-read-from-string (plist-get outputs :text)))))) + +(defun ein:pytools--estimate-screen-dpi () + (let* ((pixel-width (display-pixel-width)) + (pixel-height (display-pixel-height)) + (in-width (/ (display-mm-width) 25.4)) + (in-height (/ (display-mm-height) 25.4))) + (values (/ pixel-width in-width) (/ pixel-height in-height)))) + +(defun ein:pytools-matplotlib-dpi-correction () + "Estimate the screen dpi and set the matplotlib rc parameter 'figure.dpi' to that value. Call this command *after* importing matplotlib into your notebook, else this setting will be overwritten after the first call to `import matplotlib' Further testing is needed to see how well this works on high resolution displays." + (interactive) + (multiple-value-bind (dpi-w dpi-h) (ein:pytools--estimate-screen-dpi) + (let ((dpi (floor (/ (+ dpi-w dpi-h) 2.0)))) + (ein:log 'info "Setting matplotlib scaling to: %s dpi" dpi) + (ein:pytools-set-figure-dpi dpi)))) + + (provide 'ein-pytools) diff --git a/lisp/ein-query.el b/lisp/ein-query.el index d39c1911f..158b14cc8 100644 --- a/lisp/ein-query.el +++ b/lisp/ein-query.el @@ -25,7 +25,6 @@ ;;; Code: -(eval-when-compile (require 'cl)) (require 'request) (require 'url) @@ -125,10 +124,8 @@ variable to a reasonable value you can avoid this situation." (ein:display-warning (format "The %s program was not found" request-curl) :error)))) -(defun* ein:query-singleton-ajax (key url &rest settings - &key - (timeout ein:query-timeout) - &allow-other-keys) +(cl-defun ein:query-singleton-ajax (key url &rest settings + &key (timeout ein:query-timeout) &allow-other-keys) "Do not cancel the old process if there is a process associated with KEY, then call `request' with URL and SETTINGS. KEY is compared by `equal'." @@ -136,12 +133,12 @@ KEY, then call `request' with URL and SETTINGS. KEY is compared by (with-local-quit (when timeout (setq settings (plist-put settings :timeout (/ timeout 1000.0)))) - (loop do (ein:query-running-process-table) - for running = (hash-table-count ein:query-running-process-table) - until (< running ein:max-simultaneous-queries) - do (ein:log 'warn "ein:query-singleton-ajax: %d running processes" - running) - do (sleep-for 3)) + (cl-loop do (ein:query-running-process-table) + for running = (hash-table-count ein:query-running-process-table) + until (< running ein:max-simultaneous-queries) + do (ein:log 'warn "ein:query-singleton-ajax: %d running processes" + running) + do (sleep-for 3)) (ein:aif (gethash key ein:query-running-process-table) (unless (request-response-done-p it) (ein:log 'debug "Race! %s %s" key (request-response-data it)))) @@ -160,12 +157,11 @@ KEY, then call `request' with URL and SETTINGS. KEY is compared by (defun ein:get-response-redirect (response) "Determine if the query has been redirected, and if so return then URL the request was redirected to." - (if (length (request-response-history response)) (let ((url (url-generic-parse-url (format "%s" (request-response-url response))))) (format "%s://%s:%s" (url-type url) (url-host url) - (url-port url))))) + (url-port url)))) ;;; Cookie diff --git a/lisp/ein-shared-output.el b/lisp/ein-shared-output.el index 5005293ea..b601f9f85 100644 --- a/lisp/ein-shared-output.el +++ b/lisp/ein-shared-output.el @@ -30,7 +30,6 @@ ;;; Code: -(eval-when-compile (require 'cl)) (require 'eieio) (require 'ein-cell) diff --git a/lisp/ein-skewer.el b/lisp/ein-skewer.el index 52cd0dd4e..ecc4976f1 100644 --- a/lisp/ein-skewer.el +++ b/lisp/ein-skewer.el @@ -89,8 +89,8 @@ "html" "eval"))) (cl-loop until result - do (accept-process-output nil 0.01) - finally (return result))))) + do (accept-process-output nil 0.01) + finally (return result))))) (deferred:nextc it (lambda (result) (ein:update-javascript-output cell json result))))) diff --git a/lisp/ein-timestamp.el b/lisp/ein-timestamp.el index 3c9958895..489b1eab7 100644 --- a/lisp/ein-timestamp.el +++ b/lisp/ein-timestamp.el @@ -55,8 +55,8 @@ See `ein:format-time-string'." (if (slot-value cell 'running) (ein:insert-read-only "Execution pending\n\n") (if-let ((etime (plist-get (ein:cell-metadata cell) :execute-time))) - (let ((start-time (date-to-time (first etime))) - (end-time (date-to-time (second etime)))) + (let ((start-time (date-to-time (car etime))) + (end-time (date-to-time (cadr etime)))) (ein:insert-read-only (format "Last executed %s in %ss\n\n" (ein:format-time-string ein:timestamp-format start-time) diff --git a/lisp/ein-traceback.el b/lisp/ein-traceback.el index 386588d37..83ac4365e 100644 --- a/lisp/ein-traceback.el +++ b/lisp/ein-traceback.el @@ -26,7 +26,6 @@ ;;; Code: -(eval-when-compile (require 'cl)) (require 'eieio) (require 'ewoc) (require 'ansi-color) @@ -34,6 +33,8 @@ (require 'ein-core) (require 'ein-shared-output) +(declare-function ein:notebook-buffer "ein-notebook" (notebook)) + (defclass ein:traceback () ((tb-data :initarg :tb-data :type list) (notebook :initarg :source-notebook ;; :type ein:$notebook @@ -103,7 +104,7 @@ (list beg end))) (cl-defmethod ein:tb-file-path-at-point ((traceback ein:traceback)) - (destructuring-bind (beg end) + (cl-destructuring-bind (beg end) (ein:tb-range-of-node-at-point traceback) (let* ((file-tail (if (>= emacs-major-version 24) @@ -117,7 +118,7 @@ file)))) (cl-defmethod ein:tb-file-lineno-at-point ((traceback ein:traceback)) - (destructuring-bind (beg end) + (cl-destructuring-bind (beg end) (ein:tb-range-of-node-at-point traceback) (when (save-excursion (goto-char beg) @@ -131,7 +132,7 @@ (if (string-match " (length line) indent) - collect (ein:trim-right (substring line indent)) - else - collect line))) + (cl-loop for line in lines + if (> (length line) indent) + collect (ein:trim-right (substring line indent)) + else + collect line))) (ein:join-str "\n" trimmed))) (defun ein:join-str (sep strings) @@ -414,7 +412,7 @@ Adapted from twittering-mode.el's `case-string'." (min mincol (current-column)) (current-column)))) (unless (= (forward-line 1) 0) - (return-from ein:find-leftmot-column mincol))) + (cl-return-from ein:find-leftmot-column mincol))) mincol))) @@ -429,12 +427,12 @@ Adapted from twittering-mode.el's `case-string'." (defun ein:plist-iter (plist) "Return list of (key . value) in PLIST." ;; FIXME: this is not needed. See: `ein:plist-exclude'. - (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))) + (cl-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))) (defun ein:plist-exclude (plist keys) "Exclude entries specified by KEYS in PLIST. @@ -442,9 +440,9 @@ Adapted from twittering-mode.el's `case-string'." Example:: (ein:plist-exclude '(:a 1 :b 2 :c 3 :d 4) '(:b :c))" - (loop for (k v) on plist by 'cddr - unless (memq k keys) - nconc (list k v))) + (cl-loop for (k v) on plist by 'cddr + unless (memq k keys) + nconc (list k v))) (defun ein:clip-list (list first last) "Return elements in region of the LIST specified by FIRST and LAST element. @@ -452,68 +450,68 @@ Example:: Example:: (ein:clip-list '(1 2 3 4 5 6) 2 4) ;=> (2 3 4)" - (loop for elem in list - with clipped - with in-region-p = nil - when (eq elem first) - do (setq in-region-p t) - when in-region-p - do (push elem clipped) - when (eq elem last) - return (reverse clipped))) - -(defun* ein:list-insert-after (list pivot new &key (test #'eq)) + (cl-loop for elem in list + with clipped + with in-region-p = nil + when (eq elem first) + do (setq in-region-p t) + when in-region-p + do (push elem clipped) + when (eq elem last) + return (reverse clipped))) + +(cl-defun ein:list-insert-after (list pivot new &key (test #'eq)) "Insert NEW after PIVOT in LIST destructively. Note: do not rely on that `ein:list-insert-after' change LIST in place. Elements are compared using the function TEST (default: `eq')." - (loop for rest on list - when (funcall test (car rest) pivot) - return (progn (push new (cdr rest)) list) - finally do (error "PIVOT %S is not in LIST %S" pivot list))) + (cl-loop for rest on list + when (funcall test (car rest) pivot) + return (progn (push new (cdr rest)) list) + finally do (error "PIVOT %S is not in LIST %S" pivot list))) -(defun* ein:list-insert-before (list pivot new &key (test #'eq)) +(cl-defun ein:list-insert-before (list pivot new &key (test #'eq)) "Insert NEW before PIVOT in LIST destructively. Note: do not rely on that `ein:list-insert-before' change LIST in place. Elements are compared using the function TEST (default: `eq')." (if (and list (funcall test (car list) pivot)) (cons new list) - (loop for rest on list - when (funcall test (cadr rest) pivot) - return (progn (push new (cdr rest)) list) - finally do (error "PIVOT %S is not in LIST %S" pivot list)))) + (cl-loop for rest on list + when (funcall test (cadr rest) pivot) + return (progn (push new (cdr rest)) list) + finally do (error "PIVOT %S is not in LIST %S" pivot list)))) -(defun* ein:list-move-left (list elem &key (test #'eq)) +(cl-defun ein:list-move-left (list elem &key (test #'eq)) "Move ELEM in LIST left. TEST is used to compare elements" - (macrolet ((== (a b) `(funcall test ,a ,b))) + (cl-macrolet ((== (a b) `(funcall test ,a ,b))) (cond ((== (car list) elem) (append (cdr list) (list (car list)))) (t - (loop for rest on list - when (== (cadr rest) elem) - return (let ((prev (car rest))) - (setf (car rest) elem) - (setf (cadr rest) prev) - list) - finally do (error "ELEM %S is not in LIST %S" elem list)))))) - -(defun* ein:list-move-right (list elem &key (test #'eq)) + (cl-loop for rest on list + when (== (cadr rest) elem) + return (let ((prev (car rest))) + (setf (car rest) elem) + (setf (cadr rest) prev) + list) + finally do (error "ELEM %S is not in LIST %S" elem list)))))) + +(cl-defun ein:list-move-right (list elem &key (test #'eq)) "Move ELEM in LIST right. TEST is used to compare elements" - (loop with first = t - for rest on list - when (funcall test (car rest) elem) - return (if (cdr rest) - (let ((next (cadr rest))) - (setf (car rest) next) - (setf (cadr rest) elem) - list) - (if first - list - (setcdr rest-1 nil) - (cons elem list))) - finally do (error "ELEM %S is not in LIST %S" elem list) - for rest-1 = rest - do (setq first nil))) + (cl-loop with first = t + for rest on list + when (funcall test (car rest) elem) + return (if (cdr rest) + (let ((next (cadr rest))) + (setf (car rest) next) + (setf (cadr rest) elem) + list) + (if first + list + (setcdr rest-1 nil) + (cons elem list))) + finally do (error "ELEM %S is not in LIST %S" elem list) + for rest-1 = rest + do (setq first nil))) (defun ein:get-value (obj) "Get value from obj if it is a variable or function." @@ -554,15 +552,15 @@ FUNC is called as (apply FUNC ARG ARGS)." (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)) + (cl-loop for l in list + for i from 0 + when (not (memq i indices)) + collect l)) (defun ein:ask-choice-char (prompt choices) "Show PROMPT and read one of acceptable key specified as CHOICES." - (let ((char-list (loop for i from 0 below (length choices) - collect (elt choices i))) + (let ((char-list (cl-loop for i from 0 below (length choices) + collect (elt choices i))) (answer 'recenter)) (while (let ((key @@ -593,12 +591,12 @@ PREDARGS is argument list for the PREDICATE function. Make TIMEOUT-SECONDS larger \(default 5) to wait longer before timeout." (ein:log 'debug "WAIT-UNTIL start") (unless timeout-seconds (setq timeout-seconds 5)) - (unless (loop repeat (/ timeout-seconds 0.05) - when (apply predicate predargs) - return t - ;; borrowed from `deferred:sync!': - do (sit-for 0.05) - do (sleep-for 0.05)) + (unless (cl-loop repeat (/ timeout-seconds 0.05) + when (apply predicate predargs) + return t + ;; borrowed from `deferred:sync!': + do (sit-for 0.05) + do (sleep-for 0.05)) (warn "Timeout")) (ein:log 'debug "WAIT-UNTIL end")) @@ -614,9 +612,11 @@ otherwise it should be a function, which is called on `time'." ;;; Emacs utilities (defmacro ein:message-whir (mesg &rest body) "Display MESG with a modest animation until ASYNC-CALL completes." - `(lexical-let* (done-p - (done-callback (lambda (&rest ignore) (setf done-p t))) - (errback (lambda (&rest ignore) (setf done-p 'error)))) + `(let* (done-p + (done-callback (lambda (&rest _ignore) (setf done-p t))) + (errback (lambda (&rest _ignore) (setf done-p 'error)))) + (ignore done-callback) + (ignore errback) (ein:message-whir-subr ,mesg (lambda () done-p)) ,@body)) @@ -624,21 +624,21 @@ otherwise it should be a function, which is called on `time'." "Display MESG with a modest animation until done-p returns t. DONEBACK returns t or 'error when calling process is done, and nil if not done." - (lexical-let* ((mesg mesg) - (doneback doneback) - (count -1)) - (message "%s%s" mesg (make-string (1+ (% (incf count) 3)) ?.)) + (let* ((mesg mesg) + (doneback doneback) + (count -1)) + (message "%s%s" mesg (make-string (1+ (% (cl-incf count) 3)) ?.)) ;; https://github.com/kiwanami/emacs-deferred/issues/28 ;; "complicated timings of macro expansion lexical-let, deferred:lambda" ;; using deferred:loop instead (deferred:$ - (deferred:loop (loop for i from 1 below 30 by 1 collect i) + (deferred:loop (cl-loop for i from 1 below 30 by 1 collect i) (lambda () (deferred:$ (deferred:next (lambda () (ein:aif (funcall doneback) it - (message "%s%s" mesg (make-string (1+ (% (incf count) 3)) ?.)) + (message "%s%s" mesg (make-string (1+ (% (cl-incf count) 3)) ?.)) (sleep-for 0 365))))))) (deferred:nextc it (lambda (status) @@ -680,7 +680,7 @@ Use `ein:log' for debugging and logging." (defun ein:generate-menu (list-name-callback) (mapcar (lambda (name-callback) - (destructuring-bind (name callback &rest args) name-callback + (cl-destructuring-bind (name callback &rest args) name-callback `[,name ,callback :help ,(ein:get-docstring callback) ,@args])) list-name-callback)) @@ -689,7 +689,7 @@ Use `ein:log' for debugging and logging." :type 'boolean :group 'ein) -(lexical-let ((current-gc-cons-threshold gc-cons-threshold)) +(let ((current-gc-cons-threshold gc-cons-threshold)) (defun ein:gc-prepare-operation () (ein:log 'debug "[GC-PREPARE-OPERATION] Setting cons threshold to %s." (* current-gc-cons-threshold 10000) ) (when ein:enable-gc-adjust diff --git a/lisp/ein-websocket.el b/lisp/ein-websocket.el index a7f783361..2bcff959b 100644 --- a/lisp/ein-websocket.el +++ b/lisp/ein-websocket.el @@ -1,4 +1,4 @@ -;;; ein-websocket.el --- Wrapper of websocket.el +;;; ein-websocket.el --- Wrapper of websocket.el -*- lexical-binding: t -*- ;; Copyright (C) 2012- Takafumi Arakaki @@ -25,7 +25,6 @@ ;;; Code: -(eval-when-compile (require 'cl)) (require 'websocket) (require 'ein-core) (require 'ein-classes) @@ -33,7 +32,7 @@ (require 'request) ;; Fix issues reading cookies in request when using curl backend -(defun fix-request-netscape-cookie-parse (next-method) +(defun fix-request-netscape-cookie-parse (_next-method) "Parse Netscape/Mozilla cookie format." (goto-char (point-min)) (let ((tsv-re (concat "^\\=" @@ -65,9 +64,9 @@ (url-cookie-store (car c) (cdr c) nil host-port url-filename securep)) (defun ein:maybe-get-jhconn-user (url) - (let ((paths (rest (split-string (url-filename (url-generic-parse-url url)) "/")))) - (if (string= (first paths) "user") - (format "/%s/%s/" (first paths) (second paths)) + (let ((paths (cl-rest (split-string (url-filename (url-generic-parse-url url)) "/")))) + (if (string= (car paths) "user") + (format "/%s/%s/" (car paths) (cadr paths)) ""))) ;;(advice-add 'request--netscape-cookie-parse :around #'fix-request-netscape-cookie-parse) @@ -75,25 +74,24 @@ "Websocket gets its cookies using the url-cookie API, so we need to copy over any cookies that are made and stored during the contents API calls via emacs-request." - (lexical-let* - ((parsed-url (url-generic-parse-url url)) - (host-port (if (url-port-if-non-default parsed-url) - (format "%s:%s" (url-host parsed-url) (url-port parsed-url)) - (url-host parsed-url))) - (securep (string-match "^wss://" url)) - (read-cookies-func (lambda (path) - (request-cookie-alist - (url-host parsed-url) path securep))) - (cookies (loop repeat 4 - for cand = (mapcan read-cookies-func - `("/" "/hub/" - ,(ein:maybe-get-jhconn-user url))) - until (cl-some (lambda (x) (string= "_xsrf" (car x))) cand) - do (ein:log 'info - "ein:websocket--prepare-cookies: no _xsrf among %s, retrying." - cand) - do (sleep-for 0 300) - finally return cand))) + (let* ((parsed-url (url-generic-parse-url url)) + (host-port (if (url-port-if-non-default parsed-url) + (format "%s:%s" (url-host parsed-url) (url-port parsed-url)) + (url-host parsed-url))) + (securep (string-match "^wss://" url)) + (read-cookies-func (lambda (path) + (request-cookie-alist + (url-host parsed-url) path securep))) + (cookies (cl-loop repeat 4 + for cand = (mapcan read-cookies-func + `("/" "/hub/" + ,(ein:maybe-get-jhconn-user url))) + until (cl-some (lambda (x) (string= "_xsrf" (car x))) cand) + do (ein:log 'info + "ein:websocket--prepare-cookies: no _xsrf among %s, retrying." + cand) + do (sleep-for 0 300) + finally return cand))) (dolist (c cookies) (ein:websocket-store-cookie c host-port (car (url-path-and-query parsed-url)) securep)))) diff --git a/lisp/ein-worksheet.el b/lisp/ein-worksheet.el index 36bd2922b..ce60cfa29 100644 --- a/lisp/ein-worksheet.el +++ b/lisp/ein-worksheet.el @@ -26,13 +26,13 @@ ;;; Code: -(eval-when-compile (require 'cl)) (require 'eieio) (require 'ewoc) (require 'ein-core) (require 'ein-utils) (require 'ein-cell) +(require 'ein-cell-edit) ; for `ein:src--ws' (require 'ein-kill-ring) (require 'poly-ein) @@ -77,7 +77,7 @@ Normalize `buffer-undo-list' by removing extraneous details, and update the ein: (let ((cell-id (ein:aif (ein:worksheet-get-current-cell :noerror t) (ein:worksheet--unique-enough-cell-id it) nil))) (let ((ein:log-print-level 5)) - (ein:log 'debug "which-cell (%s . %s) %s %S fill=%s" change-beg change-end cell-id (subseq buffer-undo-list 0 fill) fill)) + (ein:log 'debug "which-cell (%s . %s) %s %S fill=%s" change-beg change-end cell-id (cl-subseq buffer-undo-list 0 fill) fill)) (setq ein:%which-cell% (nconc (make-list fill cell-id) ein:%which-cell%)))))))) @@ -97,11 +97,11 @@ Normalize `buffer-undo-list' by removing extraneous details, and update the ein: (ein:worksheet--element-start cell :footer)))))) (defsubst ein:worksheet--saved-input-length (cell) - (or (fourth (plist-get ein:%cell-lengths% (slot-value cell 'cell-id))) 0)) + (or (cl-fourth (plist-get ein:%cell-lengths% (slot-value cell 'cell-id))) 0)) (defsubst ein:worksheet--prompt-length (cell &optional cached) (if cached - (or (first (plist-get ein:%cell-lengths% (slot-value cell 'cell-id))) 0) + (or (car (plist-get ein:%cell-lengths% (slot-value cell 'cell-id))) 0) ;; 1+ for newline (1+ (- (ein:worksheet--element-start cell :input) (ein:worksheet--element-start cell :prompt))))) @@ -109,7 +109,7 @@ Normalize `buffer-undo-list' by removing extraneous details, and update the ein: (defsubst ein:worksheet--output-length (cell &optional cached) (if cached ;; 1 for when cell un-executed, there is still a newline - (or (second (plist-get ein:%cell-lengths% (slot-value cell 'cell-id))) 1) + (or (cadr (plist-get ein:%cell-lengths% (slot-value cell 'cell-id))) 1) (if (string= (slot-value cell 'cell-type) "code") (- (ein:worksheet--next-cell-start cell) (ein:worksheet--element-start cell :output)) @@ -117,7 +117,7 @@ Normalize `buffer-undo-list' by removing extraneous details, and update the ein: (defsubst ein:worksheet--total-length (cell &optional cached) (if cached - (or (third (plist-get ein:%cell-lengths% (slot-value cell 'cell-id))) 0) + (or (cl-third (plist-get ein:%cell-lengths% (slot-value cell 'cell-id))) 0) (- (ein:worksheet--next-cell-start cell) (ein:worksheet--element-start cell :prompt)))) @@ -147,12 +147,13 @@ Normalize `buffer-undo-list' by removing extraneous details, and update the ein: (cons m (cdr u))) u))) ((and (consp u) (null (car u)) (numberp (car (last u))) (numberp (cdr (last u)))) - (append (subseq u 0 3) + (append (cl-subseq u 0 3) (cons (+ ,distance (car (last u))) + (+ ,distance (cdr (last u)))))) ((and (consp u) (eq (car u) 'apply) (numberp (nth 2 u)) (numberp (nth 3 u))) - (append (subseq u 0 2) + (append (cl-subseq u 0 2) (list (+ ,distance (nth 2 u))) (list (+ ,distance (nth 3 u))) (nthcdr 4 u))) @@ -166,13 +167,14 @@ Normalize `buffer-undo-list' by removing extraneous details, and update the ein: result)) (defun ein:worksheet--jigger-undo-list (&optional change-cell-id) - (if (/= (length buffer-undo-list) (length ein:%which-cell%)) + (if (/= (safe-length buffer-undo-list) (length ein:%which-cell%)) (ein:log 'debug "jig %s to %s: %S %S" (length ein:%which-cell%) (length buffer-undo-list) buffer-undo-list ein:%which-cell%)) (ein:and-let* ((old-cell-id (car change-cell-id)) (new-cell-id (cdr change-cell-id)) (changed-p (not (eq old-cell-id new-cell-id)))) - (setq ein:%which-cell% (-replace old-cell-id new-cell-id ein:%which-cell%))) - (let ((fill (- (length buffer-undo-list) (length ein:%which-cell%)))) + (when changed-p + (setq ein:%which-cell% (-replace old-cell-id new-cell-id ein:%which-cell%)))) + (let ((fill (- (safe-length buffer-undo-list) (length ein:%which-cell%)))) (if (> (abs fill) 1) (progn (let ((msg (format "Undo failure diagnostic %s %s | %s" @@ -185,16 +187,18 @@ Normalize `buffer-undo-list' by removing extraneous details, and update the ein: (when (buffer-live-p b) (poly-ein-copy-state (ein:worksheet--get-buffer ein:%worksheet%) b)))) (ein:display-warning msg :error) - (error "ein:worksheet--jigger-undo-list: aborting"))) + (when ein:debug + (error "ein:worksheet--jigger-undo-list: aborting")))) (if (< fill 0) - (setq ein:%which-cell% (nthcdr (- fill) ein:%which-cell%)) + (setq ein:%which-cell% (nthcdr (- fill) ein:%which-cell%)) (if (> fill 0) (setq ein:%which-cell% (nconc (make-list fill (car ein:%which-cell%)) ein:%which-cell%)))))) - (cl-assert (= (length buffer-undo-list) (length ein:%which-cell%)) t + (cl-assert (= (safe-length buffer-undo-list) (length ein:%which-cell%)) + t "ein:worksheet--jigger-undo-list %d != %d" - (length buffer-undo-list) (length ein:%which-cell%))) + (safe-length buffer-undo-list) (length ein:%which-cell%))) (defun ein:worksheet--unshift-undo-list (cell &optional exogenous-input old-cell) "Adjust `buffer-undo-list' for adding CELL. Unshift in list parlance means prepending to list." @@ -224,7 +228,7 @@ Normalize `buffer-undo-list' by removing extraneous details, and update the ein: (ein:worksheet--jigger-undo-list (cons (ein:worksheet--unique-enough-cell-id old-cell) (ein:worksheet--unique-enough-cell-id cell))) - (dolist (uc (mapcar* 'cons buffer-undo-list ein:%which-cell%)) + (dolist (uc (cl-mapcar 'cons buffer-undo-list ein:%which-cell%)) (let ((u (car uc)) (cell-id (or (cdr uc) ""))) (if (string= (ein:worksheet--unique-enough-cell-id cell) cell-id) @@ -234,9 +238,9 @@ Normalize `buffer-undo-list' by removing extraneous details, and update the ein: (ein:log 'debug "unsh adj %s %s" u cell-id) (setq lst (nconc lst (list (funcall func-after-cell u))))) (setq lst (nconc lst (list u))))))) - (cl-assert (= (length buffer-undo-list) (length lst)) t + (cl-assert (= (safe-length buffer-undo-list) (length lst)) t "ein:worksheet--unshift-undo-list %d != %d" - (length buffer-undo-list) (length lst)) + (safe-length buffer-undo-list) (length lst)) (setq buffer-undo-list lst) (ein:worksheet--update-cell-lengths cell exogenous-input))))) @@ -263,7 +267,7 @@ Normalize `buffer-undo-list' by removing extraneous details, and update the ein: ;; Deletion of a less recent undo affects a more recent undo (arrow of time) ;; Since buffer-undo-list is ordered most to least recent, we must ;; reverse. - (dolist (uc (nreverse (mapcar* 'cons buffer-undo-list ein:%which-cell%))) + (dolist (uc (nreverse (cl-mapcar 'cons buffer-undo-list ein:%which-cell%))) (let ((u (car uc)) (cell-id (or (cdr uc) ""))) (if (string= (ein:worksheet--unique-enough-cell-id cell) cell-id) @@ -280,7 +284,7 @@ Normalize `buffer-undo-list' by removing extraneous details, and update the ein: (setq buffer-undo-list (nreverse lst)) (setq ein:%which-cell% (nreverse wc)) (ein:worksheet--jigger-undo-list) - (remprop 'ein:%cell-lengths% (slot-value cell 'cell-id)))))) + (cl-remprop 'ein:%cell-lengths% (slot-value cell 'cell-id)))))) ;;; Class and variable @@ -317,7 +321,7 @@ Normalize `buffer-undo-list' by removing extraneous details, and update the ein: (ein:events-on events 'set_dirty.Worksheet #'ein:worksheet--set-dirty)) (defun ein:worksheet--set-next-input (-ignore- data) - (destructuring-bind (&key cell text) data + (cl-destructuring-bind (&key cell text) data (ein:with-live-buffer (ein:cell-buffer cell) (ein:and-let* ((ws ein:%worksheet%) (new-cell @@ -327,7 +331,7 @@ Normalize `buffer-undo-list' by removing extraneous details, and update the ein: (defun ein:worksheet--set-dirty (-ignore- data) "Set dirty flag of worksheet in which CELL in DATA locates." - (destructuring-bind (&key value cell) data + (cl-destructuring-bind (&key value cell) data (ein:with-live-buffer (ein:cell-buffer cell) (ein:worksheet-set-modified-p ein:%worksheet% value)))) @@ -344,7 +348,7 @@ Normalize `buffer-undo-list' by removing extraneous details, and update the ein: "Set worksheet name. \(fn ws name)" - (assert (stringp name) nil "NAME must be a string. Got: %S" name) + (cl-assert (stringp name) nil "NAME must be a string. Got: %S" name) (setf (ein:worksheet--metadata ws) (plist-put (ein:worksheet--metadata ws) :name name))) (cl-defmethod ein:worksheet-full-name ((ws ein:worksheet)) @@ -443,14 +447,14 @@ Normalize `buffer-undo-list' by removing extraneous details, and update the ein: (defun ein:worksheet-pp (ewoc-data) (let ((path (ein:$node-path ewoc-data)) (data (ein:$node-data ewoc-data))) - (case (car path) + (cl-case (car path) (cell (ein:cell-pp (cdr path) data))))) ;;; Persistance and loading (cl-defmethod ein:worksheet-from-json ((ws ein:worksheet) data) - (destructuring-bind (&key cells metadata &allow-other-keys) data + (cl-destructuring-bind (&key cells metadata &allow-other-keys) data (setf (slot-value ws 'metadata) metadata) (setf (slot-value ws 'saved-cells) (mapcar (lambda (data) (ein:cell-from-json data)) cells))) @@ -559,8 +563,7 @@ worksheet WS is reopened. (setq ewoc-node (ewoc-next (slot-value ein:%worksheet% 'ewoc) ewoc-node))) ewoc-node)) -(defun* ein:worksheet-get-current-cell (&key pos noerror - (cell-p #'ein:basecell-child-p)) +(cl-defun ein:worksheet-get-current-cell (&key pos noerror (cell-p #'ein:basecell-child-p)) "Return a cell at POS. If POS is not given, it is assumed be the current cursor position. When the current buffer is not worksheet buffer or there is no cell in the current buffer, return `nil'." @@ -579,7 +582,7 @@ buffer or there is no cell in the current buffer, return `nil'." (ein:worksheet-get-current-cell :pos beg) (ein:worksheet-get-current-cell :pos end))) -(defun* ein:worksheet-get-cells-in-region-or-at-point +(cl-defun ein:worksheet-get-cells-in-region-or-at-point (&key noerror (cell-p #'ein:basecell-child-p)) (or (seq-filter cell-p (if (region-active-p) @@ -668,19 +671,19 @@ Prefixes are act same as the normal `yank' command." (t (1- arg)))))) (let* ((cell (ein:worksheet-get-current-cell :noerror t)) ; can be nil (killed (ein:current-kill n))) - (loop for c in killed - with last = cell - do (setq last (ein:worksheet-insert-clone ws c last "below")) - finally (ein:cell-goto last)))) + (cl-loop for c in killed + with last = cell + do (setq last (ein:worksheet-insert-clone ws c last "below")) + finally (ein:cell-goto last)))) (defun ein:worksheet--node-positions (cell) (let ((result)) - (loop for k in (slot-value cell 'element-names) - do (setq result - (plist-put result k - (let* ((en-or-list (ein:cell-element-get cell k)) - (en (if (listp en-or-list) (nth 0 en-or-list) en-or-list))) - (if en (marker-position (ewoc-location en))))))) + (cl-loop for k in (slot-value cell 'element-names) + do (setq result + (plist-put result k + (let* ((en-or-list (ein:cell-element-get cell k)) + (en (if (listp en-or-list) (nth 0 en-or-list) en-or-list))) + (if en (marker-position (ewoc-location en))))))) result)) (defun ein:worksheet-maybe-new-cell (ws type-or-cell) @@ -751,7 +754,7 @@ directly." (interactive (list (ein:worksheet--get-ws-or-error) (ein:worksheet-get-current-cell) t)) - (let ((type (case (slot-value ws 'nbformat) + (let ((type (cl-case (slot-value ws 'nbformat) (2 (ein:case-equal (slot-value cell 'cell-type) (("code") "markdown") (("markdown") "code"))) @@ -800,13 +803,13 @@ an integer used only when the TYPE is \"heading\"." (interactive (let* ((ws (ein:worksheet--get-ws-or-error)) (cell (ein:worksheet-get-current-cell)) - (choices (case (slot-value ws 'nbformat) + (choices (cl-case (slot-value ws 'nbformat) (2 "cm") (3 "cmr123456") (4 "chmr123456"))) (key (ein:ask-choice-char (format "Cell type [%s]: " choices) choices)) - (type (case key + (type (cl-case key (?c "code") (?h "hy-code") (?m "markdown") @@ -869,7 +872,7 @@ If prefix is given, merge current cell into next cell." (when cell (let* ((next-cell (ein:cell-next cell)) (head (ein:cell-get-text cell))) - (assert next-cell nil "No cell to merge.") + (cl-assert next-cell nil "No cell to merge.") (ein:worksheet-delete-cell ws cell) (save-excursion (goto-char (ein:cell-input-pos-min next-cell)) @@ -879,7 +882,7 @@ If prefix is given, merge current cell into next cell." ;;; Cell selection. -(defun* ein:worksheet-next-input-cell (ewoc-node &optional up (nth 1)) +(cl-defun ein:worksheet-next-input-cell (ewoc-node &optional up (nth 1)) "Return a cell containing the next input node after EWOC-NODE. When UP is non-`nil', do the same for the *previous* input node. When NTH is specified, return NTH cell. Note that this function is @@ -889,12 +892,12 @@ When NTH is specified, return NTH cell. Note that this function is (setq nth (* nth -1)) (setq up (not up))) (let ((cell (ein:worksheet-next-input-cell-1 ewoc-node up))) - (loop repeat (1- nth) - with next = (if up #'ein:cell-prev #'ein:cell-next) - if (funcall next cell) - do (setq cell it) - else - return nil) + (cl-loop repeat (1- nth) + with next = (if up #'ein:cell-prev #'ein:cell-next) + if (funcall next cell) + do (setq cell it) + else + return nil) cell))) (defun ein:worksheet-next-input-cell-1 (ewoc-node &optional up) @@ -1083,11 +1086,39 @@ cell bellow." (ein:worksheet-get-current-cell))) (ein:worksheet-execute-cell-and-goto-next ws cell t)) -(defun ein:worksheet-execute-all-cell (ws) +;;; TODO add version number here before creating a new release +(define-obsolete-function-alias + 'ein:worksheet-execute-all-cell + 'ein:worksheet-execute-all-cells) + +(defun ein:worksheet-execute-all-cells (ws) "Execute all cells in the current worksheet buffer." (interactive (list (ein:worksheet--get-ws-or-error))) - (mapc #'ein:cell-execute - (seq-filter #'ein:codecell-p (ein:worksheet-get-cells ws)))) + (cl-loop for c in (ein:worksheet-get-cells ws) + when (ein:codecell-p c) + do (ein:cell-execute c))) + +(defun ein:worksheet-execute-all-cells-above (ws) + "Execute all cells above the current cell (exclusively) in the +current worksheet buffer." + (interactive (list (ein:worksheet--get-ws-or-error))) + (cl-loop with curr-cell-id = (ein:cell-id (ein:worksheet-get-current-cell)) + for c in (ein:worksheet-get-cells ws) + until (equal (ein:cell-id c) curr-cell-id) + when (ein:codecell-p c) + do (ein:cell-execute c))) + +(defun ein:worksheet-execute-all-cells-below (ws) + "Execute all cells below the current cell (inclusively) in the +current worksheet buffer." + (interactive (list (ein:worksheet--get-ws-or-error))) + (cl-loop with curr-cell-id = (ein:cell-id (ein:worksheet-get-current-cell)) + and curr-cell-reached? + for c in (ein:worksheet-get-cells ws) + when (and (not curr-cell-reached?) (equal (ein:cell-id c) curr-cell-id)) + do (setq curr-cell-reached? t) + when (and curr-cell-reached? (ein:codecell-p c)) + do (ein:cell-execute c))) (defun ein:worksheet-insert-last-input-history (ws cell index) "Insert INDEX-th previous history into CELL in worksheet WS." @@ -1097,7 +1128,7 @@ cell bellow." :history_reply (cons (lambda (cell content -metadata-not-used-) - (destructuring-bind (session line-number input) + (cl-destructuring-bind (session line-number input) (car (plist-get content :history)) (if (eq (ein:worksheet-get-current-cell) cell) (ein:cell-set-text cell input) @@ -1215,9 +1246,9 @@ in the history." (defun ein:worksheet--cells-before-cell (ws cell) (let ((cells (ein:worksheet-get-cells ws))) - (loop for c in cells - collecting c - until (eql (ein:cell-id c) (ein:cell-id cell))))) + (cl-loop for c in cells + collecting c + until (eql (ein:cell-id c) (ein:cell-id cell))))) (defun ein:worksheet--cells-after-cell (ws cell) (let ((cells (ein:worksheet-get-cells ws)) @@ -1225,10 +1256,10 @@ in the history." (seq-drop cells prior-cells))) (defun ein:worksheet-first-executing-cell (cells) - (loop for c in cells - when (and (ein:codecell-p c) - (slot-value c 'running)) - return c)) + (cl-loop for c in cells + when (and (ein:codecell-p c) + (slot-value c 'running)) + return c)) (defun ein:worksheet-jump-to-first-executing-cell () "Move the point to the first executing cell in the current worksheet." @@ -1295,13 +1326,21 @@ function." "`imenu-create-index-function' for notebook buffer." ;; As Imenu does not provide the way to represent level *and* ;; position, use #'s to do that. - (loop for cell in (when (ein:worksheet-p ein:%worksheet%) - (seq-filter #'ein:headingcell-p - (ein:worksheet-get-cells ein:%worksheet%))) - for sharps = (loop repeat (slot-value cell 'level) collect "#") - for text = (ein:cell-get-text cell) - for name = (ein:join-str "" (append sharps (list " " text))) - collect (cons name (ein:cell-input-pos-min cell)))) + (cl-loop for cell in (when (ein:worksheet-p ein:%worksheet%) + (seq-filter #'(lambda (cell) (or (ein:headingcell-p cell) + (ein:cell--markdown-heading-p cell))) + (ein:worksheet-get-cells ein:%worksheet%))) + for sharps = (if (ein:headingcell-p cell) + (cl-loop repeat (slot-value cell 'level) collect "#") + (cl-loop repeat(progn + (string-match "^#+" (ein:cell-get-text cell)) + (match-end 0)) + collect "#")) + for text = (ein:cell-get-text cell) + for name = (if (ein:headingcell-p cell) + (ein:join-str "" (append sharps (list " " text))) + text) + collect (cons name (ein:cell-input-pos-min cell)))) (defun ein:worksheet-imenu-setup () "Called via notebook mode hooks." diff --git a/lisp/ein_hytools.hy b/lisp/ein_hytools.hy new file mode 100644 index 000000000..73f674037 --- /dev/null +++ b/lisp/ein_hytools.hy @@ -0,0 +1,67 @@ + +;; Hy utilities useable from ein. + +;; Copyright (C) 2020- John Miller + +;; Author: John Miller + +;; ein_hytools.hy 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_hytools.hy 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.py. If not, see . + + +(setv --ein-hytools-version-- "1.0.0") + +(try + (import [matplotlib [rc :as --ein-rc + rcParams as --ein-rcParams]]) + (setv ein-matplotlib-available True) + (except [ImportError] + (setv ein-matplotlib-available False))) + + +(defn ein-find-edit-target [name] + (import [inspect [getsourcefile getsourcelines]]) + (import hy) + (try + (setv obj (eval (hy.read-str name))) + (except [NameError] + (return False)) + (else + (setv sfile (getsourcefile obj)) + (setv sline (get (getsourcelines obj) -1)) + (if (and sfile sline) + (return (, sfile sline False)) + (return False))))) + +(defn ein-find-source [name] + (setv ret (--ein-find-edit-target name)) + (if ret + (do + (setv (, filename lineno use-temp) ret) + (if (not use-temp) + (do + (print filename) + (print lineno) + (return))))) + (raise (RuntimeError (.format "Source code for {0} cannot be found" name)))) + + +(defn ein-print-object-info-for [obj] + (import json) + (import [IPython.core [oinspect]]) + (setv inspector (oinspect.Inspector)) + (try + (setv oinfo (inspector.info obj)) + (except [Exception] + (setv oinfo (inspector.info None)))) + (print (json.dumps oinfo))) diff --git a/lisp/ein_remote_safe.py b/lisp/ein_remote_safe.py index ba148f1dc..1b15b770d 100644 --- a/lisp/ein_remote_safe.py +++ b/lisp/ein_remote_safe.py @@ -20,6 +20,14 @@ """ +__ein_pytools_version = "1.1.0" + +try: + from matplotlib import rc as __ein_rc + from matplotlib import rcParams as __ein_rcParams + __ein_matplotlib_available = True +except ImportError: + __ein_matplotlib_available = False def __ein_export_nb(nb_json, format): import IPython.nbconvert as nbconvert @@ -61,13 +69,32 @@ def __ein_find_edit_target_python(name): except ImportError: __ein_find_edit_target = __ein_find_edit_target_012 -def __ein_set_figure_size(*dim): - try: - from matplotlib.pyplot import rcParams - rcParams['figure.figsize'] = dim - except: + +def __ein_set_matplotlib_param(family, setting, value): + settings = {} + if __ein_matplotlib_available: + settings[setting] = eval(value) + __ein_rc(family, **settings) + else: raise RuntimeError("Matplotlib not installed in this instance of python!") + +def __ein_set_figure_size(dim): + __ein_set_matplotlib_param('figure', 'figsize', dim) + + +def __ein_set_figure_dpi(dpi): + __ein_set_matplotlib_param('figure', 'dpi', dpi) + + +def __ein_get_matplotlib_params(): + if __ein_matplotlib_available: + import json + print(json.dumps([k for k in __ein_rcParams.keys()])) + else: + raise RuntimeError("Matplotlib not installed in this instance of python!") + + def __ein_find_source(name): """Given an object as string, `name`, print its place in source code.""" # FIXME: use JSON display object instead of stdout @@ -89,6 +116,16 @@ def __ein_run_docstring_examples(obj, verbose=True): return doctest.run_docstring_examples(obj, globs, verbose=verbose) +def __ein_generate_oinfo_data(ostrings, locals=None): + import json + + defined_objects = [__ein_maybe_undefined_object(obj, locals) for obj in ostrings + if __ein_maybe_undefined_object(obj, locals) is not None] + odata = [__ein_object_info_for(obj) for obj in defined_objects] + + print (json.dumps(odata)) + return odata + def __ein_maybe_undefined_object(obj, locals=None): try: return eval(obj, None, locals) @@ -97,16 +134,21 @@ def __ein_maybe_undefined_object(obj, locals=None): except SyntaxError: return None -def __ein_print_object_info_for(obj): +def __ein_object_info_for(obj): import IPython.core.oinspect - import json inspector = IPython.core.oinspect.Inspector() try: - print(json.dumps(inspector.info(obj))) + return inspector.info(obj) except Exception: - print(json.dumps(inspector.info(None))) + return inspector.info(None) + +def __ein_print_object_info_for(obj): + import json + + oinfo = __ein_object_info_for(obj) + print (json.dumps(oinfo)) def __ein_eval_hy_string(obj): try: diff --git a/lisp/ob-ein.el b/lisp/ob-ein.el index 4e2903353..9a1a00fca 100644 --- a/lisp/ob-ein.el +++ b/lisp/ob-ein.el @@ -71,7 +71,11 @@ ("ein-R" . R) ("ein-r" . R) ("ein-julia" . julia) - ("ein-hy" . hy)) + ("ein-hy" . hy) + ("ein-c" . c) + ("ein-C++11" . c++) + ("ein-C++14" . c++) + ("ein-C++17" . c++)) "ob-ein has knowledge of these (ein-LANG . LANG-MODE) pairs." :type '(repeat (cons string symbol)) :group 'ein) @@ -115,33 +119,33 @@ (base64-decode-region (point-min) (point-max))))) (defun ob-ein--return-mime-type (json file) - (loop - for key in ein:output-types-text-preferred - for type = (intern (format ":%s" key)) ; something like `:text' - for value = (plist-get json type) ; FIXME: optimize - when (plist-member json type) - return - (case key - ((svg image/svg) - (let ((file (or file (ob-ein--inline-image-info value)))) - (ob-ein--write-base64-image value file) - (format "[[file:%s]]" file))) - ((png image/png jpeg image/jpeg) - (let ((file (or file (ob-ein--inline-image-info value)))) - (ob-ein--write-base64-image value file) - (format "[[file:%s]]" file))) - (t (plist-get json type))))) + (cl-loop + for key in ein:output-types-text-preferred + for type = (intern (format ":%s" key)) ; something like `:text' + for value = (plist-get json type) ; FIXME: optimize + when (plist-member json type) + return + (cl-case key + ((svg image/svg) + (let ((file (or file (ob-ein--inline-image-info value)))) + (ob-ein--write-base64-image value file) + (format "[[file:%s]]" file))) + ((png image/png jpeg image/jpeg) + (let ((file (or file (ob-ein--inline-image-info value)))) + (ob-ein--write-base64-image value file) + (format "[[file:%s]]" file))) + (t (plist-get json type))))) (defun ob-ein--process-outputs (outputs params) (let ((file (cdr (assoc :image params)))) (ein:join-str "\n" - (loop for o in outputs - collecting (ob-ein--return-mime-type o file))))) + (cl-loop for o in outputs + collecting (ob-ein--return-mime-type o file))))) (defun ob-ein--get-name-create (src-block-info) "Get the name of a src block or add a uuid as the name." - (if-let ((name (fifth src-block-info))) + (if-let ((name (cl-fifth src-block-info))) name (save-excursion (let ((el (org-element-context)) @@ -209,25 +213,25 @@ Based on ob-ipython--configure-kernel." (ob-ein--initiate-session session kernelspec callback) (if (ein:eval-if-bound 'org-current-export-file) (save-excursion - (loop with interval = 2000 - with pending = t - repeat (/ (* ob-ein-timeout-seconds 1000) interval) - do (progn - (org-babel-goto-named-result name) - (forward-line 1) - (setq pending (re-search-forward - (regexp-quote *ob-ein-sentinel*) - (org-babel-result-end) t))) - until (not pending) - do (sleep-for 0 interval) - finally return - (if pending - (progn - (ein:log 'error "ob-ein--execute-body: %s timed out" name) - "") - (ob-ein--process-outputs - (ein:oref-safe (ein:shared-output-get-cell) 'outputs) - processed-params)))) + (cl-loop with interval = 2000 + with pending = t + repeat (/ (* ob-ein-timeout-seconds 1000) interval) + do (progn + (org-babel-goto-named-result name) + (forward-line 1) + (setq pending (re-search-forward + (regexp-quote *ob-ein-sentinel*) + (org-babel-result-end) t))) + until (not pending) + do (sleep-for 0 interval) + finally return + (if pending + (progn + (ein:log 'error "ob-ein--execute-body: %s timed out" name) + "") + (ob-ein--process-outputs + (ein:oref-safe (ein:shared-output-get-cell) 'outputs) + processed-params)))) (org-babel-remove-result) *ob-ein-sentinel*))) @@ -258,7 +262,7 @@ Based on ob-ipython--configure-kernel." (org-babel-insert-result result (cdr (assoc :result-params - (third (org-babel-get-src-block-info))))) + (cl-third (org-babel-get-src-block-info))))) (org-redisplay-inline-images))))))) buffer params result-params name)) @@ -281,7 +285,7 @@ one at a time. Further, we do not order the queued up blocks!" (ein:shared-output-eval-string kernel body nil))))) (defun ob-ein--parse-session (session) - (multiple-value-bind (url-or-port _password) (ein:jupyter-server-conn-info) + (cl-multiple-value-bind (url-or-port _password) (ein:jupyter-server-conn-info) (let ((tokens (split-string session "/")) (parsed-url (url-generic-parse-url session))) (cond ((null (url-host parsed-url)) @@ -296,11 +300,11 @@ one at a time. Further, we do not order the queued up blocks!" candidate))) (t (ein:url session)))))) -(defun ob-ein--initiate-session (session kernelspec callback) +(defun ob-ein--initiate-session (session kernelspec callback &optional babel-info) "Retrieve notebook based on SESSION path and KERNELSPEC, starting jupyter instance if necessary. Install CALLBACK (i.e., cell execution) upon notebook retrieval." (let* ((nbpath (ob-ein--parse-session session)) - (info (org-babel-get-src-block-info)) + (info (or (org-babel-get-src-block-info) babel-info)) (anonymous-path (format ob-ein-anonymous-path (nth 0 info))) (parsed-url (url-generic-parse-url nbpath)) (slash-path (car (url-path-and-query parsed-url))) @@ -311,16 +315,16 @@ if necessary. Install CALLBACK (i.e., cell execution) upon notebook retrieval." (substring nbpath 0 (- (length slash-path))))) (notebook (ein:notebook-get-opened-notebook url-or-port path)) (callback-nbopen (lambda (nb _created) - (loop repeat 50 - for live-p = (ein:kernel-live-p (ein:$notebook-kernel nb)) - until live-p - do (sleep-for 0 300) - finally - do (if (not live-p) - (ein:log 'error - "Kernel for %s failed to launch" - (ein:$notebook-notebook-name nb)) - (funcall callback nb))))) + (cl-loop repeat 50 + for live-p = (ein:kernel-live-p (ein:$notebook-kernel nb)) + until live-p + do (sleep-for 0 300) + finally + do (if (not live-p) + (ein:log 'error + "Kernel for %s failed to launch" + (ein:$notebook-notebook-name nb)) + (funcall callback nb))))) (errback-nbopen (lambda (url-or-port status-code) (if (eq status-code 404) (ein:notebooklist-new-notebook-with-name @@ -342,13 +346,13 @@ if necessary. Install CALLBACK (i.e., cell execution) upon notebook retrieval." (list 'ob-ein--initiate-session (ein:url url-or-port path)) (ein:notebook-url notebook) :type "DELETE")) - (loop repeat 8 - for extant = (file-exists-p path) - until (not extant) - do (sleep-for 0 500) - finally do (if extant - (ein:display-warning (format "cannot del %s" path)) - (ob-ein--initiate-session session kernelspec callback)))) + (cl-loop repeat 8 + for extant = (file-exists-p path) + until (not extant) + do (sleep-for 0 500) + finally do (if extant + (ein:display-warning (format "cannot del %s" path)) + (ob-ein--initiate-session session kernelspec callback)))) (notebook (funcall callback notebook)) ((string= (url-host parsed-url) ein:url-localhost) (ein:process-refresh-processes) @@ -397,7 +401,7 @@ Instead the binding will be to `ob-ein--edit-ctrl-c-ctrl-c', which will execute (unless (string= "none" it) (format "%s" it))) ein:url-localhost)) - (lang "ein-python") + (lang (car babel-info)) (kernelspec (or (cdr (assoc :kernelspec processed-parameters)) (ein:aif (cdr (assoc lang org-src-lang-modes)) (cons 'language (format "%s" it)) @@ -408,13 +412,14 @@ Instead the binding will be to `ob-ein--edit-ctrl-c-ctrl-c', which will execute kernelspec (lambda (notebook) (ein:connect-buffer-to-notebook notebook buffer t) - (define-key ein:connect-mode-map "\C-c\C-c" 'ob-ein--edit-ctrl-c-ctrl-c)))))) + (define-key ein:connect-mode-map "\C-c\C-c" 'ob-ein--edit-ctrl-c-ctrl-c)) + babel-info)))) (defun org-babel-edit-prep:ein-python (babel-info) (org-babel-edit-prep:ein babel-info)) -(loop for (lang . mode) in ob-ein-languages - do (ob-ein--babelize-lang lang mode)) +(cl-loop for (lang . mode) in ob-ein-languages + do (ob-ein--babelize-lang lang mode)) ;;;###autoload (if (featurep 'org) diff --git a/lisp/poly-ein.el b/lisp/poly-ein.el index 9674722d2..3c8266199 100644 --- a/lisp/poly-ein.el +++ b/lisp/poly-ein.el @@ -21,7 +21,7 @@ after changing this setting!" (defmacro poly-ein--remove-hook (label functions) "Remove any hooks saying LABEL from FUNCTIONS" - `(mapc (lambda (x) (when (search ,label (symbol-name x)) + `(mapc (lambda (x) (when (cl-search ,label (symbol-name x)) (remove-hook (quote ,functions) x t))) ,functions)) @@ -191,13 +191,13 @@ TYPE can be 'body, nil." (ein:display-warning (format "pm:get-span: no major mode for kernelspec language '%s'" lang))) (setq result-cm - (loop for ocm in (eieio-oref pm/polymode '-auto-innermodes) - when (equal mode (ein:oref-safe ocm :mode)) - return ocm - finally return (let ((new-mode (clone cm :mode mode))) - (object-add-to-list pm/polymode '-auto-innermodes + (cl-loop for ocm in (eieio-oref pm/polymode '-auto-innermodes) + when (equal mode (ein:oref-safe ocm :mode)) + return ocm + finally return (let ((new-mode (clone cm :mode mode))) + (object-add-to-list pm/polymode '-auto-innermodes new-mode) - new-mode)))) + new-mode)))) ;; Span is a zebra pattern of "body" (within input cell) and "nil" ;; (outside input cell). Decide boundaries of span and return it. (let ((rel (poly-ein--relative-to-input pos cell))) @@ -335,8 +335,8 @@ TYPE can be 'body, nil." (defsubst poly-ein--span-start-end (args) (if (or pm-initialization-in-progress (not poly-ein-mode)) args - (let* ((span-start (first args)) - (span-end (second args)) + (let* ((span-start (car args)) + (span-end (cadr args)) (range (pm-innermost-range (or span-start (point))))) (setq span-start (max (or span-start (car range)) (car range))) (setq span-end (min (or span-end (cdr range)) (cdr range))) diff --git a/test/test_ein_remote.py b/test/test_ein_remote.py new file mode 100644 index 000000000..531460927 --- /dev/null +++ b/test/test_ein_remote.py @@ -0,0 +1,69 @@ +# +import json +import sys + +sys.path.append('lisp/') + + +def test_undefined_object01(): + from ein_remote_safe import __ein_maybe_undefined_object + # Set up some user variables + a = 1 + b = "A string" + c = [1, 2, 3] + obj_a = __ein_maybe_undefined_object('a', locals=locals()) + obj_b = __ein_maybe_undefined_object('b', locals=locals()) + obj_c = __ein_maybe_undefined_object('c', locals=locals()) + assert obj_a == a + assert obj_b == b + assert obj_c == c + + +def test_undefined_object02(): + from ein_remote_safe import __ein_maybe_undefined_object + # Test on objects that don't exist + obj_a = __ein_maybe_undefined_object('not_an_object', locals=locals()) + assert obj_a == None + + +def test_get_object_info01(): + from ein_remote_safe import __ein_object_info_for + a = [1, 2, 3] + oinfo_a = __ein_object_info_for(a) + assert oinfo_a['type_name'] == "list" + assert oinfo_a['string_form'] == "[1, 2, 3]" + + +def test_get_oinfo_from_string(): + from ein_remote_safe import __ein_maybe_undefined_object, __ein_object_info_for + a = [1, 2,3 ] + oinfo_a = __ein_object_info_for(__ein_maybe_undefined_object('a', locals=locals())) + assert oinfo_a['type_name'] == "list" + assert oinfo_a['string_form'] == "[1, 2, 3]" + + +def test_generate_oinfo_data01(): + from ein_remote_safe import __ein_generate_oinfo_data + a = 1 + b = "A string" + c = [1, 2, 3] + oinfos = __ein_generate_oinfo_data(['a', 'b', 'c'], locals=locals()) + assert oinfos[0]['type_name'] == "int" + assert oinfos[1]['type_name'] == "str" + assert oinfos[2]['type_name'] == "list" + + +def test_generate_oinfo_package01(): + import heapq + from ein_remote_safe import __ein_generate_oinfo_data + oinfos = __ein_generate_oinfo_data(dir(heapq), locals=locals()) + assert len(oinfos) == 8 # Not everything in the package will generate object info. + assert oinfos[0]['type_name'] == "dict" + + +def test_generate_oinfo_package02(): + import sys + from ein_remote_safe import __ein_generate_oinfo_data + oinfos = __ein_generate_oinfo_data(dir(sys), locals=locals()) + assert len(oinfos) == 7 + assert oinfos[0]['type_name'] == "str" diff --git a/tools/install-virtualenv.sh b/tools/install-virtualenv.sh index b3e886dc6..658ab66d7 100644 --- a/tools/install-virtualenv.sh +++ b/tools/install-virtualenv.sh @@ -10,6 +10,8 @@ WORKDIR=${HOME}/local if [ "x$TRAVIS_OS_NAME" = "xosx" ]; then brew list pyenv-virtualenv &>/dev/null || HOMEBREW_NO_AUTO_UPDATE=1 brew install pyenv-virtualenv + eval "$(pyenv init -)" + eval "$(pyenv virtualenv-init -)" case "${TOXENV}" in py27) @@ -20,5 +22,14 @@ if [ "x$TRAVIS_OS_NAME" = "xosx" ]; then pyenv install -s 3.5.2 pyenv virtualenv -f 3.5.2 py35 ;; + py36) + pyenv install -s 3.6.9 + pyenv virtualenv -f 3.6.9 py36 + ;; + py37) + pyenv install -s 3.7.5 + pyenv virtualenv -f 3.7.5 py37 + pyenv rehash + ;; esac fi diff --git a/tools/travis-logs.sh b/tools/travis-logs.sh new file mode 100755 index 000000000..551de3823 --- /dev/null +++ b/tools/travis-logs.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +travis logs -i | sed '/^begin 664/,/^end/!d' | uudecode