Merge pull request #502 from dickmao/polymode

Polymode
This commit is contained in:
John Miller 2019-04-03 10:59:38 -05:00 committed by GitHub
commit 6b752bc36e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
42 changed files with 763 additions and 639 deletions

2
.gitignore vendored
View file

@ -27,6 +27,6 @@ _static
.travis.yml.swp
*.zip
.gitattributes
.ecukes-temp*
.ecukes*
dist
*.ob-ein.ipynb

View file

@ -20,8 +20,8 @@ In your `init.el` or `.emacs`, add the following:
Now whatever changes you make to the repo will be reflected in new emacs instances.
Dev tools
---------------
Most dev functionality lies in `ein-dev.el` the most important of which is `ein:dev-start-debug` which activates full logging and backtrace on error.
---------
`M-x ein:dev-start-debug` activates full logging and backtrace on error.
Quick sanity checking
---------------------

2
Cask
View file

@ -19,6 +19,8 @@
(depends-on "auto-complete")
(depends-on "company")
(depends-on "smartrep")
(depends-on "polymode")
(depends-on "markdown-mode")
(depends-on "px")
(depends-on "f")
(depends-on "s"))

View file

@ -19,14 +19,19 @@ README.rst: README.in.rst
(load \"ein-notebook\") \
(describe-minor-mode \"ein:notebook-mode\") \
(with-current-buffer \"*Help*\" (princ (buffer-string))))" 2>/dev/null \
| tools/readme-sed.sh "KEYS NOTEBOOK" README.in.rst > README.rst0
| tools/readme-sed.sh "KEYS NOTEBOOK" README.in.rst "key.*binding" > README.rst0
sed "/CI VERSION/c"`grep -o 'emacs-[0-9][.0-9]*' .travis.yml | sort -n | head -1 | grep -o '[.0-9]*'` README.rst0 > README.rst1
grep ';;' lisp/ein.el \
| awk '/;;;\s*Commentary/{within=1;next}/;;;\s*/{within=0}within' \
| sed -e 's/^\s*;;*\s*//g' \
| tools/readme-sed.sh "COMMENTARY" README.rst1 > README.rst0
cask eval "(progn \
(add-to-list 'load-path \"./lisp\") \
(load \"ein-connect\") \
(describe-minor-mode \"ein:connect-mode\") \
(with-current-buffer \"*Help*\" (princ (buffer-string))))" 2>/dev/null \
| tools/readme-sed.sh "KEYS CONNECT" README.rst0 > README.rst
rm README.rst0
| tools/readme-sed.sh "KEYS CONNECT" README.rst0 "key.*binding" > README.rst
rm README.rst0 README.rst1
.PHONY: autoloads
autoloads:
@ -59,7 +64,13 @@ test-jupyterhub:
-cask exec ecukes --tags @jupyterhub
.PHONY: test
test: quick test-int
test: quick test-int test-poly
.PHONY: 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; (ret=$$? ; rm -f features/support/test-poly.el && exit $$ret)
.PHONY: test-int
test-int:

View file

@ -4,9 +4,7 @@
--- or **E**\ IN **I**\ s not only for **N**\ otebooks.
Emacs IPython Notebook (EIN) lets you edit and run Jupyter_ (formerly IPython) notebooks within Emacs. It channels all the power of Emacs without the idiosyncrasies of in-browser editing.
EIN was originally written by tkf_. More `complete documentation`_ is available.
.. COMMENTARY (see Makefile)
.. |build-status|
image:: https://secure.travis-ci.org/millejoh/emacs-ipython-notebook.png?branch=master
@ -29,21 +27,13 @@ Install from MELPA_ (recommended) or ``make install`` from github source.
Usage
=====
There are multiple, mutually exclusive ways to launch EIN.
Start EIN using ONE of the following:
Launch a local session
----------------------
``M-x ein:jupyter-server-start`` (aliased ``M-x ein:run``) launches a jupyter process from emacs.
- Open an ``.ipynb`` file normally in emacs and press ``C-c C-o``, or,
- ``M-x ein:run`` launches a jupyter process from emacs, or,
- ``M-x ein:login`` to a running jupyter server
Login to a local or remote session
----------------------------------
``M-x ein:notebooklist-login`` (aliased ``M-x ein:login``) to a running jupyter server.
Jupyter services relayed over HTTP such as ``mybinder.org`` require cookie authentication. Issuing ``C-u M-x ein:login`` prompts for cookie information. See the `Wiki`_ for more information.
Open a notebook file
--------------------
Open an ``.ipynb`` file normally in emacs and press ``C-c C-o``.
Use ``C-u M-x ein:login`` for services such as ``mybinder.org`` requiring cookie authentication.
.. _Cask: https://cask.readthedocs.io/en/latest/guide/installation.html
.. _MELPA: http://melpa.org/#/
@ -51,40 +41,37 @@ Open an ``.ipynb`` file normally in emacs and press ``C-c C-o``.
It doesn't work
---------------
EIN is tested and developed on GNU Emacs and works best on Emacs versions 25.3
and later. Your mileage may vary with the `spacemacs layer`_ and other
*emacsen*. For a full list of dependencies see the `documentation`_.
EIN is tested on GNU Emacs versions
.. CI VERSION (see Makefile)
and later. Your mileage may vary with the `spacemacs layer`_ and other *emacsen*.
You may also try to self-diagnose:
First invoke ``M-x ein:dev-start-debug``. Then reproduce the error.
Higher level diagnostics appear in ``M-x ein:log-pop-to-all-buffer``.
High level diagnostics appear in ``M-x ein:log-pop-to-all-buffer``.
Lower level diagnostics (the actual ``curl`` requests) appear in ``M-x ein:log-pop-to-request-buffer``.
Low level diagnostics appear in ``M-x ein:log-pop-to-request-buffer``.
If you cannot resolve the problem, file an issue using ``M-x ein:dev-bug-report-template``. Please ensure the resulting system output does not include information sensitive to your institution.
.. _`documentation`: http://millejoh.github.io/emacs-ipython-notebook/#requirements
Highlighted Features
====================
* Easily copy cells between different notebooks.
* Execute code from an arbitrary buffer in a running kernel. See `Keybindings - Connect`_.
* Jump to definition via ``M-.``
* Completion via company-mode_.
* Limited jupyterhub_ support.
If you cannot resolve the problem, file an issue using ``M-x ein:dev-bug-report-template``.
.. _spacemacs layer: https://github.com/syl20bnr/spacemacs/tree/master/layers/%2Blang/ipython-notebook
.. _auto-complete: https://github.com/auto-complete/auto-complete
.. _company-mode: https://github.com/company-mode/company-mode
.. _jupyterhub: https://github.com/jupyterhub/jupyterhub
I want to use Elpy, ESS, LSP, etc.
==================================
Enable `polymode`_ via::
M-x customize-group RET ein
Toggle Ein:Polymode
Org-mode Integration
====================
EIN provides org-babel functionality similar to ob-ipython_ and scimax_. Acknowledgements to those fine packages.
EIN provides org-babel functionality similar to ob-ipython_ and scimax_.
*Language* is ``ein``. The ``:session`` header argument is the notebook url, e.g., ``https://localhost:8888/my.ipynb``, or simply ``localhost``, in which case EIN will evaluate org blocks in an anonymous notebook::
@ -98,40 +85,27 @@ EIN provides org-babel functionality similar to ob-ipython_ and scimax_. Acknow
plt.plot(x,y)
#+END_SRC
You may also specify the port, i.e., ``localhost:8889``. See `complete details`_.
You may also specify the port, i.e., ``localhost:8889``. See `ob-ein details`_.
.. _ob-ipython: https://github.com/gregsexton/ob-ipython/
.. _polymode: https://github.com/polymode/polymode
.. _ob-ipython: https://github.com/gregsexton/ob-ipython
.. _scimax: https://github.com/jkitchin/scimax
.. _complete details: http://millejoh.github.io/emacs-ipython-notebook/#org-mode-integration
.. _ob-ein details: http://millejoh.github.io/emacs-ipython-notebook/#org-mode-integration
Screenshots
===========
Connected Buffers
=================
.. figure:: https://github.com/millejoh/emacs-ipython-notebook/wiki/images/demo_plotnormal.PNG
:alt: Plotting in Emacs IPython Notebook
Use ``M-x ein:connect-to-notebook`` to submit code from an arbitrary buffer to a running jupyter kernel. See `connected buffer details`_.
.. figure:: https://github.com/millejoh/emacs-ipython-notebook/wiki/images/R-kernel-example.PNG
:alt: EIN connecting to an R kernel
.. _connected buffer details: http://millejoh.github.io/emacs-ipython-notebook/#connected-buffer
See `more <https://github.com/millejoh/emacs-ipython-notebook/wiki/Screenshots>`_!
Keybindings - Notebook
----------------------
Keymap (C-h m)
==============
::
.. KEYS NOTEBOOK (see Makefile)
Keybindings - Connect
---------------------
You can execute code from an arbitrary buffer in a running kernel via
``M-x ein:connect-to-notebook``.
::
.. KEYS CONNECT (see Makefile)
Links
=====
* `Complete documentation <http://millejoh.github.io/emacs-ipython-notebook/>`_

View file

@ -4,7 +4,9 @@
--- or **E**\ IN **I**\ s not only for **N**\ otebooks.
Emacs IPython Notebook (EIN) lets you edit and run Jupyter_ (formerly IPython) notebooks within Emacs. It channels all the power of Emacs without the idiosyncrasies of in-browser editing.
Emacs IPython Notebook (EIN) lets you edit and run Jupyter_ (formerly IPython)
notebooks within Emacs. It channels all the power of Emacs without the
idiosyncrasies of in-browser editing.
EIN was originally written by tkf_. More `complete documentation`_ is available.
@ -29,21 +31,13 @@ Install from MELPA_ (recommended) or ``make install`` from github source.
Usage
=====
There are multiple, mutually exclusive ways to launch EIN.
Start EIN using ONE of the following:
Launch a local session
----------------------
``M-x ein:jupyter-server-start`` (aliased ``M-x ein:run``) launches a jupyter process from emacs.
- Open an ``.ipynb`` file normally in emacs and press ``C-c C-o``, or,
- ``M-x ein:run`` launches a jupyter process from emacs, or,
- ``M-x ein:login`` to a running jupyter server
Login to a local or remote session
----------------------------------
``M-x ein:notebooklist-login`` (aliased ``M-x ein:login``) to a running jupyter server.
Jupyter services relayed over HTTP such as ``mybinder.org`` require cookie authentication. Issuing ``C-u M-x ein:login`` prompts for cookie information. See the `Wiki`_ for more information.
Open a notebook file
--------------------
Open an ``.ipynb`` file normally in emacs and press ``C-c C-o``.
Use ``C-u M-x ein:login`` for services such as ``mybinder.org`` requiring cookie authentication.
.. _Cask: https://cask.readthedocs.io/en/latest/guide/installation.html
.. _MELPA: http://melpa.org/#/
@ -51,40 +45,37 @@ Open an ``.ipynb`` file normally in emacs and press ``C-c C-o``.
It doesn't work
---------------
EIN is tested and developed on GNU Emacs and works best on Emacs versions 25.3
and later. Your mileage may vary with the `spacemacs layer`_ and other
*emacsen*. For a full list of dependencies see the `documentation`_.
EIN is tested on GNU Emacs versions
25.2
and later. Your mileage may vary with the `spacemacs layer`_ and other *emacsen*.
You may also try to self-diagnose:
First invoke ``M-x ein:dev-start-debug``. Then reproduce the error.
Higher level diagnostics appear in ``M-x ein:log-pop-to-all-buffer``.
High level diagnostics appear in ``M-x ein:log-pop-to-all-buffer``.
Lower level diagnostics (the actual ``curl`` requests) appear in ``M-x ein:log-pop-to-request-buffer``.
Low level diagnostics appear in ``M-x ein:log-pop-to-request-buffer``.
If you cannot resolve the problem, file an issue using ``M-x ein:dev-bug-report-template``. Please ensure the resulting system output does not include information sensitive to your institution.
.. _`documentation`: http://millejoh.github.io/emacs-ipython-notebook/#requirements
Highlighted Features
====================
* Easily copy cells between different notebooks.
* Execute code from an arbitrary buffer in a running kernel. See `Keybindings - Connect`_.
* Jump to definition via ``M-.``
* Completion via company-mode_.
* Limited jupyterhub_ support.
If you cannot resolve the problem, file an issue using ``M-x ein:dev-bug-report-template``.
.. _spacemacs layer: https://github.com/syl20bnr/spacemacs/tree/master/layers/%2Blang/ipython-notebook
.. _auto-complete: https://github.com/auto-complete/auto-complete
.. _company-mode: https://github.com/company-mode/company-mode
.. _jupyterhub: https://github.com/jupyterhub/jupyterhub
I want to use Elpy, ESS, LSP, etc.
==================================
Enable `polymode`_ via::
M-x customize-group RET ein
Toggle Ein:Polymode
Org-mode Integration
====================
EIN provides org-babel functionality similar to ob-ipython_ and scimax_. Acknowledgements to those fine packages.
EIN provides org-babel functionality similar to ob-ipython_ and scimax_.
*Language* is ``ein``. The ``:session`` header argument is the notebook url, e.g., ``https://localhost:8888/my.ipynb``, or simply ``localhost``, in which case EIN will evaluate org blocks in an anonymous notebook::
@ -98,25 +89,22 @@ EIN provides org-babel functionality similar to ob-ipython_ and scimax_. Acknow
plt.plot(x,y)
#+END_SRC
You may also specify the port, i.e., ``localhost:8889``. See `complete details`_.
You may also specify the port, i.e., ``localhost:8889``. See `ob-ein details`_.
.. _ob-ipython: https://github.com/gregsexton/ob-ipython/
.. _polymode: https://github.com/polymode/polymode
.. _ob-ipython: https://github.com/gregsexton/ob-ipython
.. _scimax: https://github.com/jkitchin/scimax
.. _complete details: http://millejoh.github.io/emacs-ipython-notebook/#org-mode-integration
.. _ob-ein details: http://millejoh.github.io/emacs-ipython-notebook/#org-mode-integration
Screenshots
===========
Connected Buffers
=================
.. figure:: https://github.com/millejoh/emacs-ipython-notebook/wiki/images/demo_plotnormal.PNG
:alt: Plotting in Emacs IPython Notebook
Use ``M-x ein:connect-to-notebook`` to submit code from an arbitrary buffer to a running jupyter kernel. See `connected buffer details`_.
.. figure:: https://github.com/millejoh/emacs-ipython-notebook/wiki/images/R-kernel-example.PNG
:alt: EIN connecting to an R kernel
.. _connected buffer details: http://millejoh.github.io/emacs-ipython-notebook/#connected-buffer
See `more <https://github.com/millejoh/emacs-ipython-notebook/wiki/Screenshots>`_!
Keybindings - Notebook
----------------------
Keymap (C-h m)
==============
::
@ -203,37 +191,6 @@ Keybindings - Notebook
C-c M-{ ein:notebook-worksheet-move-prev
C-c M-} ein:notebook-worksheet-move-next
Keybindings - Connect
---------------------
You can execute code from an arbitrary buffer in a running kernel via
``M-x ein:connect-to-notebook``.
::
key binding
--- -------
C-c Prefix Command
ESC Prefix Command
C-: ein:shared-output-eval-string
M-, ein:pytools-jump-back-command
M-. ein:pytools-jump-to-source-command
C-c C-a ein:connect-toggle-autoexec
C-c C-c ein:connect-run-or-eval-buffer
C-c C-h ein:pytools-request-tooltip-or-help
C-c TAB ein:completer-complete
C-c C-l ein:connect-reload-buffer
C-c C-o ein:console-open
C-c C-r ein:connect-eval-region
C-c C-x ein:tb-show
C-c C-z ein:connect-pop-to-notebook
C-c C-, ein:pytools-jump-back-command
C-c C-. ein:pytools-jump-to-source-command
C-c C-/ ein:notebook-scratchsheet-open
Links
=====
* `Complete documentation <http://millejoh.github.io/emacs-ipython-notebook/>`_

View file

@ -1,3 +1,4 @@
@connect
Scenario: Company completion in a python buffer
Given I set "ein:completion-backend" to eval "(quote ein:use-company-backend)"
Given I kill all websocket buffers
@ -9,7 +10,6 @@ Scenario: Company completion in a python buffer
And I type "import itertools"
And I press "RET"
And I call "ein:connect-run-buffer"
And I wait 3 seconds
And I type "itertools."
And I call "company-complete"
And I wait for completions "itertools.chain"
@ -24,6 +24,7 @@ Scenario: Company completion in a python buffer
Then I should see "itertools.chain"
Then no completion traffic
@connect
Scenario: Test shared eval
Given new default notebook
When I open temp file "connect.py"
@ -31,9 +32,9 @@ Scenario: Test shared eval
And I connect to default notebook
And I evaluate the python code "1+1"
And I switch to buffer like "*ein:shared-output*"
And I dump buffer
And I wait for buffer to say "2"
@connect
Scenario: Connect buffer to a running notebook
Given new default notebook
When I open temp file "connect.py"
@ -51,9 +52,10 @@ Scenario: Connect buffer to a running notebook
And I press "RET"
And I press "RET"
And I call "ein:connect-eval-buffer"
And I wait 2 seconds
And I switch to log expr "ein:log-all-buffer-name"
And I wait for buffer to say "test01"
And I switch to buffer like "Untitled"
And I evaluate the python code "test01()"
And I switch to buffer like "*ein:shared-output*"
And I dump buffer
And I wait for buffer to say "'hello'"

View file

@ -1,7 +1,7 @@
@eldoc
Scenario: not running server locally
Given I enable "ein:enable-eldoc-support"
Given I fset "ein:pytools-add-sys-path" to "ignore"
Given I clear log expr "ein:log-all-buffer-name"
Given new default notebook
And I type "import math"
And I press "C-a"
@ -53,6 +53,13 @@ Scenario: company completion
Given new default notebook
Given I set "ein:completion-backend" to eval "(quote ein:use-none-backend)"
@rename
Scenario: rename notebook
Given new default notebook
And I press "C-c C-/"
And I switch to buffer like "Untitled"
And rename notebook to "Renamed" succeeds
@switch
Scenario: switch kernel
Given new default notebook

View file

@ -45,6 +45,7 @@ Scenario: Specific port, portless localhost refers to same, concurrent execution
@org
Scenario: portless url with path, image, C-c ' lets you C-c C-c as well
Given I set "ein:completion-backend" to eval "(quote ein:use-none-backend)"
Given I stop the server
When I open temp file "path.org"
And I call "org-mode"

14
features/polymode.feature Normal file
View file

@ -0,0 +1,14 @@
@poly
Scenario: selection spans cells
Given new default notebook
And I press "C-c C-b"
And I press "C-<up>"
And I press "C-p"
And I press "C-SPC"
And I press "C-n"
And I press "C-n"
And I press "C-n"
And I press "C-n"
Then newlined region should be "In [ ]:\n\n\nIn [ ]:\n"
And I press "C-g"
Then the region should not be active

View file

@ -30,12 +30,13 @@
(When "^I switch kernel to \"\\(.+\\)\"$"
(lambda (kernel-name)
(let ((notebook (ein:notebook-switch-kernel (ein:get-notebook) kernel-name)))
(loop repeat 10
until (ein:kernel-live-p (ein:$notebook-kernel notebook))
do (sleep-for 0 500)
finally do (should (string= "R" (ein:$kernelspec-language
(ein:$notebook-kernelspec notebook))))))))
(cl-letf (((symbol-function 'R-mode) #'ignore))
(let ((notebook (ein:notebook-switch-kernel (ein:get-notebook) kernel-name)))
(loop repeat 10
until (ein:kernel-live-p (ein:$notebook-kernel notebook))
do (sleep-for 0 500)
finally do (should (string= "R" (ein:$kernelspec-language
(ein:$notebook-kernelspec notebook)))))))))
(When "^I kill kernel$"
(lambda ()
@ -103,6 +104,21 @@
(lambda (substr)
(switch-to-buffer (car (-non-nil (mapcar (lambda (b) (if (search substr (buffer-name b)) b)) (buffer-list)))))))
(When "^rename notebook to \"\\(.+\\)\" succeeds$"
(lambda (new-name)
(let* ((old-name (ein:$notebook-notebook-name ein:%notebook%))
(old-count
(length (seq-filter (lambda (b)
(search old-name (buffer-name b)))
(buffer-list)))))
(ein:notebook-rename-command new-name)
(ein:testing-wait-until
(lambda ()
(= old-count
(length (seq-filter (lambda (b)
(search new-name (buffer-name b)))
(buffer-list)))))))))
(When "^I am in notebooklist buffer$"
(lambda ()
(switch-to-buffer (ein:notebooklist-get-buffer (car (ein:jupyter-server-conn-info))))
@ -160,6 +176,11 @@
(ein:jupyter-server-start (executable-find "jupyterhub") nil))
(ein:testing-wait-until (lambda () (ein:notebooklist-list)) nil 20000 1000))))
(When "^newlined region should be \"\\(.*\\)\"$"
(lambda (region)
(should (string= (s-replace "\\n" "\n" region)
(buffer-substring-no-properties (region-beginning) (region-end))))))
(When "^I start \\(and login to \\)?the server configured \"\\(.*\\)\"$"
(lambda (login config)
(When "I stop the server")
@ -301,17 +322,18 @@
(When "^I wait for cell to execute$"
(lambda ()
(let* ((cell (ein:worksheet-get-current-cell :cell-p #'ein:codecell-p))
(orig (if (slot-boundp cell 'input-prompt-number)
(slot-value cell 'input-prompt-number))))
(call-interactively #'ein:worksheet-execute-cell)
(ein:testing-wait-until
(lambda ()
(ein:aand (and (slot-boundp cell 'input-prompt-number)
(slot-value cell 'input-prompt-number))
(and (numberp it)
(not (equal orig it)))))
nil 10000 2000))))
(poly-ein-base
(let* ((cell (ein:worksheet-get-current-cell :cell-p #'ein:codecell-p))
(orig (if (slot-boundp cell 'input-prompt-number)
(slot-value cell 'input-prompt-number))))
(call-interactively #'ein:worksheet-execute-cell)
(ein:testing-wait-until
(lambda ()
(ein:aand (and (slot-boundp cell 'input-prompt-number)
(slot-value cell 'input-prompt-number))
(and (numberp it)
(not (equal orig it)))))
nil 10000 2000)))))
(When "^I undo again$"
(lambda ()

View file

@ -43,7 +43,7 @@
do (sleep-for 0 500)
finally do (when (ein:notebook-live-p notebook)
(ein:display-warning (format "cannot close %s" path))))
do (when (search "Untitled" path)
do (when (or (search "Untitled" path) (search "Renamed" path))
(ein:notebooklist-delete-notebook path)
(loop repeat 8
with fullpath = (concat (file-name-as-directory ein:testing-jupyter-server-root) path)
@ -60,7 +60,6 @@
(Setup
(ein:dev-start-debug)
(setq ein:jupyter-server-args '("--no-browser" "--debug"))
(setq ein:notebook-autosave-frequency 0)
(setq ein:notebook-create-checkpoint-on-save nil)
(setq ein:testing-dump-file-log (concat default-directory "log/ecukes.log"))
@ -68,6 +67,7 @@
(setq ein:testing-dump-file-server (concat default-directory "log/ecukes.server"))
(setq ein:testing-dump-file-request (concat default-directory "log/ecukes.request"))
(setq org-confirm-babel-evaluate nil)
(setq transient-mark-mode t)
(Given "I start and login to the server configured \"\\n\""))
(After

View file

@ -1,3 +1,4 @@
@undo
Scenario: Undo turned off
Given I disable "ein:worksheet-enable-undo"
Given new default notebook
@ -6,7 +7,7 @@ Scenario: Undo turned off
And I undo demoting errors
Then I should see message "demoted: (user-error No undo information in this buffer)"
@yank
@undo
Scenario: Kill yank doesn't break undo
Given I enable "ein:worksheet-enable-undo"
Given new default notebook
@ -24,6 +25,7 @@ Scenario: Kill yank doesn't break undo
And I press "C-/"
Then the cursor should be at point "74"
@undo
Scenario: Collapse doesn't break undo
Given I enable "ein:worksheet-enable-undo"
Given new default notebook
@ -48,7 +50,7 @@ Scenario: Collapse doesn't break undo
And I undo again
Then the cursor should be at point "55"
@prob
@undo
Scenario: Test the conflagrative commands
Given I enable "ein:worksheet-enable-undo"
Given new default notebook
@ -79,6 +81,7 @@ Scenario: Test the conflagrative commands
And I undo again
Then the cursor should be at point "22"
@undo
Scenario: Clear output doesn't break undo
Given I enable "ein:worksheet-enable-undo"
Given new default notebook
@ -103,6 +106,7 @@ Scenario: Clear output doesn't break undo
And I undo again
Then the cursor should be at point "55"
@undo
Scenario: Moving cells doesn't break undo
Given I enable "ein:worksheet-enable-undo"
Given new default notebook
@ -168,7 +172,7 @@ Scenario: Split and merge don't break undo
And I undo again
Then the cursor should be at point "50"
@reopened
@undo
Scenario: Undo needs to at least work for reopened notebooks
Given I enable "ein:worksheet-enable-undo"
Given old notebook "undo.ipynb"
@ -215,7 +219,7 @@ Scenario: Undo needs to at least work for reopened notebooks
And I undo again
Then the cursor should be at point "125"
@toggle
@undo
Scenario: Toggling between markdown and codecell does not break undo
Given I enable "ein:worksheet-enable-undo"
Given new default notebook

View file

@ -97,7 +97,7 @@
"metadata": {
"kernelspec": {
"display_name": "Python",
"name": "python"
"name": "python3"
},
"name": "undo.ipynb"
},

View file

@ -38,7 +38,7 @@
"Set to `t' to use preset a little bit hacky auto-complete configuration.
When this option is enabled, cached omni completion is available."
:type 'boolean
:group 'ein-completion)
:group 'ein)
(defvar ein:ac-sources (and (boundp 'ac-sources)
(default-value 'ac-sources))
@ -113,8 +113,7 @@ When this option is enabled, cached omni completion is available."
(cons (lambda (_ content __)
(ein:ac-prepare-completion (plist-get content :matches)))
nil))
#'ignore)))
(ein:use-ac-jedi-backend (ein:jedi-complete))))
#'ignore)))))
;;; Completer interface
@ -150,7 +149,6 @@ compatibility with `ein:completer-finish-completing-default'."
(when matches ; No auto-complete drop-down list when no matches
(let ((ac-expand-on-auto-complete expand))
(ac-start))))
;; Why `ac-start'? See: `jedi:complete'.
;;; Async document request hack
@ -235,7 +233,7 @@ first candidate when the `ac-menu' pops up."
Adding `ac-sources' to them makes it impossible to different
`ac-sources' between chunks, which is good for EIN but may not
for other package."
(and ein:%notebook%
(and ein:notebook-mode
(ein:eval-if-bound 'ein:notebook-mumamo-mode)
(eql major-mode ein:mumamo-codecell-mode)
(ein:ac-setup)))

View file

@ -216,7 +216,6 @@ a number will limit the number of lines in a cell output."
(("markdown") 'ein:markdowncell)
(("raw") 'ein:rawcell)
(("heading") 'ein:headingcell)
;; Defined in ein-shared-output.el:
(("shared-output") 'ein:shared-output-cell)
(t (error "No cell type called %S" type))))
@ -226,17 +225,6 @@ a number will limit the number of lines in a cell output."
(setf (gethash 'slide_type ss-table) slide-type)
ss-table))
(defun ein:preprocess-nb4-cell (cell-data)
(let ((source (plist-get cell-data :source)))
(when (and (string= (plist-get cell-data :cell_type) "markdown")
(string-match "\\(^#+\\)" source)
(not (string-match "\n+" source)))
(let ((heading-level (match-end 0)))
(plist-put cell-data :cell_type "heading")
(plist-put cell-data :level heading-level)
(plist-put cell-data :source (substring source (1+ heading-level))))))
cell-data)
(defun ein:cell-from-type (type &rest args)
(apply (ein:cell-class-from-type type) args))
@ -250,10 +238,9 @@ a number will limit the number of lines in a cell output."
base-type)))
(defun ein:cell-from-json (data &rest args)
(let ((data (ein:preprocess-nb4-cell data))
(cell (ein:cell-init (apply #'ein:cell-from-type
(ein:cell--determine-cell-type data) args)
data)))
(let ((cell (ein:cell-init (apply #'ein:cell-from-type
(ein:cell--determine-cell-type data) args)
data)))
(when (plist-get data :metadata)
(ein:oset-if-empty cell 'metadata (plist-get data :metadata))
(ein:aif (plist-get (slot-value cell 'metadata) :slideshow)
@ -328,9 +315,7 @@ a number will limit the number of lines in a cell output."
do (progn
(setf (slot-value new 'element)
(plist-put (slot-value new 'element) k
(plist-get old-element k)))
)
)
(plist-get old-element k)))))
;; setting ewoc nodes
(loop for en in (ein:cell-all-element cell)
for node = (ewoc-data en)
@ -558,7 +543,7 @@ Return language name as a string or `nil' when not defined.
(when (and (not (slot-value cell 'collapsed))
(= index ein:cell-max-num-outputs)
(> (point) (point-at-bol)))
;; The first output which exceeds `ein:cell-max-num-outputs'.
;; The first output which exceeds `ein:cell-max-num-outputs'.
(ein:insert-read-only "\n"))
(ein:insert-read-only "."))
(let ((out (nth index (slot-value cell 'outputs))))
@ -673,10 +658,12 @@ Return language name as a string or `nil' when not defined.
"Set `:collapsed' slot of CELL and invalidate output ewoc nodes."
(unless (eq (slot-value cell 'collapsed) collapsed)
(setf (slot-value cell 'collapsed) collapsed)
(apply #'ewoc-invalidate
(slot-value cell 'ewoc)
(append (ein:cell-element-get cell :output)
(list (ein:cell-element-get cell :footer))))))
(let ((inhibit-read-only t)
(buffer-undo-list t))
(apply #'ewoc-invalidate
(slot-value cell 'ewoc)
(append (ein:cell-element-get cell :output)
(list (ein:cell-element-get cell :footer)))))))
(cl-defmethod ein:cell-collapse ((cell ein:codecell))
(ein:cell-set-collapsed cell t))

View file

@ -27,18 +27,13 @@
;;; Code:
(eval-when-compile (require 'cl))
(require 'jedi-core nil t)
(require 'deferred)
(require 'ein-completer)
(require 'company nil t)
(declare-function jedi:complete-request "jedi-core")
(autoload 'company-begin-backend "company")
(autoload 'company-doc-buffer "company")
;; Duplicates ein:jedi--completer-complete in ein-jedi.
;; Let's refactor and enhance our calm!
(defun ein:company--deferred-complete ()
(let ((d (deferred:new #'identity)))
(ein:completer-complete
@ -59,23 +54,6 @@
(unless (stringp replies) ;; if not an error
(ein:completions--prepare-matches prefix fetcher replies))))))
(defun ein:company--complete-jedi (fetcher)
(deferred:$
(deferred:parallel
(jedi:complete-request)
(ein:company--deferred-complete))
(deferred:nextc it
(lambda (replies)
(ein:completions--prepare-matches-jedi fetcher replies)))))
(defun ein:completions--prepare-matches-jedi (cb replies)
(destructuring-bind
(_ ((&key matches &allow-other-keys) ; :complete_reply
_metadata))
replies
(ein:completions--build-oinfo-cache matches)
(funcall cb matches)))
(defun ein:completions--prepare-matches (prefix fetcher replies)
(destructuring-bind
((&key matches cursor_start cursor_end &allow-other-keys) ; :complete_reply
@ -94,7 +72,8 @@
(interactive (list 'interactive))
(cl-case command
(interactive (company-begin-backend 'ein:company-backend))
(prefix (and (or (ein:worksheet-at-codecell-p) ein:connect-mode)
(prefix (and (eq ein:completion-backend 'ein:use-company-backend)
(or (ein:worksheet-at-codecell-p) ein:connect-mode)
(ein:get-kernel)
(ein:object-at-point)))
(annotation (let ((kernel (ein:get-kernel)))
@ -114,9 +93,6 @@
(ein:aif cached it
(unless (and (looking-at "[[:nonascii:]]") (ein:company--punctuation-check (thing-at-point 'line) (current-column)))
(case ein:completion-backend
(ein:use-company-jedi-backend
(cons :async (lambda (cb)
(ein:company--complete-jedi cb))))
(t
(cons :async
(lambda (cb)

View file

@ -128,19 +128,13 @@
(ein:case-equal msg-type
(("stream" "display_data" "pyout" "execute_result")
(ein:aif (plist-get content :text)
(setf (gethash obj (ein:$kernel-oinfo-cache kernel)) (ein:json-read-from-string it))))
(setf (gethash obj (ein:$kernel-oinfo-cache kernel))
(ein:json-read-from-string it))))
(("error" "pyerr")
;; This should only happen if ein-pytools is not loaded, which can
;; happen if the user restarts the kernel. Could probably use better logic
;; to determine if pytools have been loaded or not.
(ein:pytools-load-safely kernel)
(ein:log 'verbose "ein:completions--prepare-oinfo: %S" (plist-get content :traceback)))))
;; It's okay, bad things happen. Not everything in python is going to have a
;; pdef, which might cause the call to the json parser to fail. No need to
;; log an error as that will unnecessarily fill the log buffer, but we do
;; register a debug message in case someone really needs to know what is
;; happening.
(error (ein:log 'debug "ein:completions--prepare-oinfo: [%s] %s" err obj)
(ein:log 'verbose "ein:completions--prepare-oinfo: %s"
(plist-get content :traceback)))))
(error (ein:log 'verbose "ein:completions--prepare-oinfo: [%s] %s"
(error-message-string err) output)
(setf (gethash obj (ein:$kernel-oinfo-cache kernel)) :json-false))))
;;; Support for Eldoc

View file

@ -217,13 +217,16 @@ notebooks."
"Evaluate the whole buffer. Note that this will run the code
inside the ``if __name__ == \"__main__\":`` block."
(interactive)
(deferred:$
(deferred:next
(lambda ()
(ein:shared-output-eval-string (ein:connect-get-kernel) (buffer-string) nil :silent t)))
(deferred:nextc it
(lambda ()
(ein:connect-execute-autoexec-cells))))
(lexical-let ((b (current-buffer)))
(deferred:$
(deferred:next
(lambda ()
(with-current-buffer b
(ein:shared-output-eval-string (ein:connect-get-kernel) (buffer-string) nil :silent t))))
(deferred:nextc it
(lambda ()
(with-current-buffer b
(ein:connect-execute-autoexec-cells))))))
(ein:log 'info "Whole buffer is sent to the kernel."))
(defun ein:connect-run-buffer (&optional ask-command)
@ -399,13 +402,7 @@ notebook."
(ein:use-ac-backend
(define-key ein:connect-mode-map "." 'ein:ac-dot-complete)
(auto-complete-mode))
(ein:use-ac-jedi-backend
(define-key ein:connect-mode-map "." 'ein:ac-dot-complete)
(auto-complete-mode))
(ein:use-company-backend
(add-to-list 'company-backends #'ein:company-backend)
(company-mode))
(ein:use-company-jedi-backend
(add-to-list 'company-backends #'ein:company-backend)
(company-mode))))

View file

@ -168,7 +168,6 @@ global setting. For global setting and more information, see
:notebook-version (ein:$notebook-api-version nb)
:raw-content nb-content)))
;;; Managing/listing the content hierarchy
(defvar *ein:content-hierarchy* (make-hash-table :test #'equal)

View file

@ -42,10 +42,6 @@
:group 'applications
:prefix "ein:")
(defvar ein:version "0.15.0"
"Version number for Emacs IPython Notebook (EIN).")
;;; Configuration
(defcustom ein:url-or-port '(8888)
@ -124,27 +120,25 @@ pair of TO-PYTHON and FROM-PYTHON."
(defun ein:default-url-or-port ()
(or ein:default-url-or-port (car ein:url-or-port) 8888))
(defun ein:version (&optional copy-to-kill)
(defun ein:version (&optional interactively copy-to-kill)
"Return a longer version string.
With prefix argument, copy the string to kill ring.
The result contains `ein:version' and either git revision (if
the source is in git repository) or elpa version."
(interactive "P")
(let* ((suffix ; git or elpa
(interactive (list t current-prefix-arg))
(let* ((version
(or (and (ein:git-root-p
(concat (file-name-as-directory ein:source-dir) ".."))
(let ((default-directory ein:source-dir))
(ein:git-revision-dirty)))
(and (string-match "/ein-\\([0-9\\.]*\\)/$" ein:source-dir)
(match-string 1 ein:source-dir))))
(version (if suffix (concat ein:version "-" suffix) ein:version)))
(when (called-interactively-p 'interactive)
(match-string 1 ein:source-dir)))))
(when interactively
(message "EIN version is %s" version))
(when copy-to-kill
(kill-new version))
version))
;;; Server attribute getters. These should be moved to ein-open.el
(defvar *ein:notebook-version* (make-hash-table :test #'equal)

View file

@ -123,7 +123,7 @@ callback (`websocket-callback-debug-on-error') is enabled."
;; (setq deferred:debug t)
(setq request-log-level (quote debug))
(lexical-let ((curl-trace (concat temporary-file-directory "curl-trace")))
(setq request-curl-options `("--trace-ascii" ,curl-trace))
(nconc request-curl-options `("--trace-ascii" ,curl-trace))
(add-function :after
(symbol-function 'request--curl-callback)
(lambda (&rest args)
@ -228,6 +228,14 @@ callback (`websocket-callback-debug-on-error') is enabled."
(apply #'call-process it nil t nil args)
(buffer-string))))
(defsubst ein:dev-packages ()
(lexical-let (result)
(cl-letf (((symbol-function 'define-package)
(lambda (&rest args)
(setq result (mapcar (lambda (x) (symbol-name (first x))) (nth 3 args))))))
(load "ein-pkg")
result)))
(defun ein:dev-sys-info ()
(list
"EIN system info"
@ -253,10 +261,7 @@ callback (`websocket-callback-debug-on-error') is enabled."
(ein:dev-dump-vars '("source-dir")))
:lib (seq-filter (lambda (info) (plist-get info :path))
(mapcar #'ein:dev-sys-info--lib
'("websocket" "request" "mumamo"
"auto-complete" "popup" "fuzzy" "pos-tip"
"python" "python-mode" "markdown-mode"
"smartrep" "anything" "helm")))))
(ein:dev-packages)))))
(defun ein:dev-show-sys-info (&optional show-in-buffer)
"Show Emacs and library information."
@ -285,8 +290,7 @@ callback (`websocket-callback-debug-on-error') is enabled."
(error (insert (format "`ein:dev-sys-info' produce: %S" err))))
(insert "```\n")
(goto-char (point-min))
(when (fboundp 'markdown-mode)
(markdown-mode))
(markdown-mode)
(pop-to-buffer buffer))))
(defun ein:dev-print-sys-info (&optional stream)

View file

@ -1,96 +0,0 @@
;;; ein-jedi.el --- ein Jedi
;; Copyright (C) 2012 Takafumi Arakaki
;; Author: Takafumi Arakaki <aka.tkf at gmail.com>
;; This file is NOT part of GNU Emacs.
;; ein-jedi.el is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; ein-jedi.el is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with ein-jedi.el.
;; If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;;
;;; Code:
(require 'jedi nil t)
(require 'jedi-core nil t)
(require 'ein-completer)
(eval-when-compile (require 'ein-connect))
(declare-function jedi:complete-request "jedi-core")
(declare-function ein:ac-prepare-completion "ein-ac")
(defvar ein:jedi-dot-complete-sources
'(ac-source-jedi-direct ac-source-ein-direct))
(defun ein:jedi--completer-complete ()
(let ((d (deferred:new #'identity)))
(ein:completer-complete
(ein:get-kernel)
(list :complete_reply
(cons (lambda (d* &rest args) (deferred:callback-post d* args))
d))
(apply-partially (lambda (d* err) (deferred:callback-post d* err)) d))
d))
;;;###autoload
(defun* ein:jedi-complete (&key (expand ac-expand-on-auto-complete))
"Run completion using candidates calculated by EIN and Jedi."
(interactive)
(lexical-let ((expand expand))
(deferred:$
(deferred:parallel ; or `deferred:earlier' is better?
(jedi:complete-request) ;; need tkf/emacs-jedi submodule
(ein:jedi--completer-complete))
(deferred:nextc it
(lambda (replies)
(ein:jedi-complete--prepare-completion replies expand))))))
(defun ein:jedi-complete--prepare-completion (replies expand)
(destructuring-bind
(_ ; ignore `jedi:complete-request' what returns.
((&key matched_text matches &allow-other-keys) ; :complete_reply
_)) ; ignore metadata
replies
(ein:ac-prepare-completion matches)
(let ((ac-expand-on-auto-complete expand))
(ac-start))))
;; Why `ac-start'? See: `jedi:complete'.
;;;###autoload
(defun ein:jedi-dot-complete ()
"Insert \".\" and run `ein:jedi-complete'."
(interactive)
(ein:ac-dot-complete (lambda () (ein:jedi-complete :expand nil))))
;;;###autoload
(defun ein:jedi-setup ()
"Setup auto-completion using EIN and Jedi.el_ together.
Jedi.el_ is a Python auto-completion library for Emacs.
To use EIN and Jedi together, add the following in your Emacs setup before loading EIN.::
(setq ein:completion-backend 'ein:use-ac-jedi-backend)
.. _Jedi.el: https://github.com/tkf/emacs-jedi"
(let ((map ein:connect-mode-map))
(define-key map "\C-c\C-i" 'ein:jedi-complete)))
(provide 'ein-jedi)
;;; ein-jedi.el ends here

View file

@ -775,7 +775,6 @@ We need this to have proper behavior for the 'Stop' command in the ein:notebookl
(ein:funcall-packed it content metadata))))))))
(ein:log 'debug "KERNEL--HANDLE-IOPUB-REPLY: finished"))
;;; Utility functions
(defun ein:kernel-filename-to-python (kernel filename)

View file

@ -162,20 +162,13 @@ This function may raise an error."
(set-keymap-parent ein:notebook-multilang-mode-map ess-r-mode-map))))
(defun ein:ml-lang-setup (kernelspec)
(ein:case-equal (ein:$kernelspec-language kernelspec)
(("python") (ein:ml-lang-setup-python))
(("R") (ein:ml-lang-setup-R))))
(funcall (intern (concat "ein:ml-lang-setup-" (ein:$kernelspec-language kernelspec)))))
;; (defun ein:ml-lang-setup-markdown ()
;; "Use `markdown-mode-map'. NOTE: This function is not used now."
;; (when (featurep 'markdown-mode)
;; (set-keymap-parent ein:notebook-multilang-mode-map markdown-mode-map)))
;; FIXME: dynamically call ein:ml-lang-setup-LANG using
;; `post-command-hook'.
;; FIMXE: add more ein:ml-lang-setup-LANG to switch kaymap.
;;; yasnippet
(defvar ein:ml-yasnippet-parents '(python-mode markdown-mode)
@ -194,7 +187,6 @@ This function may raise an error."
(eval-after-load "yasnippet" '(ein:ml-setup-yasnippet))
;;; Imenu Support
;; Most of this is borrowed from python.el

View file

@ -54,7 +54,6 @@
(require 'ein-cell-output)
(require 'ein-worksheet)
(require 'ein-iexec)
(require 'ein-jedi)
(require 'ein-scratchsheet)
(require 'ein-notification)
(require 'ein-completer)
@ -70,7 +69,8 @@
(require 'ein-shared-output)
(require 'ein-notebooklist)
(require 'ein-multilang)
(require 'poly-ein)
;;; Configuration
(make-obsolete-variable 'ein:notebook-discard-output-on-save nil "0.2.0")
@ -229,7 +229,6 @@ Current buffer for these functions is set to the notebook buffer.")
args)))
notebook))
;;; Destructor
(defun ein:notebook-del (notebook)
@ -305,7 +304,6 @@ will be updated with kernel's cwd."
(defun ein:notebook-name-getter (notebook)
(cons #'ein:notebook-name notebook))
;;; Open notebook
(defun ein:notebook-url (notebook)
@ -327,7 +325,12 @@ will be updated with kernel's cwd."
(with-current-buffer (ein:notebook-buffer notebook*)
(ein:worksheet-focus-cell))
(unless no-pop*
(pop-to-buffer (ein:notebook-buffer notebook*)))
(with-current-buffer (ein:notebook-buffer notebook*)
(if ein:polymode
(progn
(pm-select-buffer (pm-innermost-span))
(pop-to-buffer (pm-span-buffer (pm-innermost-span))))
(pop-to-buffer (ein:notebook-buffer notebook*)))))
(when (null (plist-member (ein:$notebook-metadata notebook*)
:kernelspec))
(ein:aif (ein:$notebook-kernelspec notebook*)
@ -665,15 +668,24 @@ This is equivalent to do ``C-c`` in the console program."
(defun ein:notebook--worksheet-render (notebook ws)
(ein:worksheet-render ws)
(with-current-buffer (ein:worksheet-buffer ws)
(ein:notebook-mode)
(when (ein:$notebook-kernelspec notebook)
(ein:ml-lang-setup (ein:$notebook-kernelspec notebook)))
;; Now that major-mode is set, set buffer local variables:
(ein:worksheet-maybe-disable-undo ws)
(ein:notebook--notification-setup notebook)
(ein:notebook-setup-kill-buffer-hook)
(setq ein:%notebook% notebook)))
(save-current-buffer
(with-current-buffer (ein:worksheet-buffer ws)
(if ein:polymode
(poly-ein-mode)
;; Changing major mode here is super dangerous as it
;; kill-all-local-variables.
;; Our saviour has been `ein:deflocal' which applies 'permanent-local
;; to variables assigned up to this point, but we ought not rely on it
(funcall (ein:notebook-choose-mode))
(ein:worksheet-reinstall-undo-hooks ws)
(ein:aif (ein:$notebook-kernelspec notebook)
(ein:ml-lang-setup it)))
(ein:notebook-mode)
(ein:notebook--notification-setup notebook)
(ein:notebook-setup-kill-buffer-hook)
(setq ein:%notebook% notebook)
(when ein:polymode
(poly-ein-fontify-buffer notebook)))))
(defun ein:notebook--notification-setup (notebook)
(ein:notification-setup
@ -728,9 +740,7 @@ This is equivalent to do ``C-c`` in the console program."
data
(setf (ein:$notebook-metadata notebook) metadata)
(setf (ein:$notebook-nbformat notebook) nbformat)
(setf (ein:$notebook-nbformat-minor notebook) nbformat_minor)
;;(setf (ein:$notebook-notebook-name notebook) (plist-get metadata :name))
)
(setf (ein:$notebook-nbformat-minor notebook) nbformat_minor))
(setf (ein:$notebook-worksheets notebook)
(cl-case (ein:$notebook-nbformat notebook)
(3 (ein:read-nbformat3-worksheets notebook data))
@ -738,7 +748,7 @@ This is equivalent to do ``C-c`` in the console program."
(t (ein:log 'error "nbformat version %s unsupported"
(ein:$notebook-nbformat notebook)))))
(ein:notebook--worksheet-render notebook
(nth 0 (ein:$notebook-worksheets notebook)))
(first (ein:$notebook-worksheets notebook)))
notebook)
(defun ein:read-nbformat3-worksheets (notebook data)
@ -821,7 +831,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
(car (ein:$notebook-worksheets notebook))))
(first (ein:$notebook-worksheets notebook))))
(get-buffer buf))))
(condition-case err
(with-current-buffer (ein:notebook-buffer notebook)
@ -879,14 +889,12 @@ This is equivalent to do ``C-c`` in the console program."
NAME is any non-empty string that does not contain '/' or '\\'."
(interactive
(list (read-string "Rename notebook: "
(list (read-string "Rename to: "
(ein:$notebook-notebook-path ein:%notebook%))))
(unless (and (string-match ".ipynb" path) (= (match-end 0) (length path)))
(unless (and (string-match "\\.ipynb" path) (= (match-end 0) (length path)))
(setq path (format "%s.ipynb" path)))
(let ((content (ein:content-from-notebook ein:%notebook%))
(old-name (ein:$notebook-notebook-name ein:%notebook%)))
(ein:log 'info "Renaming notebook at URL %s"
(ein:notebook-url ein:%notebook%))
(let ((content (ein:content-from-notebook ein:%notebook%)))
(ein:log 'verbose "Renaming notebook %s" (ein:notebook-url ein:%notebook%))
(ein:content-rename content path #'ein:notebook-rename-success
(list ein:%notebook% content))))
@ -912,7 +920,8 @@ NAME is any non-empty string that does not contain '/' or '\\'.
(setf (ein:$notebook-notebook-path notebook) (ein:$content-path content))
(ein:notebook-put-opened-notebook notebook)
(mapc #'ein:worksheet-set-buffer-name
(ein:$notebook-worksheets notebook))
(append (ein:$notebook-worksheets notebook)
(ein:$notebook-scratchsheets notebook)))
(ein:log 'info "Notebook renamed to %s." (ein:$content-name content)))
(defun ein:notebook-close (notebook)
@ -1384,74 +1393,89 @@ Use simple `python-mode' based notebook mode when MuMaMo is not installed::
(with-eval-after-load "ein-smartrep"
(ein:smartrep-config ein:notebook-mode-map))
(defmacro ein:notebook--define-key (keymap key defn)
"Ideally we could override just the keymap binding with a (string . wrapped) cons pair (as opposed to messing with the DEFN itself), but then describe-minor-mode unhelpfully shows ?? for the keymap commands."
`(progn
(when (functionp ,defn)
(add-function :around (symbol-function ,defn)
(lambda (f &rest args)
(poly-ein-base (apply f args)))))
(define-key ,keymap ,key ,defn)))
(let ((map ein:notebook-mode-map))
(define-key map "\C-ci" 'ein:inspect-object)
(define-key map "\C-c'" 'ein:edit-cell-contents)
(define-key map "\C-cS" 'ein:worksheet-toggle-slideshow-view)
(define-key map "\C-c\C-c" 'ein:worksheet-execute-cell)
(define-key map (kbd "M-RET") 'ein:worksheet-execute-cell-and-goto-next)
(define-key map (kbd "<M-S-return>")
(ein:notebook--define-key map "\C-ci" 'ein:inspect-object)
(ein:notebook--define-key map "\C-c'" 'ein:edit-cell-contents)
(ein:notebook--define-key map "\C-cS" 'ein:worksheet-toggle-slideshow-view)
(ein:notebook--define-key map "\C-c\C-c" 'ein:worksheet-execute-cell)
(ein:notebook--define-key map (kbd "M-RET") 'ein:worksheet-execute-cell-and-goto-next)
(ein:notebook--define-key map (kbd "<M-S-return>")
'ein:worksheet-execute-cell-and-insert-below)
(define-key map (kbd "C-c C-'") 'ein:worksheet-turn-on-autoexec)
(define-key map "\C-c\C-e" 'ein:worksheet-toggle-output)
(define-key map "\C-c\C-v" 'ein:worksheet-set-output-visibility-all)
(define-key map "\C-c\C-l" 'ein:worksheet-clear-output)
(define-key map (kbd "C-c C-S-l") 'ein:worksheet-clear-all-output)
(define-key map (kbd "C-c C-;") 'ein:shared-output-show-code-cell-at-point)
(define-key map "\C-c\C-k" 'ein:worksheet-kill-cell)
(define-key map "\C-c\M-w" 'ein:worksheet-copy-cell)
(define-key map "\C-c\C-w" 'ein:worksheet-copy-cell)
(define-key map "\C-c\C-y" 'ein:worksheet-yank-cell)
(define-key map "\C-c\C-a" 'ein:worksheet-insert-cell-above)
(define-key map "\C-c\C-b" 'ein:worksheet-insert-cell-below)
(define-key map "\C-c\C-t" 'ein:worksheet-toggle-cell-type)
(define-key map "\C-c\C-d" 'ein:worksheet-toggle-slide-type)
(define-key map "\C-c\C-u" 'ein:worksheet-change-cell-type)
(define-key map "\C-c\C-s" 'ein:worksheet-split-cell-at-point)
(define-key map "\C-c\C-m" 'ein:worksheet-merge-cell)
(define-key map "\C-c\C-n" 'ein:worksheet-goto-next-input)
(define-key map "\C-c\C-p" 'ein:worksheet-goto-prev-input)
(define-key map (kbd "C-<up>") 'ein:worksheet-goto-prev-input)
(define-key map (kbd "C-<down>") 'ein:worksheet-goto-next-input)
(define-key map (kbd "C-c <up>") 'ein:worksheet-move-cell-up)
(define-key map (kbd "C-c <down>") 'ein:worksheet-move-cell-down)
(define-key map (kbd "M-<up>") 'ein:worksheet-move-cell-up)
(define-key map (kbd "M-<down>") 'ein:worksheet-move-cell-down)
(define-key map "\C-c\C-h" 'ein:pytools-request-tooltip-or-help)
(define-key map "\C-c\C-i" 'ein:completer-complete)
(define-key map (kbd "C-c C-$") 'ein:tb-show)
(define-key map "\C-c\C-x" nil)
(define-key map "\C-c\C-x\C-l" 'ein:notebook-toggle-latex-fragment)
(define-key map "\C-c\C-x\C-r" 'ein:notebook-restart-session-command)
(define-key map "\C-c\C-r" 'ein:notebook-reconnect-session-command)
(define-key map "\C-c\C-z" 'ein:notebook-kernel-interrupt-command)
(define-key map "\C-c\C-q" 'ein:notebook-kill-kernel-then-close-command)
(define-key map (kbd "C-c C-#") 'ein:notebook-close)
(define-key map (kbd "C-:") 'ein:shared-output-eval-string)
(define-key map "\C-c\C-f" 'ein:file-open)
(define-key map "\C-c\C-o" 'ein:notebook-open)
(define-key map "\C-x\C-s" 'ein:notebook-save-notebook-command)
(define-key map "\C-x\C-w" 'ein:notebook-rename-command)
(ein:notebook--define-key map (kbd "C-c C-'") 'ein:worksheet-turn-on-autoexec)
(ein:notebook--define-key map "\C-c\C-e" 'ein:worksheet-toggle-output)
(ein:notebook--define-key map "\C-c\C-v" 'ein:worksheet-set-output-visibility-all)
(ein:notebook--define-key map "\C-c\C-l" 'ein:worksheet-clear-output)
(ein:notebook--define-key map (kbd "C-c C-S-l") 'ein:worksheet-clear-all-output)
(ein:notebook--define-key map (kbd "C-c C-;") 'ein:shared-output-show-code-cell-at-point)
(ein:notebook--define-key map "\C-c\C-k" 'ein:worksheet-kill-cell)
(ein:notebook--define-key map "\C-c\M-w" 'ein:worksheet-copy-cell)
(ein:notebook--define-key map "\C-c\C-w" 'ein:worksheet-copy-cell)
(ein:notebook--define-key map "\C-c\C-y" 'ein:worksheet-yank-cell)
(ein:notebook--define-key map "\C-c\C-a" 'ein:worksheet-insert-cell-above)
(ein:notebook--define-key map "\C-c\C-b" 'ein:worksheet-insert-cell-below)
(ein:notebook--define-key map "\C-c\C-t" 'ein:worksheet-toggle-cell-type)
(ein:notebook--define-key map "\C-c\C-d" 'ein:worksheet-toggle-slide-type)
(ein:notebook--define-key map "\C-c\C-u" 'ein:worksheet-change-cell-type)
(ein:notebook--define-key map "\C-c\C-s" 'ein:worksheet-split-cell-at-point)
(ein:notebook--define-key map "\C-c\C-m" 'ein:worksheet-merge-cell)
(ein:notebook--define-key map "\C-c\C-n" 'ein:worksheet-goto-next-input)
(ein:notebook--define-key map "\C-c\C-p" 'ein:worksheet-goto-prev-input)
(ein:notebook--define-key map (kbd "C-<up>") 'ein:worksheet-goto-prev-input)
(ein:notebook--define-key map (kbd "C-<down>") 'ein:worksheet-goto-next-input)
(ein:notebook--define-key map (kbd "C-c <up>") 'ein:worksheet-move-cell-up)
(ein:notebook--define-key map (kbd "C-c <down>") 'ein:worksheet-move-cell-down)
(ein:notebook--define-key map (kbd "M-<up>") 'ein:worksheet-move-cell-up)
(ein:notebook--define-key map (kbd "M-<down>") 'ein:worksheet-move-cell-down)
(ein:notebook--define-key map "\C-c\C-h" 'ein:pytools-request-tooltip-or-help)
(ein:notebook--define-key map "\C-c\C-i" 'ein:completer-complete)
(ein:notebook--define-key map (kbd "C-c C-$") 'ein:tb-show)
(ein:notebook--define-key map "\C-c\C-x" nil)
(ein:notebook--define-key map "\C-c\C-x\C-l" 'ein:notebook-toggle-latex-fragment)
(ein:notebook--define-key map "\C-c\C-x\C-r" 'ein:notebook-restart-session-command)
(ein:notebook--define-key map "\C-c\C-r" 'ein:notebook-reconnect-session-command)
(ein:notebook--define-key map "\C-c\C-z" 'ein:notebook-kernel-interrupt-command)
(ein:notebook--define-key map "\C-c\C-q" 'ein:notebook-kill-kernel-then-close-command)
(ein:notebook--define-key map (kbd "C-c C-#") 'ein:notebook-close)
(ein:notebook--define-key map (kbd "C-:") 'ein:shared-output-eval-string)
(ein:notebook--define-key map "\C-c\C-f" 'ein:file-open)
(ein:notebook--define-key map "\C-c\C-o" 'ein:notebook-open)
(ein:notebook--define-key map "\C-x\C-s" 'ein:notebook-save-notebook-command)
(ein:notebook--define-key map "\C-x\C-w" 'ein:notebook-rename-command)
(define-key map "\M-." 'ein:pytools-jump-to-source-command)
(define-key map (kbd "C-c C-.") 'ein:pytools-jump-to-source-command)
(define-key map "\M-," 'ein:pytools-jump-back-command)
(define-key map (kbd "C-c C-,") 'ein:pytools-jump-back-command)
(define-key map "\M-p" 'ein:worksheet-previous-input-history)
(define-key map "\M-n" 'ein:worksheet-next-input-history)
(define-key map (kbd "C-c C-/") 'ein:notebook-scratchsheet-open)
(ein:notebook--define-key map "\M-p" 'ein:worksheet-previous-input-history)
(ein:notebook--define-key map "\M-n" 'ein:worksheet-next-input-history)
(ein:notebook--define-key map (kbd "C-c C-/") 'ein:notebook-scratchsheet-open)
;; Worksheets
(define-key map (kbd "C-c !") 'ein:worksheet-rename-sheet)
(define-key map (kbd "C-c {") 'ein:notebook-worksheet-open-prev-or-last)
(define-key map (kbd "C-c }") 'ein:notebook-worksheet-open-next-or-first)
(define-key map (kbd "C-c M-{") 'ein:notebook-worksheet-move-prev)
(define-key map (kbd "C-c M-}") 'ein:notebook-worksheet-move-next)
(define-key map (kbd "C-c +") 'ein:notebook-worksheet-insert-next)
(define-key map (kbd "C-c M-+") 'ein:notebook-worksheet-insert-prev)
(define-key map (kbd "C-c -") 'ein:notebook-worksheet-delete)
(loop for n from 1 to 8
do (define-key map (format "\C-c%d" n)
(intern (format "ein:notebook-worksheet-open-%sth" n))))
(define-key map "\C-c9" 'ein:notebook-worksheet-open-last)
(ein:notebook--define-key map (kbd "C-c !") 'ein:worksheet-rename-sheet)
(ein:notebook--define-key map (kbd "C-c {") 'ein:notebook-worksheet-open-prev-or-last)
(ein:notebook--define-key map (kbd "C-c }") 'ein:notebook-worksheet-open-next-or-first)
(ein:notebook--define-key map (kbd "C-c M-{") 'ein:notebook-worksheet-move-prev)
(ein:notebook--define-key map (kbd "C-c M-}") 'ein:notebook-worksheet-move-next)
(ein:notebook--define-key map (kbd "C-c +") 'ein:notebook-worksheet-insert-next)
(ein:notebook--define-key map (kbd "C-c M-+") 'ein:notebook-worksheet-insert-prev)
(ein:notebook--define-key map (kbd "C-c -") 'ein:notebook-worksheet-delete)
(ein:notebook--define-key map "\C-c1" 'ein:notebook-worksheet-open-1th)
(ein:notebook--define-key map "\C-c2" 'ein:notebook-worksheet-open-2th)
(ein:notebook--define-key map "\C-c3" 'ein:notebook-worksheet-open-3th)
(ein:notebook--define-key map "\C-c4" 'ein:notebook-worksheet-open-4th)
(ein:notebook--define-key map "\C-c5" 'ein:notebook-worksheet-open-5th)
(ein:notebook--define-key map "\C-c6" 'ein:notebook-worksheet-open-6th)
(ein:notebook--define-key map "\C-c7" 'ein:notebook-worksheet-open-7th)
(ein:notebook--define-key map "\C-c8" 'ein:notebook-worksheet-open-8th)
(ein:notebook--define-key map "\C-c9" 'ein:notebook-worksheet-open-last)
;; Menu
(easy-menu-define ein:notebook-menu map "EIN Notebook Mode Menu"
`("EIN Notebook"
@ -1597,24 +1621,17 @@ watch the fireworks!"
;; It is executed after toggling the mode, and before running MODE-hook.
(when ein:notebook-mode
(funcall (ein:notebook-choose-mode))
(case ein:completion-backend
(ein:use-ac-backend
(define-key ein:notebook-mode-map "." 'ein:notebook-ac-dot-complete)
(auto-complete-mode))
(ein:use-ac-jedi-backend
(define-key ein:notebook-mode-map "." 'ein:notebook-ac-dot-complete)
(ein:notebook--define-key ein:notebook-mode-map "." 'ein:notebook-ac-dot-complete)
(auto-complete-mode))
(ein:use-company-backend
(define-key ein:notebook-mode-map "." nil)
(company-mode))
(ein:use-company-jedi-backend
(define-key ein:notebook-mode-map "." nil)
(ein:notebook--define-key ein:notebook-mode-map "." nil)
(company-mode)))
(ein:aif ein:helm-kernel-history-search-key
(define-key ein:notebook-mode-map it 'helm-ein-kernel-history))
(ein:notebook--define-key ein:notebook-mode-map it 'helm-ein-kernel-history))
(ein:aif ein:anything-kernel-history-search-key
(define-key ein:notebook-mode-map it 'anything-ein-kernel-history))
(ein:notebook--define-key ein:notebook-mode-map it 'anything-ein-kernel-history))
(setq indent-tabs-mode nil) ;; Being T causes problems with Python code.
(when (and (featurep 'eldoc) ein:enable-eldoc-support)
(add-function :before-until (local 'eldoc-documentation-function)
@ -1622,8 +1639,7 @@ watch the fireworks!"
(eldoc-mode))
(ein:worksheet-imenu-setup)
(when ein:use-smartrep
(require 'ein-smartrep))
(ein:worksheet-reinstall-which-cell-hook)))
(require 'ein-smartrep))))
;; To avoid MuMaMo to discard `ein:notebook-mode', make it
;; permanent local.

View file

@ -325,7 +325,7 @@ Generated by `ein:header-line-define-mouse-commands'" slot)
As `header-line-format' is buffer local variable, it must be set
for each chunk when in
See also `ein:ac-setup-maybe'."
(and (ein:eval-if-bound 'ein:%notebook%)
(and ein:notebook-mode
(ein:eval-if-bound 'mumamo-multi-major-mode)
(setq header-line-format ein:header-line-format)))
(add-hook 'after-change-major-mode-hook 'ein:header-line-setup-maybe)

View file

@ -1,11 +1,13 @@
(define-package "ein"
"0.15.0"
"0.16.0"
"Emacs IPython Notebook"
'((websocket "1.7")
(auto-complete "1.4.0")
(request "0.3")
(deferred "0.5")
(cl-generic "0.3")
(polymode "0.1.5")
(markdown-mode "2.3")
(dash "2.13.0")
(s "1.11.0")
(skewer-mode "1.6.2")))

View file

@ -264,25 +264,32 @@ When the prefix argument ``C-u`` is given, open the source code
in the other window. You can explicitly specify the object by
selecting it."
(interactive "P")
(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.")
(ein:pytools-jump-to-source kernel object other-window
(when ein:propagate-connect
(ein:get-notebook)))))
(if poly-ein-mode
(cl-letf (((symbol-function 'xref--prompt-p) #'ignore))
(if other-window
(call-interactively #'xref-find-definitions-other-window)
(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.")
(ein:pytools-jump-to-source kernel object other-window
(when ein:propagate-connect
(ein:get-notebook))))))
(defun ein:pytools-jump-back-command (&optional other-window)
"Go back to the point where `ein:pytools-jump-to-source-command'
is executed last time. When the prefix argument ``C-u`` is
given, open the last point in the other window."
(interactive "P")
(when (ein:aand (car ein:pytools-jump-stack)
(equal (point) (marker-position it)))
(setq ein:pytools-jump-stack (cdr ein:pytools-jump-stack)))
(ein:aif (car ein:pytools-jump-stack)
(ein:goto-marker it other-window)
(ein:log 'info "Nothing on stack.")))
(if poly-ein-mode
(call-interactively #'xref-pop-marker-stack)
(when (ein:aand (car ein:pytools-jump-stack)
(equal (point) (marker-position it)))
(setq ein:pytools-jump-stack (cdr ein:pytools-jump-stack)))
(ein:aif (car ein:pytools-jump-stack)
(ein:goto-marker it other-window)
(ein:log 'info "Nothing on stack."))))
(define-obsolete-function-alias
'ein:pytools-eval-string-internal

View file

@ -27,25 +27,18 @@
;;; Code:
(defcustom ein:completion-backend 'ein:use-ac-backend
"Determines which completion backend to use in opened EIN notebooks.
(defcustom ein:completion-backend 'ein:use-none-backend
"EIN defaults to your individual company-mode or auto-complete-mode configuration. Change this setting to gather completions from the jupyter server::
After changing the value of this variable it is recommended that
you restart Emacs. The available completion backends are::
* ein:use-ac-backend : Use auto-complete with IPython's builtin completion engine.
* ein:use-ac-jedi-backend : Use auto-complete with the Jedi backend.
* ein:use-company-backend : Use company-mode with IPython's builtin completion engine.
* ein:use-company-jedi-backends : Use company-mode with the Jedi backend (currently not implemented).
* ein:use-none-backend: Avoid autocomplete altogether
* ein:use-none-backend: local completions only (configured outside EIN)
* ein:use-company-backend: company-style remote completions (elpy takes precedence)
* ein:use-ac-backend: deprecated auto-complete remote completions
"
:type '(choice
(const ein:use-ac-backend)
(const ein:use-ac-jedi-backend)
(const ein:use-none-backend)
(const ein:use-company-backend)
(const ein:use-company-jedi-backend)
(const ein:use-none-backend))
:group 'ein-completion)
(const ein:use-ac-backend))
:group 'ein)
(provide 'ein-subpackages)

View file

@ -71,6 +71,9 @@ while maintaining the undo list for the current buffer."
((= (length head) 1) `(if ,(car head) ,rest))
(t `(let (,head) (if ,(car head) ,rest)))))))
(defvar ein:local-variables '()
"Modified by `ein:deflocal'")
(defmacro ein:deflocal (name &optional initvalue docstring)
"Define permanent buffer local variable named NAME.
INITVALUE and DOCSTRING are passed to `defvar'."
@ -79,7 +82,8 @@ INITVALUE and DOCSTRING are passed to `defvar'."
`(progn
(defvar ,name ,initvalue ,docstring)
(make-variable-buffer-local ',name)
(put ',name 'permanent-local t)))
(put ',name 'permanent-local t)
(setq ein:local-variables (append ein:local-variables '(,name)))))
(defmacro ein:with-read-only-buffer (buffer &rest body)
(declare (indent 1))

View file

@ -34,8 +34,8 @@
(require 'ein-utils)
(require 'ein-cell)
(require 'ein-kill-ring)
(require 'poly-ein)
;;; Configuration
;; (define-obsolete-variable-alias
@ -57,10 +57,8 @@
:type 'boolean
:group 'ein)
(ein:deflocal buffer-local-enable-undo t
"Buffer local variable with activating undo accounting. Should not modify.")
"Buffer local variable activating undo accounting. Should not modify.")
(ein:deflocal ein:%cell-lengths% '()
"Buffer local variable with buffer-undo-list's current knowledge of cell lengths.")
@ -73,7 +71,10 @@
(intern (substring (slot-value cell 'cell-id) 0 5)))
(defun ein:worksheet--which-cell-hook (change-beg change-end prev-len)
(when (and (not (null buffer-undo-list)) (listp buffer-undo-list))
"Hook important for undo thats runs for everything we type (an after-change-functions hook).
Normalize `buffer-undo-list' by removing extraneous details, and update the ein:%which-cell% ledger that associates changes in `buffer-undo-list' with individual cells."
(when (and buffer-undo-list (listp buffer-undo-list))
(setq buffer-undo-list (cl-delete-if (lambda (u) (or (numberp u) (and (consp u) (markerp (car u))))) buffer-undo-list))
(let ((fill (- (length buffer-undo-list) (length ein:%which-cell%))))
(if (< fill 0)
@ -181,12 +182,16 @@
(setq ein:%which-cell% (-replace old-cell-id new-cell-id ein:%which-cell%)))
(let ((fill (- (length buffer-undo-list) (length ein:%which-cell%))))
(if (> (abs fill) 1)
;; TODO: reset ein:%which-cell% when major mode gets swapped
(progn
(let ((msg (format "Undo failure diagnostic %s %s | %s"
buffer-undo-list ein:%which-cell% fill)))
buffer-undo-list ein:%which-cell% fill))
(pm-allow-post-command-hook nil))
(setq ein:worksheet-enable-undo nil)
(ein:worksheet-maybe-disable-undo ein:%worksheet%)
(ein:worksheet-undo-setup ein:%worksheet%)
(when pm/polymode
(dolist (b (eieio-oref pm/polymode '-buffers))
(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")))
(if (< fill 0)
@ -371,11 +376,29 @@
(cl-defmethod ein:worksheet--get-buffer ((ws ein:worksheet))
(or (ein:worksheet-buffer ws)
(generate-new-buffer (ein:worksheet--buffer-name ws))))
(with-current-buffer (generate-new-buffer (ein:worksheet--buffer-name ws))
(let ((buffer-undo-list t))
(setf (ein:worksheet--ewoc ws)
(ein:ewoc-create 'ein:worksheet-pp
(ein:propertize-read-only "\n")
nil t))
(current-buffer)))))
(cl-defmethod ein:worksheet-set-buffer-name ((ws ein:worksheet))
(ein:with-live-buffer (ein:worksheet-buffer ws)
(rename-buffer (ein:worksheet--buffer-name ws) t)))
(dolist (b (or (and pm/polymode (eieio-oref pm/polymode '-buffers))
(list (current-buffer))))
(ein:with-live-buffer b
(rename-buffer
(let ((simple-name (ein:worksheet--buffer-name ein:%worksheet%)))
(if (and pm/polymode (not (eq (pm-base-buffer) (current-buffer))))
(let ((chunkmode (nth 3 (pm-innermost-span))))
(format "%s[%s]" simple-name
(replace-regexp-in-string
"poly-\\|-mode" ""
(symbol-name
(pm--get-existing-mode (eieio-oref chunkmode 'mode))))))
simple-name)))))))
(cl-defmethod ein:worksheet-set-modified-p ((ws ein:worksheet) dirty)
(ein:with-live-buffer (ein:worksheet-buffer ws)
@ -391,36 +414,35 @@
(not (ein:worksheet--show-slide-data-p ws)))
(ein:worksheet-render ws)))
(cl-defmethod ein:worksheet-maybe-disable-undo ((ws ein:worksheet))
(cl-defmethod ein:worksheet-undo-setup ((ws ein:worksheet))
(with-current-buffer (ein:worksheet--get-buffer ws)
(setq buffer-local-enable-undo ein:worksheet-enable-undo)
(let ((undo-binding (key-binding (kbd "C-/"))))
(if ein:worksheet-enable-undo
(if (eq undo-binding 'undo)
(setq buffer-local-enable-undo t)
(if buffer-local-enable-undo
(unless (eq undo-binding 'undo)
(setq buffer-local-enable-undo nil)
(ein:display-warning-once (format "Disabling undo for %s" undo-binding)))
(setq buffer-local-enable-undo nil)))
(when (not buffer-local-enable-undo)
(setq buffer-undo-list t))))
(ein:display-warning-once (format "Disabling undo for %s" undo-binding)))))
(ein:worksheet-reinstall-undo-hooks ws)
(if buffer-local-enable-undo
(progn
(setq buffer-undo-list nil)
(setq ein:%which-cell% nil)
(setq ein:%cell-lengths% nil))
(setq buffer-undo-list t))))
(cl-defmethod ein:worksheet-reinstall-undo-hooks ((ws ein:worksheet))
(with-current-buffer (ein:worksheet--get-buffer ws)
(if buffer-local-enable-undo
(add-hook 'after-change-functions 'ein:worksheet--which-cell-hook nil t)
(remove-hook 'after-change-functions 'ein:worksheet--which-cell-hook t))))
(cl-defmethod ein:worksheet-render ((ws ein:worksheet))
(with-current-buffer (ein:worksheet--get-buffer ws)
(setq ein:%worksheet% ws)
(when buffer-local-enable-undo
(setq buffer-undo-list nil)
(setq ein:%which-cell% nil)
(setq ein:%cell-lengths% nil))
(ein:worksheet-reinstall-which-cell-hook)
(let ((inhibit-read-only t))
(erase-buffer)
(let ((ewoc (let ((buffer-undo-list t))
(ein:ewoc-create 'ein:worksheet-pp
(ein:propertize-read-only "\n")
nil t)))
(cells (ein:worksheet--saved-cells ws)))
(setf (ein:worksheet--ewoc ws) ewoc)
(ein:worksheet-undo-setup ws)
(let ((inhibit-read-only t)
(ewoc (ein:worksheet--ewoc ws)))
(let ((cells (ein:worksheet--saved-cells ws)))
(if cells
(let ((buffer-undo-list t))
(mapc (lambda (c)
@ -641,8 +663,9 @@ kill-ring of Emacs (kill-ring for texts)."
(deactivate-mark)))))
(let ((cells (mapcar
(lambda (c)
(ein:cell-deactivate (ein:cell-copy c))) cells)))
(ein:log 'info "%s cells are copied." (length cells))
(ein:cell-deactivate (ein:cell-copy c)))
cells)))
(ein:log 'info "%s cells are copied." (length cells))
(ein:kill-new cells)))
(defun ein:worksheet-insert-clone (ws cell pivot up)
@ -857,16 +880,18 @@ If prefix is given, merge current cell into next cell."
t))
(unless next
(setq cell (ein:cell-prev cell))
(unless cell (error "No previous cell"))
(ein:cell-goto cell))
(let* ((next-cell (ein:cell-next cell))
(head (ein:cell-get-text cell)))
(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))
(insert head "\n"))
(when focus (ein:cell-goto next-cell))))
(if cell
(ein:cell-goto cell)
(message "No previous cell")))
(when cell
(let* ((next-cell (ein:cell-next cell))
(head (ein:cell-get-text cell)))
(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))
(insert head "\n"))
(when focus (ein:cell-goto next-cell)))))
;;; Cell selection.
@ -901,7 +926,7 @@ When NTH is specified, return NTH cell. Note that this function is
(defun ein:worksheet-goto-input (ewoc-node up)
(ein:aif (ein:worksheet-next-input-cell ewoc-node up)
(ein:cell-goto it)
(error "No %s input!" (if up "previous" "next"))))
(message "No %s input" (if up "previous" "next"))))
(defun ein:worksheet-goto-next-input (ewoc-node)
(interactive (list (and (ein:worksheet--get-ws-or-error)
@ -963,7 +988,7 @@ It is set in `ein:notebook-multilang-mode'."
(setq clone (ein:worksheet-insert-clone ws cell pivot-cell (if up "above" "below")))
(ein:cell-goto clone)
(oset ws :dirty t))
(error "No %s cell" (if up "previous" "next"))))
(message "No %s cell" (if up "previous" "next"))))
(defun ein:worksheet-move-cell-up (ws cell)
(interactive (list (ein:worksheet--get-ws-or-error)
@ -1293,12 +1318,6 @@ function."
for name = (ein:join-str "" (append sharps (list " " text)))
collect (cons name (ein:cell-input-pos-min cell))))
(defun ein:worksheet-reinstall-which-cell-hook ()
"Fontify clobbers the which-cell hook."
(if buffer-local-enable-undo
(add-hook 'after-change-functions 'ein:worksheet--which-cell-hook t t)
(remove-hook 'after-change-functions 'ein:worksheet--which-cell-hook t)))
(defun ein:worksheet-imenu-setup ()
"Called via notebook mode hooks."
(setq imenu-create-index-function #'ein:worksheet-imenu-create-index))

View file

@ -24,57 +24,12 @@
;;; Commentary:
;; ==================================
;; EIN -- Emacs IPython Notebook
;; ==================================
;; --- or **E**\ IN **I**\ s not only for **N**\ otebooks.
;; EIN works with IPython 2.x_, 3.x_, and Juptyer_! Note that remote and
;; password protected logins are working with IPython 3.x, but have not been
;; tested with Jupyter.
;; .. note:: The code has been stable enough for my day to day work, but there are
;; no guarantees for the safety for your notebook data. Please make sure
;; that you backup and backup often!
;; .. _2.x: http://ipython.org/ipython-doc/2/index.html
;; .. _3.x: http://ipython.org/ipython-doc/3/index.html
;; .. _Jupyter: http://jupyter.org
;; Features
;; ========
;; The Emacs IPython Notebook (EIN) provides a client for the IPython v2.x and
;; 3.x notebooks and an integrated REPL (like SLIME_) in Emacs. EIN makes
;; notebook editing very powerful by allowing you to use any Emacs features, it
;; also expose IPython features such as code evaluation, object inspection and
;; code completion to the Emacs side. These features can be accessed anywhere
;; in Emacs and improve Python code editing and reading in Emacs.
;; .. _SLIME: http://common-lisp.net/project/slime/
;; Highlighted features:
;; * Copy/paste cells, even to/from different notebooks.
;; * Console integration: You can easily connect to a kernel via the console
;; application. This enables you to start debugging in the same kernel. It is
;; even possible to connect to a console over ssh.
;; * An IPython kernel can be "connected" to any buffer. This enables you to
;; evaluate a buffer or buffer region using the same kernel as the notebook.
;; Notebook goodies such as tooltip help, help browser and code completion are
;; available in these buffers.
;; * Jump to definition (go to the definition by hitting ``M-.`` over an object).
;; Other notebook features:
;; * Inline images
;; * Auto/manual-completion
;; * Popup (tooltip) help
;; * Syntax highlighting in each cell types (Python/Markdown)
;; * Help browser (opens when executing ``function?``)
;; * Traceback viewer
;; Emacs IPython Notebook (EIN) lets you edit and run Jupyter_ (formerly IPython)
;; notebooks within Emacs. It channels all the power of Emacs without the
;; idiosyncrasies of in-browser editing.
;;
;; EIN was originally written by tkf_. More `complete documentation`_ is available.
;;
;;; Code:

299
lisp/poly-ein.el Normal file
View file

@ -0,0 +1,299 @@
(add-to-list 'features 'poly-lock) ;; skirt poly-lock
(require 'polymode)
(require 'ein-cell)
(require 'jit-lock)
(defmacro poly-ein-base (&rest body)
"Copy the undo accounting to the base buffer and run BODY in it."
`(let ((base-buffer (pm-base-buffer))
(derived-buffer (current-buffer))
(pm-allow-post-command-hook nil)
(pm-initialization-in-progress t))
(poly-ein--set-buffer derived-buffer base-buffer)
(condition-case err
(prog1 (progn ,@body)
(poly-ein--set-buffer base-buffer derived-buffer))
(error (message "%s" (error-message-string err))
(poly-ein--set-buffer base-buffer derived-buffer)))))
(defcustom ein:polymode nil
"Turn off hacky major mode emulations, turn on polymode."
:type 'boolean
:group 'ein)
(defclass pm-inner-overlay-chunkmode (pm-inner-auto-chunkmode)
()
"Inner chunkmode delimited by cell overlays.")
(cl-defmethod pm-get-span ((cm pm-inner-overlay-chunkmode) &optional pos)
"Return a list of the form (TYPE POS-START POS-END RESULT-CM).
TYPE can be 'body, nil."
(poly-ein-base
(setq pos (or pos (point)))
;; Assume: ein:worksheet-get-current-cell always returns non-nil
(let ((result-cm cm)
(span `(nil ,(point-min) ,(point-min)))
(cell (ein:worksheet-get-current-cell :pos pos :noerror nil)))
;; Change :mode if necessary
(ein:and-let* ((lang
(condition-case err
(ein:$kernelspec-language
(ein:$notebook-kernelspec
(ein:get-notebook)))
(error (message "%s: defaulting language to python"
(error-message-string err))
"python")))
(mode
(pm-get-mode-symbol-from-name
(cond ((ein:codecell-p cell) lang)
((ein:markdowncell-p cell) "markdown")
(t "fundamental"))))
((not (equal mode (ein:oref-safe cm :mode)))))
(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
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)))
(cond ((zerop rel)
(setq span `(body
,(ein:cell-input-pos-min cell)
,(1+ (ein:cell-input-pos-max cell)))))
((< rel 0)
(setq span `(nil
,(or (ein:aand (ein:cell-prev cell)
(1+ (ein:cell-input-pos-max it)))
(point-min))
,(ein:cell-input-pos-min cell))))
(t
(setq span `(nil
,(1+ (ein:cell-input-pos-max cell))
,(or (ein:aand (ein:cell-next cell)
(ein:cell-input-pos-min it))
(point-max)))))))
(append span (list result-cm)))))
(defun poly-ein-fontify-buffer (notebook)
"Called from `ein:notebook--worksheet-render'"
(with-current-buffer (ein:notebook-buffer notebook)
(save-excursion
(pm-map-over-spans
(lambda (span)
(condition-case err
(let ((syntax-propertize--done (point-min)))
(jit-lock-function (nth 1 span)))
(error (ein:log 'warn "ein:notebook--worksheet-render: %s"
(error-message-string err)))))))))
(defun poly-ein--relative-to-input (pos cell)
"Return -1 if POS before input, 1 if after input, 0 if within"
(let* ((input-pos-min (ein:cell-input-pos-min cell))
(input-pos-max (ein:cell-input-pos-max cell)))
(cond ((< pos input-pos-min) -1)
((> pos input-pos-max) 1)
(t 0))))
(defvar jit-lock-start)
(defvar jit-lock-end)
(defun poly-ein--hem-jit-lock (start end _old-len)
(when (and poly-ein-mode (not pm-initialization-in-progress))
(let ((range (pm-innermost-range (or start (point)))))
(setq jit-lock-start (max jit-lock-start (car range)))
(setq jit-lock-end (min jit-lock-end (cdr range))))))
(defun poly-ein-undo-damage (type)
(remove-hook 'after-change-functions 'polymode-flush-syntax-ppss-cache t)
(add-hook 'jit-lock-after-change-extend-region-functions #'poly-ein--hem-jit-lock t t)
(setq jit-lock-contextually nil) ; else recenter font-lock-fontify-keywords-region
(setq jit-lock-context-unfontify-pos nil)
(if (eq type 'host)
(setq syntax-propertize-function nil)
(setq syntax-propertize-function pm--syntax-propertize-function-original)
(add-function :before-until (local 'syntax-propertize-function)
#'poly-ein--unrelated-span)
(add-function :filter-args (local 'syntax-propertize-function)
#'poly-ein--span-start-end)))
(defun poly-ein-init-input-cell (_type)
(mapc (lambda (f) (add-to-list 'after-change-functions f))
(buffer-local-value 'after-change-functions (pm-base-buffer)))
(poly-ein-copy-state (pm-base-buffer) (current-buffer))
(ein:notebook-mode))
(defcustom pm-host/ein
(pm-host-chunkmode :name "ein"
:init-functions '(poly-ein-undo-damage))
"EIN host chunkmode"
:group 'poly-hostmodes
:type 'object)
(defcustom pm-inner/ein-input-cell
(pm-inner-overlay-chunkmode :name "ein-input-cell"
:init-functions '(poly-ein-undo-damage poly-ein-init-input-cell))
"EIN input cell."
:group 'poly-innermodes
:type 'object)
(defcustom poly-ein-mode-hook nil
"Hook for poly-ein-mode"
:type 'hook :group 'poly-ein)
;;;###autoload (autoload 'poly-ein-mode "poly-ein")
(define-polymode poly-ein-mode
:lighter " PM-ipynb"
:hostmode 'pm-host/ein
:innermodes '(pm-inner/ein-input-cell))
(defun poly-ein-copy-state (src-buf dest-buf)
"Consolidate fragility here."
(unless (eq src-buf dest-buf)
(with-current-buffer dest-buf (remove-overlays nil nil 'face 'ein:cell-input-area))
(mapc (lambda (ol)
(if (eq 'ein:cell-input-area (overlay-get ol 'face))
(move-overlay (copy-overlay ol)
(overlay-start ol) (overlay-end ol)
dest-buf)))
(with-current-buffer src-buf (overlays-in (point-min) (point-max))))
(pm--move-vars (append ein:local-variables '(header-line-format buffer-undo-list))
src-buf dest-buf)))
(defsubst poly-ein--set-buffer (src-buf dest-buf &optional switch)
(when (and (not (eq src-buf dest-buf))
(buffer-live-p src-buf)
(buffer-live-p dest-buf))
(cl-destructuring-bind (point window-start region-begin pos-visible _)
(with-current-buffer src-buf (list (point) (window-start)
(and switch (region-active-p) (mark))
(pos-visible-in-window-p)
(when switch (deactivate-mark))))
(poly-ein-copy-state src-buf dest-buf)
(if switch
(switch-to-buffer dest-buf)
(set-buffer dest-buf))
(when region-begin
(setq deactivate-mark nil) ;; someone is setting this, I don't know who
(push-mark region-begin t t))
(goto-char point)
(setq syntax-propertize--done (point-min))
(when switch
(when pos-visible
(set-window-start (get-buffer-window) window-start))
(bury-buffer-internal src-buf)
(set-window-prev-buffers
nil
(assq-delete-all src-buf (window-prev-buffers nil)))
(run-hook-with-args 'polymode-switch-buffer-hook src-buf dest-buf)
(pm--run-hooks pm/polymode :switch-buffer-functions src-buf dest-buf)
(pm--run-hooks pm/chunkmode :switch-buffer-functions src-buf dest-buf)))))
(add-function
:before-until (symbol-function 'pm-select-buffer)
(lambda (span &optional visibly)
(prog1 poly-ein-mode
(when poly-ein-mode
(let ((src-buf (current-buffer))
(dest-buf (pm-span-buffer span)))
;; (font-lock-flush)
(poly-ein--set-buffer src-buf dest-buf visibly))))))
(add-function
:override (symbol-function 'poly-lock-mode)
(symbol-function (default-value 'font-lock-function)))
(defun poly-ein--narrow-to-inner (modifier f &rest args)
(if (or pm-initialization-in-progress (not poly-ein-mode))
(apply f args)
(save-restriction
(widen)
(let ((range (pm-innermost-range
(or (when (car args) (funcall modifier (car args)))
(point)))))
(narrow-to-region (car range) (cdr range))
(apply f args)))))
(add-function
:before-until (symbol-function 'syntax-propertize)
(lambda (pos)
(prog1 poly-ein-mode
(when (and poly-ein-mode (< syntax-propertize--done pos))
(save-excursion
(with-silent-modifications
(let ((parse-sexp-lookup-properties t)
(start (point-min))
(end (point-max)))
;; (dolist (fun syntax-propertize-extend-region-functions)
;; (ein:and-let* ((new (funcall fun start end)))
;; (setq start (min start (car new)))
;; (setq end (max end (cdr new)))))
(setq syntax-propertize--done end)
(remove-text-properties start end
'(syntax-table nil syntax-multiline nil))
;; avoid recursion if syntax-propertize-function calls me (syntax-propertize)
(when syntax-propertize-function
(let ((syntax-propertize--done most-positive-fixnum))
(funcall syntax-propertize-function start end))))))))))
(add-function
:around (symbol-function 'syntax-propertize)
(apply-partially #'poly-ein--narrow-to-inner #'1-))
(add-function
:around (symbol-function 'syntax-ppss)
(apply-partially #'poly-ein--narrow-to-inner #'1-))
(add-function
:around (symbol-function 'jit-lock-mode)
(lambda (f &rest args)
(cl-letf (((symbol-function 'buffer-base-buffer) #'ignore)) (apply f args))))
(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))
(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)))
(append (list span-start span-end) (cddr args)))))
(defsubst poly-ein--unrelated-span (&optional beg end)
(or pm-initialization-in-progress
(and poly-ein-mode
(not (eq major-mode
(eieio-oref (nth 3 (pm-innermost-span (or beg (point)))) :mode))))))
;; :before-until before :filter-args (reversed order when executed)
(add-function :before-until (symbol-function 'jit-lock-refontify)
#'poly-ein--unrelated-span)
(add-function :before-until (symbol-function 'jit-lock-fontify-now)
#'poly-ein--unrelated-span)
(add-function :filter-args (symbol-function 'jit-lock-refontify)
#'poly-ein--span-start-end)
(add-function :filter-args (symbol-function 'jit-lock-fontify-now)
#'poly-ein--span-start-end)
(add-function :filter-args (symbol-function 'font-lock-flush)
#'poly-ein--span-start-end)
(add-function :filter-args (symbol-function 'jit-lock-after-change)
#'poly-ein--span-start-end)
(make-variable-buffer-local 'parse-sexp-lookup-properties)
(with-eval-after-load "markdown-mode"
(fset 'markdown-unfontify-region-wiki-links #'ignore))
(add-function :before-until
(symbol-function 'pm--synchronize-points)
(lambda (&rest args) poly-ein-mode))
(provide 'poly-ein)

View file

@ -51,7 +51,10 @@
(ein:events-new))
; matryoshka: new-content makes a ein:$content using CONTENT as template
; populating its raw_content field with DATA's content field
(ein:notebook-open--callback notebook nil nil (ein:new-content (ein:$notebook-url-or-port notebook) (ein:$notebook-notebook-path notebook) data))
(ein:notebook-open--callback
notebook nil nil
(ein:new-content (ein:$notebook-url-or-port notebook)
(ein:$notebook-notebook-path notebook) data))
(ein:notebook-buffer notebook)))))
(defun ein:testing-notebook-make-data (name path cells)

View file

@ -3,27 +3,6 @@
(require 'ein-core)
;;; `ein:version'
(ert-deftest ein:version ()
"Check if `ein:version' can be parsed by `version-to-list'."
(version-to-list ein:version))
(ert-deftest ein:version-func-prefix-is-the-variable ()
(should (string-prefix-p ein:version (ein:version)))
(let ((default-directory "/tmp/"))
(should (string-prefix-p ein:version (ein:version)))))
(ert-deftest ein:version-func-outside-of-git-repo ()
(flet ((ein:git-root-p (dir) nil))
(should (equal (ein:version) ein:version)))
(flet ((ein:git-revision-dirty () nil))
(should (equal (ein:version) ein:version))))
;; Generic getter
(defmacro eintest:generic-getter-should-return-nil (func)

View file

@ -17,6 +17,8 @@
(map-keymap assert-fboundp value))
((and (listp value) (eq (car value) 'menu-item))
(funcall assert-fboundp (cadr value) (caddr value)))
((consp value)
(should (functionp (cdr value))))
(value ; nil is also valid in keymap
(should (commandp value))))))
(map-keymap assert-fboundp keymap)))

View file

@ -1,7 +1,7 @@
(eval-when-compile (require 'cl))
(require 'ert)
(require 'ein) ; for `ein:version'
(require 'ein)
(require 'ein-utils)
(ert-deftest ein-url-simple ()

View file

@ -186,18 +186,19 @@ See the definition of `create-image' for how it works."
(should (search-forward-regexp "^Hello$" nil t))))))
(ert-deftest 14-notebook-execute-current-cell-question ()
:expected-result (if (eq system-type 'darwin) t :passed)
(let ((notebook (ein:testing-get-untitled0-or-create *ein:testing-port*)))
(lexical-let ((notebook (ein:testing-get-untitled0-or-create *ein:testing-port*)))
(ein:testing-wait-until
(lambda () (ein:aand (ein:$notebook-kernel notebook)
(ein:kernel-live-p it))))
(with-current-buffer (ein:notebook-buffer notebook)
(call-interactively #'ein:worksheet-insert-cell-below)
(insert "range?")
(let ((cell (call-interactively #'ein:worksheet-execute-cell)))
(ein:testing-wait-until (lambda () (and (not (oref cell :running))
(ein:$notebook-pager notebook)
(get-buffer (ein:$notebook-pager notebook))))))
(lexical-let ((cell (call-interactively #'ein:worksheet-execute-cell)))
(ein:testing-wait-until
(lambda ()
(and (not (oref cell :running))
(ein:$notebook-pager notebook)
(get-buffer (ein:$notebook-pager notebook))))))
(with-current-buffer (get-buffer (ein:$notebook-pager notebook))
(should (search-forward "Docstring:"))))))

1
test/test-poly.el Normal file
View file

@ -0,0 +1 @@
(setq ein:polymode t)

View file

@ -19,6 +19,8 @@
(setq ein:testing-dump-file-messages (concat default-directory "log/testfunc.messages"))
(setq ein:testing-dump-file-server (concat default-directory "log/testfunc.server"))
(setq ein:testing-dump-file-request (concat default-directory "log/testfunc.request"))
(with-eval-after-load "python"
(setq python-indent-guess-indent-offset-verbose nil))
(ein:dev-start-debug)
(ein:jupyter-server-start *ein:testing-jupyter-server-command* *ein:testing-jupyter-server-directory*)
(ein:testing-wait-until (lambda () (ein:notebooklist-list)) nil 15000 1000)

View file

@ -4,7 +4,10 @@
# Define sample multi-line literal.
input=`cat`
replace=$(awk '/key.*binding/,EOF { print " " $0 }' <<<"$input")
replace="$input"
if [ ! -z "$3" ]; then
replace=$(awk "/$3/,EOF { print \" \" \$0 }" <<<"$input")
fi
# Escape it for use as a Sed replacement string.
IFS= read -d '' -r < <(sed -e ':a' -e '$!{N;ba' -e '}' -e 's/[&/\]/\\&/g; s/\n/\\&/g' <<<"$replace")