Eksekusi async dalam org babel

14

Apakah ada penyesuaian umum yang baik dari org-babel untuk berjalan secara tidak sinkron? Baru-baru ini saya berencana untuk menggunakan MATLAB melalui org-babel, tetapi saya ingin itu secara async, karena beberapa perhitungan memang membutuhkan waktu.

Saya tidak ingin mengkustomisasi ob-matlab saja. Ini karena saya pikir itu harus dilakukan di level framework daripada aplikasi. Dengan kata lain, modifikasi yang sama harus mengaktifkan fitur async untuk ekstensi bahasa lain, misalnya bahasa R.

Adakah yang punya solusi yang bagus? Sejauh ini saya telah mencoba async.eldan deferred.elmemodifikasi org-babel-execute-safely-maybeyang dapat ditemukan ob-core.elpada saat ini.

diadochos
sumber
Petunjuk lain adalah dapat meneruskan blok babel ke layar atau tmux.
stardiviner
Saya belum pernah menerapkannya, tapi kedengarannya mungkin. Terima kasih.
diadochos
Saya kira saya menerima jawaban saya sendiri karena belum ada solusi lain yang diposting selama satu bulan terakhir.
diadochos

Jawaban:

6

Sejauh ini saya telah menemukan bahwa menelurkan proses Emacs baru adalah solusi.

Inilah yang telah saya lakukan.

1. Tambahkan fungsi untuk memulai proses emacs eksternal.

init.el

(defvar my/async-emacs-repl-org-babel-init-file "~/.emacs.d/org-babel-async-init" "File to load on executing async babel evaluation.")

(defun my/async-emacs-repl--start (process-name init-file)
  "Start a new Emacs process as a REPL server."
  (async-shell-command (concat
                        "TERM=vt200 emacs --batch -nw"
                        " --eval '(load \"" init-file "\")'"
                        " --eval '(while t (print (eval (read))))'"
                        )
                       process-name))

(defun my/async-emacs-repl--org-babel--start-server ()
  "Starts an Emacs process for async org-babel execution."
  (my/async-emacs-repl--start "*org-babel-async*" my/async-emacs-repl-org-babel-init-file))

(defun my/async-emacs-repl--org-babel--start-if-not-exists ()
  "Starts an Emacs process if the process does not exist."
  (if (not (get-buffer-process "*org-babel-async*")) (my/async-emacs-repl--org-babel--start-server)))

(defun my/async-emacs-repl--org-babel--execute--build-command (file-name line-number)
  "Build the command for executing `org-babel-execute-src-block'."
  (concat
   "(progn"
   " (find-file \"" file-name "\")"
   " (revert-buffer t t)"
   " (goto-line " (number-to-string line-number) ")"
   " (org-babel-execute-src-block t)"
   " (save-buffer)"
   ")"
   "\n"))

(defun my/async-emacs-repl--org-babel--execute (process-name file-name line-number)
  "Sends the command to the server to run the code-block the cursor is at."
  (process-send-string
   process-name
   (my/async-emacs-repl--org-babel--execute--build-command file-name line-number)))

(defun my/async-emacs-repl-org-babel-do-execute ()
  "Run org babel execution at point."
  (my/async-emacs-repl--org-babel--execute "*org-babel-async*" (buffer-file-name) (line-number-at-pos)))

(defun my/async-emacs-repl-org-babel-execute ()
  "Run by the user. Executes command. Starts buffer if not exists."
  (interactive)
  (save-buffer)
  (my/async-emacs-repl--org-babel--start-if-not-exists)
  (my/async-emacs-repl-org-babel-do-execute))

2. Tambahkan file konfigurasi untuk dimuat dalam proses emacs baru.

Fungsi di atas memulai emacs dalam --batchmode. Dengan demikian init.el yang normal tidak akan dimuat.

Sebagai gantinya, kami ingin membuat file konfigurasi yang lebih pendek (untuk memuat path dan sebagainya).

Path ke file konfigurasi baru kami disimpan di async-emacs-repl-org-babel-init-filedalam cuplikan di atas.

org-babel-async-init.el

;; 1
(package-initialize)

;; 2
(setq org-confirm-babel-evaluate nil)

;; 3
(let ((my/org-babel-evaluated-languages
       '(emacs-lisp
         ditaa
         python
         ruby
         C
         matlab
         clojure
         sh
         dot
         plantuml)))
  (org-babel-do-load-languages
   'org-babel-load-languages
   (mapcar (lambda (lang)
             (cons lang t))
           my/org-babel-evaluated-languages)))

Disini kita ...

  1. Tambahkan jalur paket.
  2. Beri tahu org-mode untuk tidak bertanya apakah akan menjalankan blok kode.
  3. Beri tahu org-babel bahasa mana yang diperlukan.

Catatan Kaki 1: Tanpa pengaturan ini, evaluasi akan gagal dengan "No org-babel-execute function for $lang!"

Catatan kaki 2: Tentu saja Anda dapat memuat init.el normal daripada membuat file konfigurasi baru, jika diinginkan. Lakukan itu dengan menambahkan (setq org-babel-async-init-file "~/.emacs.d/init")ke init.el. Tapi saya pikir membuat file konfigurasi untuk tugas ini lebih mudah.

3. Selain itu ...

Tambahkan ke init.el

;; This will stop the new process buffer from getting focus.
(setq display-buffer-alist (append display-buffer-alist '(("*org-babel-async*" display-buffer-no-window))))

;; This will automatically show the result section.
(global-auto-revert-mode 1)

Tambahkan ke org-babel-async-init.el

;; This will skip the "Save anyway?" confirmation of automatically saving the file when you also edited the buffer from Emacs while an asynchronous process is running.
(defun advice:verify-visited-file-modtime (orig-func &rest args) t)
(advice-add 'verify-visited-file-modtime :around 'advice:verify-visited-file-modtime)

;; This will skip the "Select coding system" prompt that appears when the result is inserted. This may vary among environments.
(setq coding-system-for-write 'utf-8)

;; This will skip the "changed on disk; really edit the buffer?" checking.
(defun ask-user-about-supersession-threat (fn) "blatantly ignore files that changed on disk")

Tambahkan ke org-babel-async-init.el (Anda mungkin tidak membutuhkan ini. Ini untuk MATLAB)

;; This will set MATLAB cli path.
(setq-default matlab-shell-command "/Applications/MATLAB_R2016a.app/bin/matlab")
;; The MATLAB cli path can be obtained by running `fullfile(matlabroot, 'bin')` in your MATLAB.

;; This will stop MATLAB from showing the splash (the MATLAB logo) at the beginning.
(setq-default matlab-shell-command-switches '("-nodesktop" "-nosplash"))

Tambahkan ke org-babel-async-init.el (Anda mungkin tidak membutuhkan ini. Ini untuk Julia, R dan bahasa lain yang menggunakan ESS.)

;; This will enable :session header in Julia and other languages that use ESS (Emacs speaks statistics).
(load "/path/to/ess-site")
;; This will suppress ESS from prompting for session directory.
(setq ess-ask-for-ess-directory nil)

4. Penggunaan

(Setelah pengaturan di atas.)

  1. Pindahkan kursor ke potongan kode yang ingin Anda jalankan.
  2. Jalankan M-x my/async-emacs-repl-org-babel-execute(bukannya melakukan C-c C-c). Ini akan memulai proses Emacs eksternal sebagai server REPL jika diperlukan, dan kemudian jalankan blok sumber Anda.

Ucapan Terima Kasih

Saya telah belajar ide memulai proses emacs untuk evaluasi org-babel dari posting ini . Saya ingin mengucapkan terima kasih kepada penulis.

Komentar untuk penyesuaian

Idenya di sini sederhana. Mulai emacs baru memproses sebagai REPL untuk Elisp, lakukan find-fileke file .org sama kita mengedit, goto-lineke titik kursor yang sama, berjalan org-babel-execute-src-block, save-buffer. Berhenti keluar sampai pengguna menghentikan proses (Jika tidak, grafik akan langsung hilang setelah ditampilkan). Seseorang dapat secara alami berpikir untuk memperpanjang ini dengan:

  • Menggunakan mode-org C-c C-cdaripada menjalankan fungsi dengan tangan / mengatur keybind baru (yang dapat dicapai dengan saran).
  • Pengalihan nama proses secara kondisional sesuai dengan: variabel sesi dan bahasa
  • Mengganti file init secara kondisional berdasarkan bahasa.

Sebenarnya, keberhasilan pendekatan ini menurut saya menunjukkan cara umum mengembangkan fitur-fitur async di Emacs. Membuat lapisan "perintah", menambahkan skrip untuk melakukan tugas, dan memiliki kerangka kerja untuk memulai dan menggunakan kembali proses emacs. Sama seperti kerangka kerja Symfony dari PHP (PHP tidak memiliki utas) memiliki fitur Command.

Edit riwayat

Kode yang di-refactored (2016-04-02). Solusi sekarang menggunakan kembali proses Emacs (2016-04-02). Solusi sekarang disederhanakan dan hanya memiliki satu interactiveperintah untuk dijalankan (2016-04-02. Konfigurasi tambahan (2016-04-12).

diadochos
sumber
Pernahkah Anda melihat async.el?
PythonNut
Ya saya punya. Ini pada dasarnya memulai proses baru Emacs, dan menjalankan lambdafungsi yang diberikan padanya. Saya tidak menggunakannya untuk solusi ini karena saya tidak dapat menemukan cara untuk mengirim data ke proses baru. Mengkomunikasikan proses diperlukan jika Anda ingin menggunakan: sesi fitur dari org-babel.
diadochos
Terima kasih telah mengerjakan solusi ini. Saya sudah mencoba tetapi saya mendapatkan pesan kesalahan ini: TERM=vt200 emacs --batch -nw --eval '(load "~/.emacs.d/org-babel-async-init")' --eval '(while t (print (eval (read))))': exited abnormally with code 255.Maaf, ini seharusnya menjadi komentar dan bukan jawaban, tetapi saya tidak punya cukup poin.
mhartm
Setelah menjalankan itu, apakah Anda melihat buffer bernama " org-babel-async "? Jika Anda dapat menemukannya, buffer itu mungkin berisi lebih banyak informasi tentang kesalahan. "keluar secara tidak normal dengan kode 255" umumnya terjadi ketika program yang ingin Anda jalankan pada proses emacs spawned gagal. Kemungkinan jalan keluar: 1) Periksa apakah Anda memiliki file yang ditentukan dalam file saya / async-emacs-repl-org-babel-init. Jika tidak, buat yang seperti dijelaskan di atas. 2) Periksa apakah Anda telah mencantumkan bahasa yang ingin Anda gunakan org-babel-do-load-languages. 3) #+SRC_BEGINBlok yang Anda jalankan mengandung bug.
diadochos
Oke, jadi masalah ini adalah bahwa saya perlu untuk menyimpan file org saya sebelum menjalankan M-x my/async-emacs-repl-org-babel-execute, jika tidak "org-babel-async" penyangga akan mengeluh: ...t/Dropbox/org/work.org locked by maarhart@htkl... (pid 68694): (s, q, p, ?)? Please type q, s, or p; or ? for help. Jadi jika ini bisa diselesaikan, itu akan fantastis. Bagaimanapun, terima kasih untuk ini, sungguh menakjubkan! Omong-omong, apakah mungkin untuk mengikatnya C-c C-catau akankah itu bertentangan dengan mode-org?
mhartm