mirror of
https://github.com/vale981/emacs-ipython-notebook
synced 2025-03-04 16:51:38 -05:00
commit
0ee5f01fe4
22 changed files with 265 additions and 809 deletions
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
|
@ -51,7 +51,7 @@ jobs:
|
|||
- uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/local
|
||||
key: ${{ runner.os }}-local
|
||||
key: ${{ runner.os }}-local-julia13
|
||||
|
||||
- uses: actions/cache@v1
|
||||
with:
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
Getting started
|
||||
---------------
|
||||
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.
|
||||
|
||||
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`.
|
||||
|
||||
In your `init.el` or `.emacs`, add the following:
|
||||
|
||||
```
|
||||
(add-to-list 'load-path "~/emacs-ipython-notebook/lisp")
|
||||
(load "ein-autoloads")
|
||||
```
|
||||
|
||||
Now whatever changes you make to the repo will be reflected in new emacs instances.
|
||||
|
||||
Dev tools
|
||||
---------
|
||||
`M-x ein:dev-start-debug` activates full logging and backtrace on error.
|
||||
|
||||
Quick sanity checking
|
||||
---------------------
|
||||
`make quick` runs a syntax check and the unit tests. It is far quicker than the laborious `make test`.
|
||||
|
||||
Unit tests
|
||||
----------
|
||||
Located in `~/emacs-ipython-notebook/test`.
|
||||
|
||||
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`.
|
||||
|
||||
To run say just the login tests, `cask exec ecukes --tags "@login"`.
|
10
Makefile
10
Makefile
|
@ -1,4 +1,5 @@
|
|||
EMACS ?= $(shell which emacs)
|
||||
export EMACS ?= $(shell which emacs)
|
||||
CASK_DIR := $(shell EMACS=$(EMACS) cask package-directory)
|
||||
SRC=$(shell cask files)
|
||||
PKBUILD=2.3
|
||||
ELCFILES = $(SRC:.el=.elc)
|
||||
|
@ -52,14 +53,17 @@ clean:
|
|||
dist-clean: clean
|
||||
rm -rf dist
|
||||
|
||||
.PHONY: cask
|
||||
cask: $(CASK_DIR)
|
||||
$(CASK_DIR): Cask
|
||||
cask install
|
||||
|
||||
.PHONY: test-compile
|
||||
test-compile: clean autoloads
|
||||
cask install
|
||||
! (cask eval "(let ((byte-compile-error-on-warn t)) (cask-cli/build))" 2>&1 | egrep -a "(Warning|Error):") ; (ret=$$? ; cask clean-elc && exit $$ret)
|
||||
|
||||
.PHONY: quick
|
||||
quick: test-compile test-ob-ein-recurse test-unit
|
||||
quick: cask test-compile test-ob-ein-recurse test-unit
|
||||
|
||||
.PHONY: test-jupyterhub
|
||||
test-jupyterhub: test-compile
|
||||
|
|
|
@ -41,11 +41,11 @@ Scenario: kernel reconnect succeeds
|
|||
And I switch to log expr "ein:log-all-buffer-name"
|
||||
Then I should see "WS closed unexpectedly"
|
||||
And I switch to buffer like "Untitled"
|
||||
And header says "Kernel requires reconnect \<ein:notebook-mode-map>\[ein:notebook-reconnect-session-command]"
|
||||
And header says "Kernel requires reconnect \<ein:notebook-mode-map>\[ein:notebook-reconnect-session-command-km]"
|
||||
And I clear log expr "ein:log-all-buffer-name"
|
||||
And I press "C-c C-r"
|
||||
And I wait for the smoke to clear
|
||||
And header does not say "Kernel requires reconnect \<ein:notebook-mode-map>\[ein:notebook-reconnect-session-command]"
|
||||
And header does not say "Kernel requires reconnect \<ein:notebook-mode-map>\[ein:notebook-reconnect-session-command-km]"
|
||||
And I switch to log expr "ein:log-all-buffer-name"
|
||||
Then I should not see "[warn]"
|
||||
And I should not see "[error]"
|
||||
|
@ -55,10 +55,10 @@ Scenario: kernel reconnect succeeds
|
|||
And I switch to log expr "ein:log-all-buffer-name"
|
||||
Then I should see "WS closed unexpectedly"
|
||||
And I switch to buffer like "Untitled"
|
||||
And header says "Kernel requires reconnect \<ein:notebook-mode-map>\[ein:notebook-reconnect-session-command]"
|
||||
And header says "Kernel requires reconnect \<ein:notebook-mode-map>\[ein:notebook-reconnect-session-command-km]"
|
||||
And I clear log expr "ein:log-all-buffer-name"
|
||||
And I wait for cell to execute
|
||||
And header does not say "Kernel requires reconnect \<ein:notebook-mode-map>\[ein:notebook-reconnect-session-command]"
|
||||
And header does not say "Kernel requires reconnect \<ein:notebook-mode-map>\[ein:notebook-reconnect-session-command-km]"
|
||||
And I switch to log expr "ein:log-all-buffer-name"
|
||||
Then I should not see "[warn]"
|
||||
And I should not see "[error]"
|
||||
|
@ -72,7 +72,7 @@ Scenario: kernel reconnect succeeds
|
|||
And I should see "ein:kernel-retrieve-session--complete"
|
||||
And I switch to buffer like "Untitled"
|
||||
And I kill kernel
|
||||
And header says "Kernel requires reconnect \<ein:notebook-mode-map>\[ein:notebook-reconnect-session-command]"
|
||||
And header says "Kernel requires reconnect \<ein:notebook-mode-map>\[ein:notebook-reconnect-session-command-km]"
|
||||
And I clear log expr "ein:log-all-buffer-name"
|
||||
And my reconnect is questioned
|
||||
|
||||
|
@ -98,7 +98,6 @@ Scenario: Assign variable, save, kill notebook buffer, get it back, check variab
|
|||
And I type "b"
|
||||
And I wait for cell to execute
|
||||
Then I should see "3.1415"
|
||||
And I press "C-x k"
|
||||
|
||||
@julia
|
||||
Scenario: Smoke test julia
|
||||
|
|
|
@ -14,6 +14,7 @@ Scenario: New Notebook
|
|||
Given I am in notebooklist buffer
|
||||
When I clear log expr "ein:log-all-buffer-name"
|
||||
And I click on "New Notebook"
|
||||
And no notebooks pending
|
||||
And I switch to log expr "ein:log-all-buffer-name"
|
||||
Then I should see "Opened notebook Untitled"
|
||||
|
||||
|
@ -28,9 +29,11 @@ Scenario: Resync
|
|||
Scenario: Stop after closing notebook
|
||||
Given I am in notebooklist buffer
|
||||
And I click on "New Notebook"
|
||||
And no notebooks pending
|
||||
And I switch to buffer like "Untitled"
|
||||
And I press "C-x k"
|
||||
And I am in notebooklist buffer
|
||||
And I clear log expr "ein:log-all-buffer-name"
|
||||
And I keep clicking "Resync" until "Stop"
|
||||
And I click on "Stop"
|
||||
And I switch to log expr "ein:log-all-buffer-name"
|
||||
|
@ -38,7 +41,10 @@ Scenario: Stop after closing notebook
|
|||
And I am in notebooklist buffer
|
||||
And I go to word "Untitled"
|
||||
And I go to beginning of line
|
||||
And I dump buffer
|
||||
And I click without going top on "Open"
|
||||
And no notebooks pending
|
||||
And I switch to buffer like "Untitled"
|
||||
|
||||
@content
|
||||
Scenario: Read a massive directory
|
||||
|
|
|
@ -113,7 +113,7 @@ Scenario: Specific port, portless localhost refers to same, concurrent execution
|
|||
And I should not see "[....]"
|
||||
|
||||
@org
|
||||
Scenario: portless url with path, image, C-c ' lets you C-c C-c as well
|
||||
Scenario: portless url with path, image
|
||||
When I open temp file "path.org"
|
||||
And I call "org-mode"
|
||||
And I type "<s"
|
||||
|
@ -134,18 +134,8 @@ Scenario: portless url with path, image, C-c ' lets you C-c C-c as well
|
|||
And I press "RET"
|
||||
And I type "import matplotlib.pyplot as plt ; import numpy as np ; x = np.linspace(0, 1, 100) ; y = np.random.rand(100,1) ; plt.plot(x,y)"
|
||||
And I ctrl-c-ctrl-c
|
||||
And I press "M->"
|
||||
And I type "<s"
|
||||
And I press "TAB"
|
||||
And I type "ein :session localhost :results raw drawer"
|
||||
And I press "RET"
|
||||
And I insert percent sign
|
||||
And I type "matplotlib inline"
|
||||
And I press "RET"
|
||||
And I type "import matplotlib.pyplot as plt ; import numpy as np ; x = np.linspace(0, 1, 100) ; y = np.random.rand(100,1) ; plt.plot(x,y)"
|
||||
And I ctrl-c-ctrl-c
|
||||
And I dump buffer
|
||||
And I wait for buffer to say "file:ein-image"
|
||||
And I dump buffer
|
||||
|
||||
@export
|
||||
Scenario: Test ob-exp captures code and results.
|
||||
|
|
|
@ -115,9 +115,23 @@
|
|||
(lambda (log-expr)
|
||||
(switch-to-buffer (symbol-value (intern log-expr)))))
|
||||
|
||||
(When "^no notebooks pending$"
|
||||
(lambda ()
|
||||
(cl-loop repeat 10
|
||||
until (zerop (hash-table-count *ein:notebook--pending-query*))
|
||||
do (sleep-for 0 500)
|
||||
finally do (should (zerop (hash-table-count *ein:notebook--pending-query*))))))
|
||||
|
||||
(When "^I switch to buffer like \"\\(.+\\)\"$"
|
||||
(lambda (substr)
|
||||
(switch-to-buffer (car (-non-nil (mapcar (lambda (b) (if (search substr (buffer-name b)) b)) (buffer-list)))))))
|
||||
(cl-loop repeat 10
|
||||
for buf = (seq-some (lambda (b)
|
||||
(and (search substr (buffer-name b)) b))
|
||||
(buffer-list))
|
||||
until (buffer-live-p buf)
|
||||
do (sleep-for 0 500)
|
||||
finally do (and (should (buffer-live-p buf))
|
||||
(switch-to-buffer buf)))))
|
||||
|
||||
(When "^rename notebook to \"\\(.+\\)\" succeeds$"
|
||||
(lambda (new-name)
|
||||
|
@ -293,7 +307,7 @@
|
|||
(When "^I click\\( without going top\\)? on \"\\(.+\\)\"$"
|
||||
(lambda (stay word)
|
||||
;; from espuds "go to word" without the '\\b's
|
||||
(when (not stay)
|
||||
(unless stay
|
||||
(goto-char (point-min)))
|
||||
(let ((search (re-search-forward (format "\\[%s\\]" word) nil t))
|
||||
(msg "Cannot go to link '%s' in buffer: %s"))
|
||||
|
@ -302,7 +316,7 @@
|
|||
(let ((was (widget-at)))
|
||||
(When "I press \"RET\"")
|
||||
(cl-loop until (not (equal was (widget-at)))
|
||||
do (sleep-for 0 500))))))
|
||||
do (sleep-for 0 500))))))
|
||||
|
||||
(When "^I click on dir \"\\(.+\\)\"$"
|
||||
(lambda (dir)
|
||||
|
|
|
@ -40,29 +40,34 @@
|
|||
(ein:testing-flush-queries)
|
||||
(with-current-buffer (ein:notebooklist-get-buffer (car (ein:jupyter-server-conn-info)))
|
||||
(cl-loop for notebook in (ein:notebook-opened-notebooks)
|
||||
for path = (ein:$notebook-notebook-path notebook)
|
||||
do (ein:notebook-kill-kernel-then-close-command notebook)
|
||||
do (cl-loop repeat 16
|
||||
until (not (ein:notebook-live-p notebook))
|
||||
do (sleep-for 0 1000)
|
||||
finally do (when (ein:notebook-live-p notebook)
|
||||
(ein:display-warning (format "cannot close %s" path))))
|
||||
do (when (or (ob-ein-anonymous-p path)
|
||||
(search "Untitled" path)
|
||||
(search "Renamed" path))
|
||||
(ein:notebooklist-delete-notebook path)
|
||||
(cl-loop repeat 16
|
||||
with fullpath = (concat (file-name-as-directory ein:testing-jupyter-server-root) path)
|
||||
for extant = (file-exists-p fullpath)
|
||||
until (not extant)
|
||||
do (sleep-for 0 1000)
|
||||
finally do (when extant
|
||||
(ein:display-warning (format "cannot del %s" path)))))))
|
||||
for path = (ein:$notebook-notebook-path notebook)
|
||||
for done-p = nil
|
||||
do (ein:notebook-kill-kernel-then-close-command
|
||||
notebook (lambda (_kernel) (setq done-p t)))
|
||||
do (cl-loop repeat 16
|
||||
until done-p
|
||||
do (sleep-for 0 1000)
|
||||
finally do (unless done-p
|
||||
(ein:display-warning (format "cannot close %s" path))))
|
||||
do (when (or (ob-ein-anonymous-p path)
|
||||
(search "Untitled" path)
|
||||
(search "Renamed" path))
|
||||
(ein:notebooklist-delete-notebook path)
|
||||
(cl-loop with fullpath = (concat (file-name-as-directory ein:testing-jupyter-server-root) path)
|
||||
repeat 10
|
||||
for extant = (file-exists-p fullpath)
|
||||
until (not extant)
|
||||
do (sleep-for 0 1000)
|
||||
finally do (when extant
|
||||
(ein:display-warning (format "cannot del %s" path)))))))
|
||||
(aif (ein:notebook-opened-notebooks)
|
||||
(cl-loop for nb in it
|
||||
for path = (ein:$notebook-notebook-path nb)
|
||||
do (ein:log 'debug "Notebook %s still open" path)
|
||||
finally do (assert nil))))
|
||||
finally do (assert nil)))
|
||||
(let ((stragglers (file-name-all-completions "Untitled"
|
||||
ein:testing-jupyter-server-root)))
|
||||
(should-not stragglers)))
|
||||
|
||||
(Setup
|
||||
(ein:dev-start-debug)
|
||||
|
|
|
@ -356,10 +356,10 @@ auto-execution mode flag in the connected buffer is `t'.")))
|
|||
(status_busy.Kernel . "Kernel is busy...")
|
||||
(status_restarting.Kernel . "Kernel restarting...")
|
||||
(status_restarted.Kernel . "Kernel restarted")
|
||||
(status_dead.Kernel . "Kernel requires restart \\<ein:notebook-mode-map>\\[ein:notebook-restart-session-command]")
|
||||
(status_dead.Kernel . "Kernel requires restart \\<ein:notebook-mode-map>\\[ein:notebook-restart-session-command-km]")
|
||||
(status_reconnecting.Kernel . "Kernel reconnecting...")
|
||||
(status_reconnected.Kernel . "Kernel reconnected")
|
||||
(status_disconnected.Kernel . "Kernel requires reconnect \\<ein:notebook-mode-map>\\[ein:notebook-reconnect-session-command]")))
|
||||
(status_disconnected.Kernel . "Kernel requires reconnect \\<ein:notebook-mode-map>\\[ein:notebook-reconnect-session-command-km]")))
|
||||
:type ein:notification-status))
|
||||
"Notification widget for Notebook.")
|
||||
|
||||
|
|
|
@ -184,21 +184,6 @@ callback (`websocket-callback-debug-on-error') is enabled."
|
|||
(websocket-get-debug-buffer-create
|
||||
(ein:$websocket-ws channel)))))
|
||||
|
||||
(defun ein:dev-notebook-plain-mode ()
|
||||
"Use `ein:notebook-plain-mode'."
|
||||
(interactive)
|
||||
(setq ein:notebook-modes '(ein:notebook-plain-mode)))
|
||||
|
||||
(defun ein:dev-notebook-python-mode ()
|
||||
"Use `ein:notebook-python-mode'."
|
||||
(interactive)
|
||||
(setq ein:notebook-modes '(ein:notebook-python-mode)))
|
||||
|
||||
(defun ein:dev-notebook-multilang-mode ()
|
||||
"Use `ein:notebook-multilang-mode'."
|
||||
(interactive)
|
||||
(setq ein:notebook-modes '(ein:notebook-multilang-mode)))
|
||||
|
||||
(defun ein:dev-sys-info--lib (name)
|
||||
(let* ((libsym (intern-soft name))
|
||||
(version-var (cl-loop for fmt in '("%s-version" "%s:version")
|
||||
|
|
|
@ -109,6 +109,16 @@ with the call to the jupyter notebook."
|
|||
(defvar *ein:jupyter-server-buffer-name*
|
||||
(format "*%s*" *ein:jupyter-server-process-name*))
|
||||
|
||||
(defun ein:jupyter-get-default-kernel (kernels)
|
||||
(cond (ein:%notebooklist-new-kernel%
|
||||
(ein:$kernelspec-name ein:%notebooklist-new-kernel%))
|
||||
((eq ein:jupyter-default-kernel 'first-alphabetically)
|
||||
(car (car kernels)))
|
||||
((stringp ein:jupyter-default-kernel)
|
||||
ein:jupyter-default-kernel)
|
||||
(t
|
||||
(symbol-name ein:jupyter-default-kernel))))
|
||||
|
||||
(defun ein:jupyter-process-lines (url-or-port command &rest args)
|
||||
"If URL-OR-PORT registered as a k8s url, preface COMMAND ARGS with `kubectl exec'."
|
||||
(condition-case err
|
||||
|
@ -197,6 +207,49 @@ our singleton jupyter server process here."
|
|||
(funcall #'ein:notebooklist-sentinel url-or-port* proc* event))
|
||||
url-or-port (process-sentinel proc))))
|
||||
|
||||
(defun ein:jupyter-crib-token (url-or-port)
|
||||
"Shell out to jupyter for its credentials knowledge. Return list of (PASSWORD TOKEN)."
|
||||
(aif (cl-loop for line in
|
||||
(apply #'ein:jupyter-process-lines url-or-port
|
||||
ein:jupyter-server-command
|
||||
(split-string
|
||||
(format "%s%s %s"
|
||||
(aif ein:jupyter-server-use-subcommand
|
||||
(concat it " ") "")
|
||||
"list" "--json")))
|
||||
with token0
|
||||
with password0
|
||||
when (destructuring-bind
|
||||
(&key password url token &allow-other-keys)
|
||||
(ein:json-read-from-string line)
|
||||
(prog1 (or (equal (ein:url url) url-or-port)
|
||||
(equal (url-host (url-generic-parse-url url))
|
||||
"0.0.0.0"))
|
||||
(setq password0 password) ;; t or :json-false
|
||||
(setq token0 token)))
|
||||
return (list password0 token0))
|
||||
it (list nil nil)))
|
||||
|
||||
(defun ein:jupyter-crib-running-servers ()
|
||||
"Shell out to jupyter for running servers."
|
||||
(nconc
|
||||
(cl-loop for line in
|
||||
(apply #'ein:jupyter-process-lines nil
|
||||
ein:jupyter-server-command
|
||||
(split-string
|
||||
(format "%s%s %s"
|
||||
(aif ein:jupyter-server-use-subcommand
|
||||
(concat it " ") "")
|
||||
"list" "--json")))
|
||||
collecting (destructuring-bind
|
||||
(&key url &allow-other-keys)
|
||||
(ein:json-read-from-string line)
|
||||
(ein:url url)))
|
||||
(aif (ein:k8s-service-url-or-port) (list it))))
|
||||
|
||||
|
||||
|
||||
|
||||
;;;###autoload
|
||||
(defun ein:jupyter-server-start (server-command
|
||||
notebook-directory
|
||||
|
@ -277,14 +330,6 @@ server command."
|
|||
;;;###autoload
|
||||
(defalias 'ein:stop 'ein:jupyter-server-stop)
|
||||
|
||||
(defun ein:undocumented-shutdown (url-or-port)
|
||||
(ein:query-singleton-ajax
|
||||
(list 'shutdown-server url-or-port)
|
||||
(ein:url url-or-port "api/shutdown")
|
||||
:type "POST"
|
||||
:timeout 3 ;; content-query-timeout and query-timeout default nil
|
||||
:sync t))
|
||||
|
||||
;;;###autoload
|
||||
(defun ein:jupyter-server-stop (&optional force log)
|
||||
(interactive)
|
||||
|
|
121
lisp/ein-k8s.el
121
lisp/ein-k8s.el
|
@ -35,57 +35,58 @@
|
|||
:type 'string
|
||||
:group 'ein)
|
||||
|
||||
(defun ein:k8s-select-context ()
|
||||
(interactive)
|
||||
(kubernetes-contexts-refresh-now)
|
||||
(if-let ((contexts (ein:k8s-get-contexts)))
|
||||
(let ((desired-context
|
||||
(ein:completing-read "Select context: " contexts nil t)))
|
||||
(kubernetes-state-clear)
|
||||
(let ((response (kubernetes-kubectl-await
|
||||
(apply-partially #'kubernetes-kubectl
|
||||
kubernetes-props
|
||||
(kubernetes-state)
|
||||
(split-string (format "config use-context %s"
|
||||
desired-context)))
|
||||
(lambda (buf)
|
||||
(with-current-buffer buf
|
||||
(string-match (rx bol "Switched to context \""
|
||||
(group (+? nonl)) "\"." (* space) eol)
|
||||
(buffer-string))
|
||||
(match-string 1 (buffer-string)))))))
|
||||
(if (string= response desired-context)
|
||||
(progn
|
||||
(kubernetes-state-update-config (kubernetes-kubectl-await-on-async
|
||||
kubernetes-props
|
||||
(kubernetes-state)
|
||||
#'kubernetes-kubectl-config-view))
|
||||
(let ((current-name (alist-get
|
||||
'name
|
||||
(kubernetes-state-current-context
|
||||
(kubernetes-state)))))
|
||||
(unless (string= current-name desired-context)
|
||||
(error "ein:k8s-select-context': could not update state for %s"
|
||||
desired-context))
|
||||
(if (kubernetes-kubectl-await
|
||||
(apply-partially #'kubernetes-kubectl
|
||||
kubernetes-props
|
||||
(kubernetes-state)
|
||||
(split-string "get nodes -o json"))
|
||||
(lambda (buf)
|
||||
(prog1 t
|
||||
(with-current-buffer buf
|
||||
(kubernetes-state-update-nodes
|
||||
(json-read-from-string (buffer-string))))))
|
||||
nil #'ignore)
|
||||
(message "Selected %s" current-name)
|
||||
(error "ein:k8s-select-context: %s is down" current-name))))
|
||||
(error "ein:k8s-select-context: use-context returned %s, expected %s"
|
||||
response desired-context))))
|
||||
(error "ein:k8s-select-context: No contexts found")))
|
||||
(defun ein:k8s-select-context (&optional query-p)
|
||||
(interactive "p")
|
||||
(when (or query-p
|
||||
(null (kubernetes-state-config (kubernetes-state))))
|
||||
(kubernetes-contexts-refresh-now)
|
||||
(if-let ((contexts (ein:k8s-get-contexts)))
|
||||
(let ((desired-context
|
||||
(ein:completing-read "Select context: " contexts nil t)))
|
||||
(message "Rereading state...")
|
||||
(kubernetes-state-clear)
|
||||
(let ((response
|
||||
(kubernetes-kubectl-await
|
||||
(apply-partially #'kubernetes-kubectl
|
||||
kubernetes-props
|
||||
(kubernetes-state)
|
||||
(split-string (format "config use-context %s"
|
||||
desired-context)))
|
||||
(lambda (buf)
|
||||
(with-current-buffer buf
|
||||
(string-match (rx bol "Switched to context \""
|
||||
(group (+? nonl)) "\"." (* space) eol)
|
||||
(buffer-string))
|
||||
(match-string 1 (buffer-string)))))))
|
||||
(if (string= response desired-context)
|
||||
(progn
|
||||
(kubernetes-state-update-config (kubernetes-kubectl-await-on-async
|
||||
kubernetes-props
|
||||
(kubernetes-state)
|
||||
#'kubernetes-kubectl-config-view))
|
||||
(let ((current-name (alist-get
|
||||
'name
|
||||
(kubernetes-state-current-context
|
||||
(kubernetes-state)))))
|
||||
(unless (string= current-name desired-context)
|
||||
(error "ein:k8s-select-context': could not update state for %s"
|
||||
desired-context))
|
||||
(if (kubernetes-nodes-refresh-now)
|
||||
(progn
|
||||
(mapc (lambda (resource)
|
||||
(when-let ((refresh-f
|
||||
(intern-soft (format "kubernetes-%s-refresh-now" resource))))
|
||||
(when (fboundp refresh-f)
|
||||
(funcall refresh-f))))
|
||||
(cl-remove-if (apply-partially #'eq 'nodes)
|
||||
(mapcar #'car kubernetes-overview-views-alist)))
|
||||
(message ""))
|
||||
(error "ein:k8s-select-context: %s is down" current-name))))
|
||||
(error "ein:k8s-select-context: use-context returned %s, expected %s"
|
||||
response desired-context))))
|
||||
(error "ein:k8s-select-context: No contexts found"))))
|
||||
|
||||
(defun ein:k8s-get-contexts ()
|
||||
(kubernetes-contexts-refresh-now)
|
||||
(let ((response (kubernetes-kubectl-await-on-async kubernetes-props
|
||||
(kubernetes-state)
|
||||
#'kubernetes-kubectl-config-view)))
|
||||
|
@ -96,7 +97,6 @@
|
|||
names)))
|
||||
|
||||
(defun ein:k8s-get-deployment ()
|
||||
(kubernetes-deployments-refresh-now)
|
||||
(-let* [(deployments (kubernetes-state-deployments (kubernetes-state)))
|
||||
((&alist 'items items) deployments)]
|
||||
(seq-some (lambda (it)
|
||||
|
@ -105,12 +105,10 @@
|
|||
items)))
|
||||
|
||||
(defun ein:k8s-get-pod ()
|
||||
(kubernetes-pods-refresh-now)
|
||||
(when-let ((deployment (ein:k8s-get-deployment)))
|
||||
(cl-first (kubernetes-overview--pods-for-deployment (kubernetes-state)
|
||||
deployment))))
|
||||
(defun ein:k8s-get-service ()
|
||||
(kubernetes-services-refresh-now)
|
||||
(-let* [(services (kubernetes-state-services (kubernetes-state)))
|
||||
((&alist 'items items) services)]
|
||||
(seq-some (lambda (it)
|
||||
|
@ -119,21 +117,20 @@
|
|||
items)))
|
||||
|
||||
(defun ein:k8s-get-node ()
|
||||
(kubernetes-nodes-refresh-now)
|
||||
(-when-let* ((pod (ein:k8s-get-pod))
|
||||
((&alist 'spec (&alist 'nodeName)) pod)
|
||||
(node (kubernetes-state-lookup-node nodeName (kubernetes-state)))
|
||||
((&alist 'metadata (&alist 'name)) node))
|
||||
node))
|
||||
|
||||
(defsubst ein:k8s-p ()
|
||||
(defsubst ein:k8s-ensure ()
|
||||
(and (executable-find kubernetes-kubectl-executable)
|
||||
(or (kubernetes-state-current-context (kubernetes-state))
|
||||
(unless noninteractive
|
||||
(condition-case err
|
||||
(ein:k8s-select-context)
|
||||
(error (ein:log 'info "ein:k8s-p %s" (error-message-string err))
|
||||
nil))))))
|
||||
(condition-case err
|
||||
(progn
|
||||
(ein:k8s-select-context)
|
||||
(kubernetes-state-current-context (kubernetes-state)))
|
||||
(error (ein:log 'info "ein:k8s-ensure: %s" (error-message-string err))
|
||||
nil))))
|
||||
|
||||
(defsubst ein:k8s-in-cluster (addr)
|
||||
"Is ein client inside the k8s cluster?"
|
||||
|
@ -149,8 +146,8 @@
|
|||
(kubernetes-state))))))
|
||||
|
||||
(defun ein:k8s-service-url-or-port ()
|
||||
(-when-let* ((k8s-p (ein:k8s-p))
|
||||
(service (ein:k8s-get-service))
|
||||
(ein:k8s-ensure)
|
||||
(-when-let* ((service (ein:k8s-get-service))
|
||||
((&alist 'spec (&alist 'ports [(&alist 'nodePort)])) service)
|
||||
(node (ein:k8s-get-node))
|
||||
((&alist 'status (&alist 'addresses)) node)
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
;;; ein-multilang-fontify.el --- Syntax highlighting for multiple-languages
|
||||
|
||||
;; Copyright (C) 2012 Takafumi Arakaki
|
||||
|
||||
;; Author: Takafumi Arakaki <aka.tkf at gmail.com>
|
||||
|
||||
;; This file is NOT part of GNU Emacs.
|
||||
|
||||
;; ein-multilang-fontify.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-multilang-fontify.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-multilang-fontify.el.
|
||||
;; If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;;
|
||||
|
||||
;;; Code:
|
||||
|
||||
;; It would be nice if org-src is available, but this module should
|
||||
;; work without org-src. Data on `org-src-lang-modes' is used
|
||||
;; if this variable is bound.
|
||||
(require 'org-src nil t)
|
||||
|
||||
(defun ein:mlf-get-lang-mode (lang)
|
||||
"Return major mode for LANG.
|
||||
Modified version of `org-src-get-lang-mode'."
|
||||
(when (symbolp lang)
|
||||
(setq lang (symbol-name lang)))
|
||||
(intern
|
||||
(format "%s-mode"
|
||||
(or (and (bound-and-true-p org-src-lang-modes)
|
||||
(cdr (assoc lang org-src-lang-modes)))
|
||||
lang))))
|
||||
|
||||
(defun ein:mlf-font-lock-fontify-block (lang start end)
|
||||
"Patched version of `org-src-font-lock-fontify-block'."
|
||||
(let ((lang-mode (ein:mlf-get-lang-mode lang)))
|
||||
(if (fboundp lang-mode)
|
||||
(let ((string (buffer-substring-no-properties start end))
|
||||
(modified (buffer-modified-p))
|
||||
(orig-buffer (current-buffer))
|
||||
pos
|
||||
next)
|
||||
(remove-text-properties start end '(face nil))
|
||||
(with-current-buffer
|
||||
(get-buffer-create
|
||||
(concat " ein:mlf-fontification:" (symbol-name lang-mode)))
|
||||
(delete-region (point-min) (point-max))
|
||||
(insert string)
|
||||
(unless (eq major-mode lang-mode) (funcall lang-mode))
|
||||
(font-lock-ensure)
|
||||
(setq pos (point-min))
|
||||
(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))
|
||||
(set-buffer-modified-p modified)))))
|
||||
|
||||
(provide 'ein-multilang-fontify)
|
||||
|
||||
;;; ein-multilang-fontify.el ends here
|
|
@ -1,354 +0,0 @@
|
|||
;;; ein-multilang.el --- Notebook mode with multiple language fontification
|
||||
|
||||
;; Copyright (C) 2012 Takafumi Arakaki
|
||||
|
||||
;; Author: Takafumi Arakaki <aka.tkf at gmail.com>
|
||||
|
||||
;; This file is NOT part of GNU Emacs.
|
||||
|
||||
;; ein-multilang.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-multilang.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-multilang.el.
|
||||
;; If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;;
|
||||
|
||||
;;; Code:
|
||||
|
||||
(eval-when-compile (defvar markdown-mode-map))
|
||||
|
||||
(require 'ein-worksheet)
|
||||
(require 'ein-multilang-fontify)
|
||||
(require 'python)
|
||||
|
||||
(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 julia-indent-line "julia-mode")
|
||||
|
||||
(defun ein:ml-fontify (limit)
|
||||
"Fontify next input area comes after the current point then
|
||||
return `t' or `nil' if not found.
|
||||
See info node `(elisp) Search-based Fontification'."
|
||||
(ein:log-ignore-errors
|
||||
(ein:ml-fontify-1 limit)))
|
||||
|
||||
(defun ein:ml-current-or-next-input-cell (ewoc-node)
|
||||
"Almost identical to `ein:worksheet-next-input-cell' but return
|
||||
the current cell if EWOC-NODE is the input area node."
|
||||
(let* ((ewoc-data (ewoc-data ewoc-node))
|
||||
(cell (ein:$node-data ewoc-data))
|
||||
(path (ein:$node-path ewoc-data))
|
||||
(element (nth 1 path)))
|
||||
(if (memql element '(prompt input))
|
||||
cell
|
||||
(ein:cell-next cell))))
|
||||
|
||||
(defun ein:ml-fontify-1 (limit)
|
||||
"Actual implementation of `ein:ml-fontify'.
|
||||
This function may raise an error."
|
||||
(ein:and-let* ((pos (point))
|
||||
(node (ein:worksheet-get-nearest-cell-ewoc-node pos limit))
|
||||
(cell (ein:ml-current-or-next-input-cell node))
|
||||
(start (ein:cell-input-pos-min cell))
|
||||
(end (ein:cell-input-pos-max cell))
|
||||
((<= end limit))
|
||||
((< start end))
|
||||
(lang (ein:cell-language cell)))
|
||||
(let ((inhibit-read-only t))
|
||||
(ein:mlf-font-lock-fontify-block lang start end)
|
||||
;; Emacs fontification mechanism requires the function to move
|
||||
;; the point. Do *not* use `(goto-char end)'. As END is in the
|
||||
;; input area, fontification falls into an infinite loop.
|
||||
(ewoc-goto-node (slot-value cell 'ewoc) (ein:cell-element-get cell :footer)))
|
||||
t))
|
||||
|
||||
(defun ein:ml-back-to-prev-node ()
|
||||
(ein:aand (ein:worksheet-get-ewoc) (ewoc-goto-prev it 1)))
|
||||
|
||||
(defvar ein:ml-font-lock-keywords
|
||||
'((ein:ml-fontify))
|
||||
"Default `font-lock-keywords' for `ein:notebook-multilang-mode'.")
|
||||
|
||||
(defun ein:ml-set-font-lock-defaults ()
|
||||
(setq-local font-lock-defaults
|
||||
'(ein:ml-font-lock-keywords
|
||||
;; The following are adapted from org-mode but I am not sure
|
||||
;; if I need them:
|
||||
t nil nil
|
||||
ein:ml-back-to-prev-node)))
|
||||
|
||||
;;;###autoload
|
||||
(define-derived-mode ein:notebook-multilang-mode prog-mode "EIN"
|
||||
"A mode for fontifying multiple languages.
|
||||
|
||||
\\{ein:notebook-multilang-mode-map}
|
||||
"
|
||||
(setq-local beginning-of-defun-function
|
||||
'ein:worksheet-beginning-of-cell-input)
|
||||
(setq-local end-of-defun-function
|
||||
'ein:worksheet-end-of-cell-input)
|
||||
(ein:ml-set-font-lock-defaults))
|
||||
|
||||
;;; Language setup functions
|
||||
|
||||
(defun ein:ml-narrow-to-cell ()
|
||||
"Narrow to the current cell."
|
||||
(ein:and-let* ((pos (point))
|
||||
(node (ein:worksheet-get-nearest-cell-ewoc-node pos))
|
||||
(cell (ein:ml-current-or-next-input-cell node))
|
||||
(start (ein:cell-input-pos-min cell))
|
||||
(end (ein:cell-input-pos-max cell))
|
||||
((< start end)))
|
||||
(narrow-to-region start end)))
|
||||
|
||||
(defun ein:ml-indent-line-function (lang-func)
|
||||
(save-restriction
|
||||
(ein:ml-narrow-to-cell)
|
||||
(funcall lang-func)))
|
||||
|
||||
(defun ein:ml-indent-region (lang-func start end)
|
||||
(save-restriction
|
||||
(ein:ml-narrow-to-cell)
|
||||
(funcall lang-func start end)))
|
||||
|
||||
(defun ein:ml-lang-setup-python ()
|
||||
"Presumably tkf had good reasons to choose only these forms from `python-mode'."
|
||||
(setq-local mode-name "EIN[Py]")
|
||||
(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 #'python-indent-line-function))
|
||||
(setq-local indent-region-function
|
||||
(apply-partially #'ein:ml-indent-region #'python-indent-region))
|
||||
(set-syntax-table python-mode-syntax-table)
|
||||
(set-keymap-parent ein:notebook-multilang-mode-map python-mode-map))
|
||||
|
||||
(defun ein:ml-lang-setup-julia ()
|
||||
(require 'julia-mode nil t)
|
||||
(when (featurep 'julia-mode)
|
||||
(setq-local mode-name "EIN[julia]")
|
||||
(setq-local comment-start "# ")
|
||||
(setq-local comment-start-skip "#+\\s-*")
|
||||
(setq-local indent-line-function
|
||||
(apply-partially #'ein:ml-indent-line-function #'julia-indent-line))
|
||||
(when (boundp 'julia-mode-syntax-table)
|
||||
(set-syntax-table julia-mode-syntax-table))
|
||||
(when (boundp 'julia-mode-map)
|
||||
(set-keymap-parent ein:notebook-multilang-mode-map julia-mode-map))))
|
||||
|
||||
(defun ein:ml-lang-setup-R ()
|
||||
(require 'ess-r-mode nil t)
|
||||
(require 'ess-custom nil t)
|
||||
(when (and (featurep 'ess-r-mode) (featurep 'ess-custom))
|
||||
(setq-local mode-name "EIN[R]")
|
||||
(when (boundp 'ess-r-customize-alist)
|
||||
(ess-setq-vars-local ess-r-customize-alist))
|
||||
(setq-local paragraph-start (concat "\\s-*$\\|" page-delimiter))
|
||||
(setq-local paragraph-separate (concat "\\s-*$\\|" page-delimiter))
|
||||
(setq-local paragraph-ignore-fill-prefix t)
|
||||
(setq-local indent-line-function
|
||||
(apply-partially #'ein:ml-indent-line-function #'ess-indent-line))
|
||||
(when (and (boundp 'ess-style) (boundp 'ess-default-style))
|
||||
(setq-local ess-style ess-default-style))
|
||||
(when (and (boundp 'prettify-symbols-alist) (boundp 'ess-r-prettify-symbols))
|
||||
(setq-local prettify-symbols-alist ess-r-prettify-symbols))
|
||||
(when (boundp 'ess-r-mode-syntax-table)
|
||||
(set-syntax-table ess-r-mode-syntax-table))
|
||||
(when (boundp 'ess-r-mode-map)
|
||||
(set-keymap-parent ein:notebook-multilang-mode-map ess-r-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)))))
|
||||
|
||||
;; (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)))
|
||||
|
||||
;;; yasnippet
|
||||
|
||||
(defvar ein:ml-yasnippet-parents '(python-mode markdown-mode)
|
||||
"Parent modes for `ein:notebook-multilang-mode' to register in yasnippet.")
|
||||
|
||||
(defun ein:ml-setup-yasnippet ()
|
||||
(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))
|
||||
|
||||
;;; Imenu Support
|
||||
|
||||
;; Most of this is borrowed from python.el
|
||||
;; Just replace python with ein in most cases.
|
||||
|
||||
(defvar ein:imenu-format-item-label-function
|
||||
'ein:imenu-format-item-label
|
||||
"Imenu function used to format an item label.
|
||||
It must be a function with two arguments: TYPE and NAME.")
|
||||
|
||||
(defvar ein:imenu-format-parent-item-label-function
|
||||
'ein:imenu-format-parent-item-label
|
||||
"Imenu function used to format a parent item label.
|
||||
It must be a function with two arguments: TYPE and NAME.")
|
||||
|
||||
(defvar ein:imenu-format-parent-item-jump-label-function
|
||||
'ein:imenu-format-parent-item-jump-label
|
||||
"Imenu function used to format a parent jump item label.
|
||||
It must be a function with two arguments: TYPE and NAME.")
|
||||
|
||||
(defun ein:imenu-format-item-label (type name)
|
||||
"Return Imenu label for single node using TYPE and NAME."
|
||||
(format "%s (%s)" name type))
|
||||
|
||||
(defun ein:imenu-format-parent-item-label (type name)
|
||||
"Return Imenu label for parent node using TYPE and NAME."
|
||||
(format "%s..." (ein:imenu-format-item-label type name)))
|
||||
|
||||
(defun python-imenu-format-parent-item-jump-label (type _name)
|
||||
"Return Imenu label for parent node jump using TYPE and NAME."
|
||||
(if (string= type "class")
|
||||
"*class definition*"
|
||||
"*function definition*"))
|
||||
|
||||
(defun ein:imenu--put-parent (type name pos tree)
|
||||
"Add the parent with TYPE, NAME and POS to TREE."
|
||||
(let ((label
|
||||
(funcall ein:imenu-format-item-label-function type name))
|
||||
(jump-label
|
||||
(funcall ein:imenu-format-parent-item-jump-label-function type name)))
|
||||
(if (not tree)
|
||||
(cons label pos)
|
||||
(cons label (cons (cons jump-label pos) tree)))))
|
||||
|
||||
(defun ein:imenu--build-tree (&optional min-indent prev-indent tree)
|
||||
"Recursively build the tree of nested definitions of a node.
|
||||
Arguments MIN-INDENT, PREV-INDENT and TREE are internal and should
|
||||
not be passed explicitly unless you know what you are doing."
|
||||
(setq min-indent (or min-indent 0)
|
||||
prev-indent (or prev-indent python-indent-offset))
|
||||
(let* ((pos (python-nav-backward-defun))
|
||||
(type)
|
||||
(name (when (and pos (looking-at python-nav-beginning-of-defun-regexp))
|
||||
(let ((split (split-string (match-string-no-properties 0))))
|
||||
(setq type (car split))
|
||||
(cadr split))))
|
||||
(label (when name
|
||||
(funcall ein:imenu-format-item-label-function type name)))
|
||||
(indent (current-indentation))
|
||||
(children-indent-limit (+ python-indent-offset min-indent)))
|
||||
(cond ((not pos)
|
||||
;; Nothing found, probably near to bobp.
|
||||
nil)
|
||||
((<= indent min-indent)
|
||||
;; The current indentation points that this is a parent
|
||||
;; node, add it to the tree and stop recursing.
|
||||
(ein:imenu--put-parent type name pos tree))
|
||||
(t
|
||||
(ein:imenu--build-tree
|
||||
min-indent
|
||||
indent
|
||||
(if (<= indent children-indent-limit)
|
||||
;; This lies within the children indent offset range,
|
||||
;; so it's a normal child of its parent (i.e., not
|
||||
;; a child of a child).
|
||||
(cons (cons label pos) tree)
|
||||
;; Oh no, a child of a child?! Fear not, we
|
||||
;; know how to roll. We recursively parse these by
|
||||
;; swapping prev-indent and min-indent plus adding this
|
||||
;; newly found item to a fresh subtree. This works, I
|
||||
;; promise.
|
||||
(cons
|
||||
(ein:imenu--build-tree
|
||||
prev-indent indent (list (cons label pos)))
|
||||
tree)))))))
|
||||
|
||||
(defun ein:imenu-create-index ()
|
||||
"Return tree Imenu alist for the current Python buffer.
|
||||
Change `ein:imenu-format-item-label-function',
|
||||
`ein:imenu-format-parent-item-label-function',
|
||||
`ein:imenu-format-parent-item-jump-label-function' to
|
||||
customize how labels are formatted."
|
||||
(goto-char (point-max))
|
||||
(let ((index)
|
||||
(tree))
|
||||
(while (setq tree (ein:imenu--build-tree))
|
||||
(setq index (cons tree index)))
|
||||
index))
|
||||
|
||||
(defun ein:imenu-create-flat-index (&optional alist prefix)
|
||||
"Return flat outline of the current Python buffer for Imenu.
|
||||
Optional argument ALIST is the tree to be flattened; when nil
|
||||
`ein:imenu-build-index' is used with
|
||||
`ein:imenu-format-parent-item-jump-label-function'
|
||||
`ein:imenu-format-parent-item-label-function'
|
||||
`ein:imenu-format-item-label-function' set to
|
||||
(lambda (type name) name)
|
||||
Optional argument PREFIX is used in recursive calls and should
|
||||
not be passed explicitly.
|
||||
|
||||
Converts this:
|
||||
|
||||
((\"Foo\" . 103)
|
||||
(\"Bar\" . 138)
|
||||
(\"decorator\"
|
||||
(\"decorator\" . 173)
|
||||
(\"wrap\"
|
||||
(\"wrap\" . 353)
|
||||
(\"wrapped_f\" . 393))))
|
||||
|
||||
To this:
|
||||
|
||||
((\"Foo\" . 103)
|
||||
(\"Bar\" . 138)
|
||||
(\"decorator\" . 173)
|
||||
(\"decorator.wrap\" . 353)
|
||||
(\"decorator.wrapped_f\" . 393))"
|
||||
;; Inspired by imenu--flatten-index-alist removed in revno 21853.
|
||||
(apply
|
||||
'nconc
|
||||
(mapcar
|
||||
(lambda (item)
|
||||
(let ((name (if prefix
|
||||
(concat prefix "." (car item))
|
||||
(car item)))
|
||||
(pos (cdr item)))
|
||||
(cond ((or (numberp pos) (markerp pos))
|
||||
(list (cons name pos)))
|
||||
((listp pos)
|
||||
(cons
|
||||
(cons name (cdar pos))
|
||||
(python-imenu-create-flat-index (cddr item) name))))))
|
||||
(or alist
|
||||
(let* ((fn (lambda (_type name) name))
|
||||
(ein:imenu-format-item-label-function fn)
|
||||
(ein:imenu-format-parent-item-label-function fn)
|
||||
(ein:imenu-format-parent-item-jump-label-function fn))
|
||||
(python-imenu-create-index))))))
|
||||
|
||||
|
||||
(provide 'ein-multilang)
|
||||
|
||||
;;; ein-multilang.el ends here
|
|
@ -61,7 +61,6 @@
|
|||
(require 'ein-traceback)
|
||||
(require 'ein-shared-output)
|
||||
(require 'ein-notebooklist)
|
||||
(require 'ein-multilang)
|
||||
(require 'ob-ein)
|
||||
(require 'poly-ein)
|
||||
|
||||
|
@ -874,21 +873,25 @@ NAME is any non-empty string that does not contain '/' or '\\'.
|
|||
(apply #'apply-partially callback cbargs)))
|
||||
(ein:notebook-ask-save notebook callback0)))
|
||||
|
||||
(defun ein:notebook-kill-kernel-then-close-command (notebook)
|
||||
(defun ein:notebook-kill-kernel-then-close-command (notebook &optional callback1)
|
||||
"Kill kernel and then kill notebook buffer.
|
||||
To close notebook without killing kernel, just close the buffer
|
||||
as usual."
|
||||
(interactive (list (ein:notebook--get-nb-or-error)))
|
||||
(let ((kernel (ein:$notebook-kernel notebook))
|
||||
(callback1 (apply-partially
|
||||
(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)
|
||||
(ein:kernel-delete-session kernel callback1))
|
||||
(funcall callback1 nil))))
|
||||
(declare (indent defun))
|
||||
(interactive (list (ein:notebook--get-nb-or-error) nil))
|
||||
(unless callback1 (setq callback1 #'ignore))
|
||||
(lexical-let ((callback1 callback1))
|
||||
(let ((kernel (ein:$notebook-kernel notebook))
|
||||
(callback (apply-partially
|
||||
(lambda (notebook* kernel*)
|
||||
(ein:notebook-close notebook*)
|
||||
(funcall callback1 kernel*))
|
||||
notebook)))
|
||||
(if (ein:kernel-live-p kernel)
|
||||
(ein:message-whir "Ending session"
|
||||
(add-function :before callback done-callback)
|
||||
(ein:kernel-delete-session kernel callback))
|
||||
(funcall callback nil)))))
|
||||
|
||||
(defun ein:fast-content-from-notebook (notebook)
|
||||
"Quickly generate a basic content structure from notebook. This
|
||||
|
@ -1234,27 +1237,12 @@ associated with current buffer (if any)."
|
|||
|
||||
|
||||
;;; Notebook mode
|
||||
|
||||
(defcustom ein:notebook-modes
|
||||
'(ein:notebook-multilang-mode)
|
||||
"Obsolete."
|
||||
:type '(repeat (choice (const :tag "Multi-lang" ein:notebook-multilang-mode)
|
||||
(const :tag "Only Python" ein:notebook-python-mode)
|
||||
(const :tag "Plain" ein:notebook-plain-mode)))
|
||||
:group 'ein)
|
||||
|
||||
|
||||
(defun ein:notebook-choose-mode ()
|
||||
"Return usable (defined) notebook mode."
|
||||
(autoload 'ein:notebook-multilang-mode "ein-multilang")
|
||||
(cl-loop for mode in ein:notebook-modes
|
||||
if (functionp mode)
|
||||
return mode))
|
||||
|
||||
(defvar ein:notebook-mode-map (make-sparse-keymap))
|
||||
|
||||
(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.
|
||||
"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.
|
||||
|
||||
Tried add-function: the &rest from :around is an emacs-25 compilation issue."
|
||||
(let ((km (intern (concat (symbol-name defn) "-km"))))
|
||||
|
@ -1557,15 +1545,6 @@ 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))))))
|
||||
(unless (funcall incompatible-func)
|
||||
(with-eval-after-load 'undo-tree
|
||||
(funcall incompatible-func))))
|
||||
|
||||
(provide 'ein-notebook)
|
||||
|
||||
;;; ein-notebook.el ends here
|
||||
|
|
|
@ -38,21 +38,17 @@
|
|||
(require 'dash)
|
||||
(require 'ido)
|
||||
|
||||
(autoload 'ein:jupyterhub-connect "ein-jupyterhub")
|
||||
(declare-function ein:jupyterhub-connect "ein-jupyterhub")
|
||||
(declare-function ein:jupyter-crib-token "ein-jupyter")
|
||||
(declare-function ein:jupyter-server-conn-info "ein-jupyter")
|
||||
(declare-function ein:jupyter-get-default-kernel "ein-jupyter")
|
||||
(declare-function ein:jupyter-crib-running-servers "ein-jupyter")
|
||||
|
||||
(defcustom ein:notebooklist-login-timeout (truncate (* 6.3 1000))
|
||||
"Timeout in milliseconds for logging into server"
|
||||
:group 'ein
|
||||
:type 'integer)
|
||||
|
||||
(defcustom ein:notebooklist-render-order
|
||||
'(render-header
|
||||
render-directory)
|
||||
"Order of notebook list sections.
|
||||
Must contain render-header, and render-directory."
|
||||
:group 'ein
|
||||
:type 'list)
|
||||
|
||||
(defcustom ein:notebooklist-first-open-hook nil
|
||||
"Hooks to run when the notebook list is opened at first time.
|
||||
|
||||
|
@ -113,7 +109,7 @@ 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)
|
||||
|
@ -168,54 +164,13 @@ This function adds NBLIST to `ein:notebooklist-map'."
|
|||
(get-buffer-create
|
||||
(format ein:notebooklist-buffer-name-template url-or-port)))
|
||||
|
||||
(defun ein:crib-token (url-or-port)
|
||||
"Shell out to jupyter for its credentials knowledge. Return list of (PASSWORD TOKEN)."
|
||||
(aif (cl-loop for line in
|
||||
(apply #'ein:jupyter-process-lines url-or-port
|
||||
ein:jupyter-server-command
|
||||
(split-string
|
||||
(format "%s%s %s"
|
||||
(aif ein:jupyter-server-use-subcommand
|
||||
(concat it " ") "")
|
||||
"list" "--json")))
|
||||
with token0
|
||||
with password0
|
||||
when (destructuring-bind
|
||||
(&key password url token &allow-other-keys)
|
||||
(ein:json-read-from-string line)
|
||||
(prog1 (or (equal (ein:url url) url-or-port)
|
||||
(equal (url-host (url-generic-parse-url url))
|
||||
"0.0.0.0"))
|
||||
(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."
|
||||
(nconc
|
||||
(cl-loop for line in
|
||||
(apply #'ein:jupyter-process-lines nil
|
||||
ein:jupyter-server-command
|
||||
(split-string
|
||||
(format "%s%s %s"
|
||||
(aif ein:jupyter-server-use-subcommand
|
||||
(concat it " ") "")
|
||||
"list" "--json")))
|
||||
collecting (destructuring-bind
|
||||
(&key url &allow-other-keys)
|
||||
(ein:json-read-from-string line)
|
||||
(ein:url url)))
|
||||
(aif (ein:k8s-service-url-or-port) (list it))))
|
||||
|
||||
(defun ein:notebooklist-token-or-password (url-or-port)
|
||||
"Return token or password for URL-OR-PORT.
|
||||
|
||||
Jupyter requires one or the other but not both.
|
||||
Return empty string token if all authentication disabled.
|
||||
Return nil if unclear what, if any, authentication applies."
|
||||
(multiple-value-bind (password-p token) (ein:crib-token url-or-port)
|
||||
(autoload 'ein:jupyter-server-conn-info "ein-jupyter")
|
||||
(multiple-value-bind (password-p token) (ein:jupyter-crib-token url-or-port)
|
||||
(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)
|
||||
|
@ -223,7 +178,6 @@ Return nil if unclear what, if any, authentication applies."
|
|||
(t nil)))))
|
||||
|
||||
(defun ein:notebooklist-ask-url-or-port ()
|
||||
(call-interactively #'ein:k8s-select-context)
|
||||
(let* ((default (ein:url (aif (ein:get-notebook)
|
||||
(ein:$notebook-url-or-port it)
|
||||
(aif ein:%notebooklist%
|
||||
|
@ -235,7 +189,7 @@ Return nil if unclear what, if any, authentication applies."
|
|||
(if (atom ein:url-or-port)
|
||||
(list ein:url-or-port)
|
||||
ein:url-or-port)
|
||||
(ein:crib-running-servers)))))
|
||||
(ein:jupyter-crib-running-servers)))))
|
||||
(url-or-port (let (ido-report-no-match ido-use-faces)
|
||||
(ein:completing-read "URL or port: "
|
||||
url-or-port-list
|
||||
|
@ -285,7 +239,7 @@ refresh the notebook connection."
|
|||
:type 'boolean
|
||||
:group 'ein)
|
||||
|
||||
(defcustom ein:notebooklist-date-format "%x"
|
||||
(defcustom ein:notebooklist-date-format "%F"
|
||||
"The format spec for date in notebooklist mode.
|
||||
See `ein:format-time-string'."
|
||||
:type '(or string function)
|
||||
|
@ -516,53 +470,14 @@ This function is called via `ein:notebook-after-rename-hook'."
|
|||
sort-order)))
|
||||
(-concat dirs nbs files)))
|
||||
|
||||
(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)))
|
||||
(widget-insert " | ")
|
||||
(widget-create
|
||||
'link
|
||||
:notify (lambda (&rest ignore)
|
||||
(ein:notebooklist-login
|
||||
(ein:$notebooklist-url-or-port ein:%notebooklist%) path))
|
||||
name)))
|
||||
(widget-insert " |\n\n"))
|
||||
|
||||
(widget-create
|
||||
'link
|
||||
:notify (lambda (&rest ignore) (ein:notebooklist-new-notebook
|
||||
(ein:$notebooklist-url-or-port ein:%notebooklist%)
|
||||
(unless (eq ein:jupyter-default-kernel
|
||||
'first-alphabetically)
|
||||
ein:jupyter-default-kernel)))
|
||||
"New Notebook")
|
||||
(widget-insert " ")
|
||||
(widget-create
|
||||
'link
|
||||
:notify (lambda (&rest ignore) (ein:notebooklist-reload nil t))
|
||||
"Reload List")
|
||||
(widget-insert " ")
|
||||
(widget-create
|
||||
'link
|
||||
: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)
|
||||
"Render the header (for ipython>=3)."
|
||||
(defun render-header (url-or-port &rest args)
|
||||
(with-current-buffer (ein:notebooklist-get-buffer url-or-port)
|
||||
(widget-insert
|
||||
(format "Contents API %s (%s)\n\n" (ein:need-notebook-version url-or-port) url-or-port))
|
||||
|
||||
(let ((breadcrumbs (generate-breadcrumbs (ein:$notebooklist-path ein:%notebooklist%))))
|
||||
(format "Contents API %s (%s)\n\n"
|
||||
(ein:need-notebook-version url-or-port)
|
||||
url-or-port))
|
||||
(let ((breadcrumbs (generate-breadcrumbs
|
||||
(ein:$notebooklist-path ein:%notebooklist%))))
|
||||
(dolist (p breadcrumbs)
|
||||
(lexical-let ((url-or-port url-or-port)
|
||||
(name (car p))
|
||||
|
@ -570,39 +485,29 @@ This function is called via `ein:notebook-after-rename-hook'."
|
|||
(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)
|
||||
(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)))
|
||||
(unless ein:%notebooklist-new-kernel%
|
||||
(setq ein:%notebooklist-new-kernel%
|
||||
(if (eq ein:jupyter-default-kernel 'first-alphabetically)
|
||||
(ein:get-kernelspec url-or-port (caar kernels))
|
||||
(ein:get-kernelspec
|
||||
url-or-port
|
||||
(if (stringp ein:jupyter-default-kernel)
|
||||
ein:jupyter-default-kernel
|
||||
(symbol-name ein:jupyter-default-kernel))))))
|
||||
(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")
|
||||
|
||||
|
@ -610,30 +515,26 @@ This function is called via `ein:notebook-after-rename-hook'."
|
|||
(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 _args)
|
||||
(setq ein:%notebooklist-new-kernel%
|
||||
(ein:get-kernelspec
|
||||
url-or-port
|
||||
(widget-value widget)))
|
||||
(message "New notebooks started with %s kernel"
|
||||
(ein:$kernelspec-display-name
|
||||
ein:%notebooklist-new-kernel%))))))
|
||||
(if (null kernels)
|
||||
(widget-insert "\n No kernels found.")
|
||||
(dolist (k kernels)
|
||||
(widget-radio-add-item radio-widget (list 'item
|
||||
:value (car k)
|
||||
:format (format "%s\n" (cdr k)))))
|
||||
(unless (eq ein:jupyter-default-kernel 'first-alphabetically)
|
||||
(widget-radio-value-set
|
||||
radio-widget
|
||||
(if (stringp ein:jupyter-default-kernel)
|
||||
ein:jupyter-default-kernel
|
||||
(symbol-name ein:jupyter-default-kernel))))
|
||||
(widget-insert "\n"))))))
|
||||
(let ((update (ein:get-kernelspec url-or-port
|
||||
(widget-value widget))))
|
||||
(unless (equal ein:%notebooklist-new-kernel% update)
|
||||
(when ein:%notebooklist-new-kernel%
|
||||
(message "New notebooks started with %s kernel"
|
||||
(ein:$kernelspec-display-name update)))
|
||||
(setq ein:%notebooklist-new-kernel% update)))))))
|
||||
(if kernels
|
||||
(let ((initial (ein:jupyter-get-default-kernel kernels)))
|
||||
(dolist (k kernels)
|
||||
(let ((child (widget-radio-add-item
|
||||
radio-widget
|
||||
(list 'item
|
||||
:value (car k)
|
||||
:format (format "%s\n" (cdr k))))))
|
||||
(when (string= initial (car k))
|
||||
(widget-apply-action (widget-get child :button)))))
|
||||
(widget-insert "\n"))
|
||||
(widget-insert "\n No kernels found."))))))
|
||||
|
||||
(defun ein:format-nbitem-data (name last-modified)
|
||||
(let ((dt (date-to-time last-modified)))
|
||||
|
@ -661,7 +562,7 @@ This function is called via `ein:notebook-after-rename-hook'."
|
|||
'link
|
||||
:notify (lexical-let ((url-or-port url-or-port)
|
||||
(name name))
|
||||
(lambda (&rest ignore)
|
||||
(lambda (&rest _ignore)
|
||||
;; each directory creates a whole new notebooklist
|
||||
(ein:notebooklist-open* url-or-port
|
||||
(concat (file-name-as-directory
|
||||
|
@ -682,7 +583,7 @@ This function is called via `ein:notebook-after-rename-hook'."
|
|||
(widget-create
|
||||
'link
|
||||
:notify (lexical-let ((path path))
|
||||
(lambda (&rest ignore)
|
||||
(lambda (&rest _ignore)
|
||||
(message "[EIN]: NBlist delete file command. Implement me!")))
|
||||
"Delete")
|
||||
(widget-insert " : " (ein:format-nbitem-data name last-modified))
|
||||
|
@ -693,7 +594,7 @@ This function is called via `ein:notebook-after-rename-hook'."
|
|||
'link
|
||||
:notify (lexical-let ((url-or-port url-or-port)
|
||||
(path path))
|
||||
(lambda (&rest ignore)
|
||||
(lambda (&rest _ignore)
|
||||
(run-at-time 3 nil #'ein:notebooklist-reload)
|
||||
(ein:notebook-open url-or-port path)))
|
||||
"Open")
|
||||
|
@ -703,7 +604,7 @@ This function is called via `ein:notebook-after-rename-hook'."
|
|||
'link
|
||||
:notify (lexical-let ((url url-or-port)
|
||||
(session (car (gethash path sessions))))
|
||||
(lambda (&rest ignore)
|
||||
(lambda (&rest _ignore)
|
||||
(ein:kernel-delete--from-session-id url session #'ein:notebooklist-reload)))
|
||||
"Stop")
|
||||
(widget-insert "------"))
|
||||
|
@ -711,7 +612,7 @@ This function is called via `ein:notebook-after-rename-hook'."
|
|||
(widget-create
|
||||
'link
|
||||
:notify (lexical-let ((path path))
|
||||
(lambda (&rest ignore)
|
||||
(lambda (&rest _ignore)
|
||||
(ein:notebooklist-delete-notebook-ask
|
||||
path)))
|
||||
"Delete")
|
||||
|
@ -734,11 +635,8 @@ Notebook list data is passed via the buffer local variable
|
|||
nil)))
|
||||
|
||||
(defun ein:notebooklist-render--finish (nb-version url-or-port restore-point sessions)
|
||||
(cl-letf (((symbol-function 'render-header) (if (< nb-version 3)
|
||||
#'render-header-ipy2
|
||||
#'render-header*)))
|
||||
(mapc (lambda (x) (funcall (symbol-function x) url-or-port sessions))
|
||||
ein:notebooklist-render-order))
|
||||
(render-header url-or-port sessions)
|
||||
(render-directory url-or-port sessions)
|
||||
(with-current-buffer (ein:notebooklist-get-buffer url-or-port)
|
||||
(ein:notebooklist-mode)
|
||||
(widget-setup)
|
||||
|
@ -945,15 +843,12 @@ and the url-or-port argument of ein:notebooklist-open*."
|
|||
("New Notebook (with name)"
|
||||
ein:notebooklist-new-notebook-with-name)))))
|
||||
|
||||
(defun ein:notebooklist-revert-wrapper (&optional ignore-auto noconfirm preserve-modes)
|
||||
(ein:notebooklist-reload))
|
||||
|
||||
(define-derived-mode ein:notebooklist-mode special-mode "ein:notebooklist"
|
||||
"IPython notebook list mode.
|
||||
Commands:
|
||||
\\{ein:notebooklist-mode-map}"
|
||||
(set (make-local-variable 'revert-buffer-function)
|
||||
'ein:notebooklist-revert-wrapper))
|
||||
(lambda (&rest _args) (ein:notebooklist-reload))))
|
||||
|
||||
|
||||
(provide 'ein-notebooklist)
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
(require 'ein-jupyter)
|
||||
(require 'ein-file)
|
||||
(require 'ein-notebooklist)
|
||||
(require 'ein-k8s)
|
||||
|
||||
(defcustom ein:process-jupyter-regexp "\\(jupyter\\|ipython\\)\\(-\\|\\s-+\\)note"
|
||||
"Regexp by which we recognize notebook servers."
|
||||
|
@ -208,9 +209,10 @@ is used instead. BUFFER-CALLBACK is called after notebook opened."
|
|||
(defun ein:process-find-file-callback ()
|
||||
"A callback function for `find-file-hook' to open notebook."
|
||||
(interactive)
|
||||
(-when-let* ((filename buffer-file-name)
|
||||
(match-p (string-match-p "\\.ipynb$" filename)))
|
||||
(ein:process-open-notebook filename #'kill-buffer-if-not-modified)))
|
||||
(cl-letf (((symbol-function 'ein:k8s-select-context) #'ignore))
|
||||
(-when-let* ((filename buffer-file-name)
|
||||
(match-p (string-match-p "\\.ipynb$" filename)))
|
||||
(ein:process-open-notebook filename #'kill-buffer-if-not-modified))))
|
||||
|
||||
(provide 'ein-process)
|
||||
|
||||
|
|
|
@ -395,7 +395,7 @@ Adapted from twittering-mode.el's `case-string'."
|
|||
|
||||
;;; Text manipulation on buffer
|
||||
|
||||
(defun ein:find-leftmot-column (beg end)
|
||||
(defun ein:find-leftmost-column (beg end)
|
||||
"Return the leftmost column in region BEG to END."
|
||||
(save-excursion
|
||||
(let (mincol)
|
||||
|
@ -407,16 +407,18 @@ 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)))
|
||||
(return-from ein:find-leftmost-column mincol)))
|
||||
mincol)))
|
||||
|
||||
|
||||
;;; Misc
|
||||
|
||||
(defun ein:completing-read (&rest args)
|
||||
(if (eq completing-read-function 'completing-read-default)
|
||||
(apply #'ido-completing-read args)
|
||||
(apply completing-read-function args)))
|
||||
(cond (noninteractive (if (consp (cl-second args))
|
||||
(car (cl-second args))
|
||||
(cl-second args)))
|
||||
((eq completing-read-function 'completing-read-default)
|
||||
(apply #'ido-completing-read args))
|
||||
(t (apply completing-read-function args))))
|
||||
|
||||
(defun ein:plist-iter (plist)
|
||||
"Return list of (key . value) in PLIST."
|
||||
|
|
|
@ -1180,7 +1180,7 @@ in the history."
|
|||
(let* ((beg (ein:cell-input-pos-min cell))
|
||||
(end (ein:cell-input-pos-max cell)))
|
||||
(indent-rigidly
|
||||
beg end (- (ein:find-leftmot-column beg end)))))
|
||||
beg end (- (ein:find-leftmost-column beg end)))))
|
||||
|
||||
(defun ein:worksheet--cells-before-cell (ws cell)
|
||||
(let ((cells (ein:worksheet-get-cells ws)))
|
||||
|
|
|
@ -12,6 +12,11 @@
|
|||
|
||||
(defsubst poly-ein--neuter-markdown-mode ()
|
||||
"Consolidate fragility here."
|
||||
(unless (eq 'ein:notebook-mode (caar minor-mode-map-alist))
|
||||
(when-let ((entry (assq 'ein:notebook-mode minor-mode-map-alist)))
|
||||
(setf minor-mode-map-alist
|
||||
(cons entry
|
||||
(assq-delete-all 'ein:notebook-mode minor-mode-map-alist)))))
|
||||
(when (eq major-mode 'markdown-mode)
|
||||
(poly-ein--remove-hook "markdown" after-change-functions)
|
||||
(poly-ein--remove-hook "markdown" jit-lock-after-change-extend-region-functions)
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
'notebook_saving.Notebook)
|
||||
(should (string-prefix-p
|
||||
(concat "IP[y]: Saving Notebook... | "
|
||||
(substitute-command-keys "Kernel requires restart \\<ein:notebook-mode-map>\\[ein:notebook-restart-session-command] | ")
|
||||
(substitute-command-keys "Kernel requires restart \\<ein:notebook-mode-map>\\[ein:notebook-restart-session-command-km] | ")
|
||||
;;"Kernel requires restart C-c C-x C-r | "
|
||||
"/1\\ /2\\ /3\\ [+]") (ein:header-line)))))
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@ def func():
|
|||
|
||||
;;; Text manipulation on buffer
|
||||
|
||||
(ert-deftest ein:find-leftmot-column-simple-cases ()
|
||||
(ert-deftest ein:find-leftmost-column-simple-cases ()
|
||||
(cl-loop for (indent text) in
|
||||
'(;; No indent
|
||||
(0 "\
|
||||
|
@ -123,7 +123,7 @@ def f():
|
|||
)
|
||||
do (with-temp-buffer
|
||||
(insert text)
|
||||
(should (= (ein:find-leftmot-column (point-min) (point-max))
|
||||
(should (= (ein:find-leftmost-column (point-min) (point-max))
|
||||
indent)))))
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue