update eaf package

This commit is contained in:
2021-01-30 14:52:51 +01:00
parent 84eb4929ee
commit 5207af83cb
4981 changed files with 11795 additions and 761570 deletions

View File

@@ -1,39 +1,44 @@
# Table of Contents
1. [Requirements](#org5ff850e)
2. [Installation](#org0e5b75a)
3. [Key bindings and functions](#org9907e5a)
4. [Packages](#orgccb3d6a)
5. [Information](#orgdd0b988)
6. [Errors](#org2fbb7be)
1. [failed execution of bibtex2html](#orgd3ccf34)
1. [Requirements](#orgf4fd69f)
2. [Installation](#orgeb12447)
3. [Key bindings and functions](#orgb4f8fdc)
4. [Packages](#orga8c6216)
5. [Information](#orgaf15aed)
6. [Errors](#org7c7dda9)
1. [failed execution of bibtex2html](#org79a8e5c)
This is an ongoing evolution of Emacs configuration files, inspired by
a bunch of online resources.
<a id="org5ff850e"></a>
<a id="orgf4fd69f"></a>
# Requirements
- external packages
- aspell/hunspell (flyspell-buffer)
- biber (for org-mode latex-mode)
- bibtex2html (for org-mode)
- ledger (for ledger-mode)
- languagetool (for langtool)
- python-pylint (for python-mode)
- for EAF emacs-application-framework
yay -S python-pyqt5 python-pyqt5-sip python-pyqtwebengine python-qrcode python-feedparser python-dbus python-pyinotify python-markdown nodejs aria2 libreoffice python-pymupdf python-grip filebrowser-bin
- copy/merge the texmf directory into your home directory
- copy all files of this directory into your ~/.config/emacs directory
- for Emacs version < 27 create symbolic link to the init file
- `ln -s ~/.config/emacs/init ~/.emacs` (or `~/.emacs.el`,
`~/.emacs.d/init.el`)
external packages:
# flyspell-buffer
yay -S aspell hunspell
# org-mode latex-mode
yay -S biber bibtex2html
# ledger-mode
yay -S ledger
# langtool
yay -S languagetool
# python-mode
yay -S python-pylint
# emacs-application-famework EAF
yay -S --needed git nodejs aria2 libreoffice wmctrl xdotool jre-openjdk \
python-pyqt5 python-pyqt5-sip python-pyqtwebengine python-qrcode \
python-feedparser python-dbus python-pyinotify python-markdown \
python-grip filebrowser-bin python-epc
Optional package for EAF see in Installation.
<a id="org0e5b75a"></a>
<a id="orgeb12447"></a>
# Installation
@@ -41,8 +46,27 @@ Install into `~/.config/emacs` (must be empty)
git clone http://gogs.weseng.de/daniel/emacs.git ~/.config/emacs
or copy all files of this directory into your ~/.config/emacs directory
<a id="org9907e5a"></a>
- for Emacs version < 27 create symbolic link to the init file
- `ln -s ~/.config/emacs/init ~/.emacs` (or `~/.emacs.el`,
`~/.emacs.d/init.el`)
copy/merge the texmf directory into your home directory
optional for EAF
yay --editmenu -S mupdf python-pymupdf
edit `PGKBUILD` file for `python-pymupdf` to:
prepare() {
cd "${_name}-${pkgver}"
patch --forward --strip=1 --input="${HOME}/.config/emacs/scripts/fix-pymupdf-library-linking-for-arcolinux.patch"
}
<a id="orgb4f8fdc"></a>
# Key bindings and functions
@@ -321,7 +345,7 @@ Install into `~/.config/emacs` (must be empty)
</table>
<a id="orgccb3d6a"></a>
<a id="orga8c6216"></a>
# Packages
@@ -330,7 +354,7 @@ Install into `~/.config/emacs` (must be empty)
colors with the color they represent as background.
<a id="orgdd0b988"></a>
<a id="orgaf15aed"></a>
# Information
@@ -340,12 +364,12 @@ but interlocks to prevent the simultaneous editing of the same
file. See [Interlocking](https://www.emacswiki.org/info-ref?find=Interlocking) in the [EmacsManual](https://www.emacswiki.org/emacs/EmacsManual) for details.
<a id="org2fbb7be"></a>
<a id="org7c7dda9"></a>
# Errors
<a id="orgd3ccf34"></a>
<a id="org79a8e5c"></a>
## failed execution of bibtex2html

View File

@@ -4,27 +4,54 @@ This is an ongoing evolution of Emacs configuration files, inspired by
a bunch of online resources.
* Requirements
- external packages
- aspell/hunspell (flyspell-buffer)
- biber (for org-mode latex-mode)
- bibtex2html (for org-mode)
- ledger (for ledger-mode)
- languagetool (for langtool)
- python-pylint (for python-mode)
- for EAF emacs-application-framework
yay -S python-pyqt5 python-pyqt5-sip python-pyqtwebengine python-qrcode python-feedparser python-dbus python-pyinotify python-markdown nodejs aria2 libreoffice python-pymupdf python-grip filebrowser-bin
- copy/merge the texmf directory into your home directory
- copy all files of this directory into your ~/.config/emacs directory
- for Emacs version < 27 create symbolic link to the init file
- =ln -s ~/.config/emacs/init ~/.emacs= (or =~/.emacs.el=,
=~/.emacs.d/init.el=)
external packages:
#+begin_src shell
# flyspell-buffer
yay -S aspell hunspell
# org-mode latex-mode
yay -S biber bibtex2html
# ledger-mode
yay -S ledger
# langtool
yay -S languagetool
# python-mode
yay -S python-pylint
# emacs-application-famework EAF
yay -S --needed git nodejs aria2 libreoffice wmctrl xdotool jre-openjdk \
python-pyqt5 python-pyqt5-sip python-pyqtwebengine python-qrcode \
python-feedparser python-dbus python-pyinotify python-markdown \
python-grip filebrowser-bin python-epc
#+end_src
Optional package for EAF see in Installation.
* Installation
Install into =~/.config/emacs= (must be empty)
#+begin_src sh
git clone http://gogs.weseng.de/daniel/emacs.git ~/.config/emacs
git clone http://gogs.weseng.de/daniel/emacs.git ~/.config/emacs
#+end_src
#+RESULTS:
or copy all files of this directory into your ~/.config/emacs directory
- for Emacs version < 27 create symbolic link to the init file
- =ln -s ~/.config/emacs/init ~/.emacs= (or =~/.emacs.el=,
=~/.emacs.d/init.el=)
copy/merge the texmf directory into your home directory
optional for EAF
#+begin_src shell
yay --editmenu -S mupdf python-pymupdf
#+end_src
edit =PGKBUILD= file for =python-pymupdf= to:
#+begin_example
prepare() {
cd "${_name}-${pkgver}"
patch --forward --strip=1 --input="${HOME}/.config/emacs/scripts/fix-pymupdf-library-linking-for-arcolinux.patch"
}
#+end_example
* Key bindings and functions
#+caption: General
| M-> | end-of-buffer | |

503
lisp/concurrent.el Normal file
View File

@@ -0,0 +1,503 @@
;;; concurrent.el --- Concurrent utility functions for emacs lisp -*- lexical-binding: t; -*-
;; Copyright (C) 2010-2016 SAKURAI Masashi
;; Author: SAKURAI Masashi <m.sakurai at kiwanami.net>
;; Version: 0.5.0
;; Package-Version: 20161229.330
;; Package-Commit: 2239671d94b38d92e9b28d4e12fd79814cfb9c16
;; Keywords: deferred, async, concurrent
;; Package-Requires: ((emacs "24.3") (deferred "0.5.0"))
;; URL: https://github.com/kiwanami/emacs-deferred/blob/master/README-concurrent.markdown
;; This program 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.
;; This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; 'concurrent.el' is a higher level library for concurrent tasks
;; based on 'deferred.el'. This library has following features:
;;
;; - Generator
;; - Green thread
;; - Semaphore
;; - Dataflow
;; - Signal/Channel
(require 'cl-lib)
(require 'deferred)
(defvar cc:version nil "version number")
(setq cc:version "0.3")
;;; Code:
(defmacro cc:aif (test-form then-form &rest else-forms)
(declare (debug (form form &rest form)))
`(let ((it ,test-form))
(if it ,then-form ,@else-forms)))
(put 'cc:aif 'lisp-indent-function 2)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Generator
(defun cc:generator-replace-yield (tree)
"[internal] Replace `yield' symbols to calling a function in TREE."
(let (ret)
(cl-loop for i in tree
do (cond
((eq i 'yield)
(push 'funcall ret)
(push i ret))
((listp i)
(push (cc:generator-replace-yield i) ret))
(t
(push i ret))))
(nreverse ret)))
(defun cc:generator-line (chain line)
"[internal] Return a macro expansion to execute the sexp LINE
asynchronously."
(cond
;; function object
((functionp line)
`(setq ,chain (deferred:nextc ,chain ,line)))
;; while loop form
((eq 'while (car line))
(let ((condition (cadr line))
(body (cddr line)))
`(setq ,chain
(deferred:nextc ,chain
(deferred:lambda (x)
(if ,condition
(deferred:nextc
(progn
,@(cc:generator-replace-yield body)) self)))))))
;; statement
(t
`(setq ,chain
(deferred:nextc ,chain
(deferred:lambda (x) ,(cc:generator-replace-yield line)))))))
(defmacro cc:generator (callback &rest body)
"Create a generator object. If BODY has `yield' symbols, it
means calling callback function CALLBACK."
(let ((chain (cl-gensym))
(cc (cl-gensym))
(waiter (cl-gensym)))
`(let* (,chain
(,cc ,callback)
(,waiter (deferred:new))
(yield (lambda (x) (funcall ,cc x) ,waiter)))
(setq ,chain ,waiter)
,@(cl-loop for i in body
collect
(cc:generator-line chain i))
(lambda () (deferred:callback ,waiter)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Thread
(defun cc:thread-line (wait-time chain line)
"[internal] Return a macro expansion to execute the sexp LINE asynchronously.
WAIT-TIME is an interval time between tasks.
CHAIN is the previous deferred task."
(cond
;; function object
((functionp line)
`(setq ,chain (deferred:nextc ,chain ,line)))
;; while loop form
((eq 'while (car line))
(let ((condition (cadr line))
(body (cddr line))
(retsym (cl-gensym)))
`(setq ,chain
(deferred:nextc ,chain
(deferred:lambda (x)
(if ,condition
(deferred:nextc
(let ((,retsym (progn ,@body)))
(if (deferred-p ,retsym) ,retsym
(deferred:wait ,wait-time)))
self)))))))
;; statement
(t
`(setq ,chain
(deferred:nextc ,chain
(lambda (x) ,line))))))
(defmacro cc:thread (wait-time-msec &rest body)
"Return a thread object."
(let ((chain (cl-gensym))
(dstart (cl-gensym)))
`(let* (,chain
(,dstart (deferred:new)))
(setq ,chain ,dstart)
,@(cl-loop for i in body
collect
(cc:thread-line wait-time-msec chain i))
(deferred:callback ,dstart))))
(put 'cc:thread 'lisp-indent-function 1)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Semaphore
(cl-defstruct cc:semaphore max-permits permits waiting-deferreds)
(defun cc:semaphore-create(permits-num)
"Return a semaphore object with PERMITS-NUM permissions."
(make-cc:semaphore :max-permits permits-num :permits permits-num))
(defun cc:semaphore-acquire(semaphore)
"Acquire an execution permission and return deferred object to chain.
If this semaphore object has permissions, the subsequent deferred
task is executed immediately. If this semaphore object has no
permissions, the subsequent deferred task is blocked. After the
permission is returned, the task is executed."
(cond
((< 0 (cc:semaphore-permits semaphore))
(cl-decf (cc:semaphore-permits semaphore))
(deferred:succeed))
(t
(let ((d (deferred:new)))
(push d (cc:semaphore-waiting-deferreds semaphore))
d))))
(defun cc:semaphore-release(semaphore)
"Release an execution permission. The programmer is responsible to return the permissions."
(when (<= (cc:semaphore-max-permits semaphore)
(cc:semaphore-permits semaphore))
(error "Too many calling semaphore-release. [max:%s <= permits:%s]"
(cc:semaphore-max-permits semaphore)
(cc:semaphore-permits semaphore)))
(let ((waiting-deferreds
(cc:semaphore-waiting-deferreds semaphore)))
(cond
(waiting-deferreds
(let* ((d (car (last waiting-deferreds))))
(setf (cc:semaphore-waiting-deferreds semaphore)
(nbutlast waiting-deferreds))
(deferred:callback-post d)))
(t
(cl-incf (cc:semaphore-permits semaphore)))))
semaphore)
(defun cc:semaphore-with (semaphore body-func &optional error-func)
"Execute the task BODY-FUNC asynchronously with the semaphore block."
(deferred:try
(deferred:nextc (cc:semaphore-acquire semaphore) body-func)
:catch
error-func
:finally
(lambda (_x) (cc:semaphore-release semaphore))))
(put 'cc:semaphore-with 'lisp-indent-function 1)
(defun cc:semaphore-release-all (semaphore)
"Release all permissions for resetting the semaphore object.
If the semaphore object has some blocked tasks, this function
return a list of the tasks and clear the list of the blocked
tasks in the semaphore object."
(setf (cc:semaphore-permits semaphore)
(cc:semaphore-max-permits semaphore))
(let ((ds (cc:semaphore-waiting-deferreds semaphore)))
(when ds
(setf (cc:semaphore-waiting-deferreds semaphore) nil))
ds))
(defun cc:semaphore-interrupt-all (semaphore)
"Clear the list of the blocked tasks in the semaphore and return a deferred object to chain.
This function is used for the interruption cases."
(when (cc:semaphore-waiting-deferreds semaphore)
(setf (cc:semaphore-waiting-deferreds semaphore) nil)
(setf (cc:semaphore-permits semaphore) 0))
(cc:semaphore-acquire semaphore))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Signal / Channel
(defun cc:signal-channel (&optional name parent-channel)
"Create a channel.
NAME is a channel name for debug.
PARENT-CHANNEL is an upstream channel. The observers of this channel can receive the upstream signals.
In the case of using the function `cc:signal-send', the observers of the upstream channel can not receive the signals of this channel. The function `cc:signal-send-global' can send a signal to the upstream channels from the downstream channels."
(let ((ch (cons
(or name (format "signal%s" (deferred:uid))) ; name for debug
(cons
parent-channel ; parent-channel
nil)))) ; observers
(when parent-channel
(cc:signal-connect
parent-channel
t (lambda (event)
(cl-destructuring-bind
(event-name event-args) event
(apply 'cc:signal-send
ch event-name event-args)))))
ch))
(defmacro cc:signal-name (ch)
"[internal] Return signal name."
`(car ,ch))
(defmacro cc:signal-parent-channel (ch)
"[internal] Return parent channel object."
`(cadr ,ch))
(defmacro cc:signal-observers (ch)
"[internal] Return observers."
`(cddr ,ch))
(defun cc:signal-connect (channel event-sym &optional callback)
"Append an observer for EVENT-SYM of CHANNEL and return a deferred object.
If EVENT-SYM is `t', the observer receives all signals of the channel.
If CALLBACK function is given, the deferred object executes the
CALLBACK function asynchronously. One can connect subsequent
tasks to the returned deferred object."
(let ((d (if callback
(deferred:new callback)
(deferred:new))))
(push (cons event-sym d)
(cc:signal-observers channel))
d))
(defun cc:signal-send (channel event-sym &rest args)
"Send a signal to CHANNEL. If ARGS values are given, observers can get the values by following code: (lambda (event) (destructuring-bind (event-sym (args)) event ... )). "
(let ((observers (cc:signal-observers channel))
(event (list event-sym args)))
(cl-loop for i in observers
for name = (car i)
for d = (cdr i)
if (or (eq event-sym name) (eq t name))
do (deferred:callback-post d event))))
(defun cc:signal-send-global (channel event-sym &rest args)
"Send a signal to the most upstream channel. "
(cc:aif (cc:signal-parent-channel channel)
(apply 'cc:signal-send-global it event-sym args)
(apply 'cc:signal-send channel event-sym args)))
(defun cc:signal-disconnect (channel deferred)
"Remove the observer object DEFERRED from CHANNEL and return
the removed deferred object. "
(let ((observers (cc:signal-observers channel)) deleted)
(setf
(cc:signal-observers channel) ; place
(cl-loop for i in observers
for d = (cdr i)
unless (eq d deferred)
collect i
else
do (push i deleted)))
deleted))
(defun cc:signal-disconnect-all (channel)
"Remove all observers."
(setf
(cc:signal-observers channel) ; place
nil))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Dataflow
;; Dataflow variable entry
(cl-defstruct cc:dataflow key (value 'cc:dataflow-undefine) deferred-list)
(defun cc:dataflow-undefine-p (obj)
"[internal] If the variable entry is not bound, return `t'."
(eq 'cc:dataflow-undefine (cc:dataflow-value obj)))
(defmacro cc:dataflow-parent-environment (df)
"[internal] Return the parent environment."
`(car ,df))
(defmacro cc:dataflow-test (df)
"[internal] Return the test function."
`(cadr ,df))
(defmacro cc:dataflow-channel (df)
"[internal] Return the channel object."
`(cl-caddr ,df))
(defmacro cc:dataflow-list (df)
"[internal] Return the list of deferred object which are waiting for value binding."
`(cl-cdddr ,df))
(defun cc:dataflow-environment (&optional parent-env test-func channel)
"Create a dataflow environment.
PARENT-ENV is the default environment. If this environment doesn't have the entry A and the parent one has the entry A, this environment can return the entry A. One can override the entry, setting another entry A to this environment.
TEST-FUNC is a test function that compares the entry keys. The default function is `equal'.
CHANNEL is a channel object that sends signals of variable events. Observers can receive following signals:
-get-first : the fist referrer is waiting for binding,
-get-waiting : another referrer is waiting for binding,
-set : a value is bound,
-get : returned a bound value,
-clear : cleared one entry,
-clear-all : cleared all entries.
"
(let ((this (list parent-env
(or test-func 'equal)
(or channel
(cc:signal-channel
'dataflow
(and parent-env
(cc:dataflow-channel parent-env)))))))
(cc:dataflow-init-connect this)
this))
(defun cc:dataflow-init-connect (df)
"[internal] Initialize the channel object."
(cc:dataflow-connect
df 'set
(lambda (args)
(cl-destructuring-bind (_event (key)) args
(let* ((obj (cc:dataflow-get-object-for-value df key))
(value (and obj (cc:dataflow-value obj))))
(when obj
(cl-loop for i in (cc:aif (cc:dataflow-get-object-for-deferreds df key)
(cc:dataflow-deferred-list it) nil)
do (deferred:callback-post i value))
(setf (cc:dataflow-deferred-list obj) nil)))))))
(defun cc:dataflow-get-object-for-value (df key)
"[internal] Return an entry object that is indicated by KEY.
If the environment DF doesn't have the entry and the parent one has the entry, this function returns the entry of the parent environment. This function doesn't affect the waiting list."
(or
(cl-loop for i in (cc:dataflow-list df)
with test = (cc:dataflow-test df)
if (and (funcall test key (cc:dataflow-key i))
(not (cc:dataflow-undefine-p i)))
return i)
(deferred:aand
(cc:dataflow-parent-environment df)
(cc:dataflow-get-object-for-value it key))))
(defun cc:dataflow-get-object-for-deferreds (df key)
"[internal] Return a list of the deferred objects those are waiting for value binding.
This function doesn't affect the waiting list and doesn't refer the parent environment."
(cl-loop for i in (cc:dataflow-list df)
with test = (cc:dataflow-test df)
if (funcall test key (cc:dataflow-key i))
return i))
(defun cc:dataflow-connect (df event-sym &optional callback)
"Append an observer for EVENT-SYM of the channel of DF and return a deferred object.
See the docstring of `cc:dataflow-environment' for details."
(cc:signal-connect (cc:dataflow-channel df) event-sym callback))
(defun cc:dataflow-signal (df event &optional arg)
"[internal] Send a signal to the channel of DF."
(cc:signal-send (cc:dataflow-channel df) event arg))
(defun cc:dataflow-get (df key)
"Return a deferred object that can refer the value which is indicated by KEY.
If DF has the entry that bound value, the subsequent deferred task is executed immediately.
If not, the task is deferred till a value is bound."
(let ((obj (cc:dataflow-get-object-for-value df key)))
(cond
((and obj (cc:dataflow-value obj))
(cc:dataflow-signal df 'get key)
(deferred:succeed (cc:dataflow-value obj)))
(t
(setq obj (cc:dataflow-get-object-for-deferreds df key))
(unless obj
(setq obj (make-cc:dataflow :key key))
(push obj (cc:dataflow-list df))
(cc:dataflow-signal df 'get-first key))
(let ((d (deferred:new)))
(push d (cc:dataflow-deferred-list obj))
(cc:dataflow-signal df 'get-waiting key)
d)))))
(defun cc:dataflow-get-sync (df key)
"Return the value which is indicated by KEY synchronously.
If the environment DF doesn't have an entry of KEY, this function returns nil."
(let ((obj (cc:dataflow-get-object-for-value df key)))
(and obj (cc:dataflow-value obj))))
(defun cc:dataflow-set (df key value)
"Bind the VALUE to KEY in the environment DF.
If DF already has the bound entry of KEY, this function throws an error signal.
VALUE can be nil as a value."
(let ((obj (cc:dataflow-get-object-for-deferreds df key)))
(cond
((and obj (not (cc:dataflow-undefine-p obj)))
;; overwrite!
(error "Can not set a dataflow value. The key [%s] has already had a value. NEW:[%s] OLD:[%s]" key value (cc:dataflow-value obj)))
(obj
(setf (cc:dataflow-value obj) value))
(t
;; just value arrived
(push (make-cc:dataflow :key key :value value)
(cc:dataflow-list df))))
;; value arrived and start deferred objects
(cc:dataflow-signal df 'set key)
value))
(defun cc:dataflow-clear (df key)
"Clear the entry which is indicated by KEY.
This function does nothing for the waiting deferred objects."
(cc:dataflow-signal df 'clear key)
(setf (cc:dataflow-list df)
(cl-loop for i in (cc:dataflow-list df)
with test = (cc:dataflow-test df)
unless (funcall test key (cc:dataflow-key i))
collect i)))
(defun cc:dataflow-get-avalable-pairs (df)
"Return an available key-value alist in the environment DF and the parent ones."
(append
(cl-loop for i in (cc:dataflow-list df)
for key = (cc:dataflow-key i)
for val = (cc:dataflow-value i)
unless (cc:dataflow-undefine-p i) collect (cons key val))
(deferred:aand
(cc:dataflow-parent-environment df)
(cc:dataflow-get-avalable-pairs it))))
(defun cc:dataflow-get-waiting-keys (df)
"Return a list of keys which have waiting deferred objects in the environment DF and the parent ones."
(append
(cl-loop for i in (cc:dataflow-list df)
for key = (cc:dataflow-key i)
if (cc:dataflow-undefine-p i) collect key)
(deferred:aand
(cc:dataflow-parent-environment df)
(cc:dataflow-get-waiting-keys it))))
(defun cc:dataflow-clear-all (df)
"Clear all entries in the environment DF.
This function does nothing for the waiting deferred objects."
(cc:dataflow-signal df 'clear-all)
(setf (cc:dataflow-list df) nil))
(provide 'concurrent)
;; Local Variables:
;; byte-compile-warnings: (not cl-functions)
;; End:
;;; concurrent.el ends here

973
lisp/deferred.el Normal file
View File

@@ -0,0 +1,973 @@
;;; deferred.el --- Simple asynchronous functions for emacs lisp -*- lexical-binding: t; -*-
;; Copyright (C) 2010-2016 SAKURAI Masashi
;; Author: SAKURAI Masashi <m.sakurai at kiwanami.net>
;; Version: 0.5.1
;; Package-Version: 20170901.1330
;; Package-Commit: 2239671d94b38d92e9b28d4e12fd79814cfb9c16
;; Keywords: deferred, async
;; Package-Requires: ((emacs "24.4"))
;; URL: https://github.com/kiwanami/emacs-deferred
;; This program 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.
;; This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; 'deferred.el' is a simple library for asynchronous tasks.
;; [https://github.com/kiwanami/emacs-deferred]
;; The API is almost the same as JSDeferred written by cho45. See the
;; JSDeferred and Mochikit.Async web sites for further documentations.
;; [https://github.com/cho45/jsdeferred]
;; [http://mochikit.com/doc/html/MochiKit/Async.html]
;; A good introduction document (JavaScript)
;; [http://cho45.stfuawsc.com/jsdeferred/doc/intro.en.html]
;;; Samples:
;; ** HTTP Access
;; (require 'url)
;; (deferred:$
;; (deferred:url-retrieve "http://www.gnu.org")
;; (deferred:nextc it
;; (lambda (buf)
;; (insert (with-current-buffer buf (buffer-string)))
;; (kill-buffer buf))))
;; ** Invoking command tasks
;; (deferred:$
;; (deferred:process "wget" "-O" "a.jpg" "http://www.gnu.org/software/emacs/tour/images/splash.png")
;; (deferred:nextc it
;; (lambda (x) (deferred:process "convert" "a.jpg" "-resize" "100x100" "jpg:b.jpg")))
;; (deferred:nextc it
;; (lambda (x)
;; (insert-image (create-image (expand-file-name "b.jpg") 'jpeg nil)))))
;; See the readme for further API documentation.
;; ** Applications
;; *Inertial scrolling for Emacs
;; [https://github.com/kiwanami/emacs-inertial-scroll]
;; This program makes simple multi-thread function, using
;; deferred.el.
(require 'cl-lib)
(require 'subr-x)
(declare-function pp-display-expression 'pp)
(defvar deferred:version nil "deferred.el version")
(setq deferred:version "0.5.0")
;;; Code:
(defmacro deferred:aand (test &rest rest)
"[internal] Anaphoric AND."
(declare (debug ("test" form &rest form)))
`(let ((it ,test))
(if it ,(if rest `(deferred:aand ,@rest) 'it))))
(defmacro deferred:$ (&rest elements)
"Anaphoric function chain macro for deferred chains."
(declare (debug (&rest form)))
`(let (it)
,@(cl-loop for i in elements
collect
`(setq it ,i))
it))
(defmacro deferred:lambda (args &rest body)
"Anaphoric lambda macro for self recursion."
(declare (debug ("args" form &rest form)))
(let ((argsyms (cl-loop repeat (length args) collect (cl-gensym))))
`(lambda (,@argsyms)
(let (self)
(setq self (lambda( ,@args ) ,@body))
(funcall self ,@argsyms)))))
(cl-defmacro deferred:try (d &key catch finally)
"Try-catch-finally macro. This macro simulates the
try-catch-finally block asynchronously. CATCH and FINALLY can be
nil. Because of asynchrony, this macro does not ensure that the
task FINALLY should be called."
(let ((chain
(if catch `((deferred:error it ,catch)))))
(when finally
(setq chain (append chain `((deferred:watch it ,finally)))))
`(deferred:$ ,d ,@chain)))
(defun deferred:setTimeout (f msec)
"[internal] Timer function that emulates the `setTimeout' function in JS."
(run-at-time (/ msec 1000.0) nil f))
(defun deferred:cancelTimeout (id)
"[internal] Timer cancellation function that emulates the `cancelTimeout' function in JS."
(cancel-timer id))
(defun deferred:run-with-idle-timer (sec f)
"[internal] Wrapper function for run-with-idle-timer."
(run-with-idle-timer sec nil f))
(defun deferred:call-lambda (f &optional arg)
"[internal] Call a function with one or zero argument safely.
The lambda function can define with zero and one argument."
(condition-case err
(funcall f arg)
('wrong-number-of-arguments
(display-warning 'deferred "\
Callback that takes no argument may be specified.
Passing callback with no argument is deprecated.
Callback must take one argument.
Or, this error is coming from somewhere inside of the callback: %S" err)
(condition-case nil
(funcall f)
('wrong-number-of-arguments
(signal 'wrong-number-of-arguments (cdr err))))))) ; return the first error
;; debug
(eval-and-compile
(defvar deferred:debug nil "Debug output switch."))
(defvar deferred:debug-count 0 "[internal] Debug output counter.")
(defmacro deferred:message (&rest args)
"[internal] Debug log function."
(when deferred:debug
`(progn
(with-current-buffer (get-buffer-create "*deferred:debug*")
(save-excursion
(goto-char (point-max))
(insert (format "%5i %s\n" deferred:debug-count (format ,@args)))))
(cl-incf deferred:debug-count))))
(defun deferred:message-mark ()
"[internal] Debug log function."
(interactive)
(deferred:message "==================== mark ==== %s"
(format-time-string "%H:%M:%S" (current-time))))
(defun deferred:pp (d)
(require 'pp)
(deferred:$
(deferred:nextc d
(lambda (x)
(pp-display-expression x "*deferred:pp*")))
(deferred:error it
(lambda (e)
(pp-display-expression e "*deferred:pp*")))
(deferred:nextc it
(lambda (_x) (pop-to-buffer "*deferred:pp*")))))
(defvar deferred:debug-on-signal nil
"If non nil, the value `debug-on-signal' is substituted this
value in the `condition-case' form in deferred
implementations. Then, Emacs debugger can catch an error occurred
in the asynchronous tasks.")
(defmacro deferred:condition-case (var protected-form &rest handlers)
"[internal] Custom condition-case. See the comment for
`deferred:debug-on-signal'."
(declare (debug condition-case)
(indent 2))
`(let ((debug-on-signal
(or debug-on-signal deferred:debug-on-signal)))
(condition-case ,var
,protected-form
,@handlers)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Back end functions of deferred tasks
(defvar deferred:tick-time 0.001
"Waiting time between asynchronous tasks (second).
The shorter waiting time increases the load of Emacs. The end
user can tune this parameter. However, applications should not
modify it because the applications run on various environments.")
(defvar deferred:queue nil
"[internal] The execution queue of deferred objects.
See the functions `deferred:post-task' and `deferred:worker'.")
(defmacro deferred:pack (a b c)
`(cons ,a (cons ,b ,c)))
(defun deferred:schedule-worker ()
"[internal] Schedule consuming a deferred task in the execution queue."
(run-at-time deferred:tick-time nil 'deferred:worker))
(defun deferred:post-task (d which &optional arg)
"[internal] Add a deferred object to the execution queue
`deferred:queue' and schedule to execute.
D is a deferred object. WHICH is a symbol, `ok' or `ng'. ARG is
an argument value for execution of the deferred task."
(push (deferred:pack d which arg) deferred:queue)
(deferred:message "QUEUE-POST [%s]: %s"
(length deferred:queue) (deferred:pack d which arg))
(deferred:schedule-worker)
d)
(defun deferred:clear-queue ()
"Clear the execution queue. For test and debugging."
(interactive)
(deferred:message "QUEUE-CLEAR [%s -> 0]" (length deferred:queue))
(setq deferred:queue nil))
(defun deferred:worker ()
"[internal] Consume a deferred task.
Mainly this function is called by timer asynchronously."
(when deferred:queue
(let* ((pack (car (last deferred:queue)))
(d (car pack))
(which (cadr pack))
(arg (cddr pack)) value)
(setq deferred:queue (nbutlast deferred:queue))
(condition-case err
(setq value (deferred:exec-task d which arg))
(error
(deferred:message "ERROR : %s" err)
(message "deferred error : %s" err)))
value)))
(defun deferred:flush-queue! ()
"Call all deferred tasks synchronously. For test and debugging."
(let (value)
(while deferred:queue
(setq value (deferred:worker)))
value))
(defun deferred:sync! (d)
"Wait for the given deferred task. For test and debugging.
Error is raised if it is not processed within deferred chain D."
(progn
(let ((last-value 'deferred:undefined*)
uncaught-error)
(deferred:try
(deferred:nextc d
(lambda (x) (setq last-value x)))
:catch
(lambda (err) (setq uncaught-error err)))
(while (and (eq 'deferred:undefined* last-value)
(not uncaught-error))
(sit-for 0.05)
(sleep-for 0.05))
(when uncaught-error
(deferred:resignal uncaught-error))
last-value)))
;; Struct: deferred
;;
;; callback : a callback function (default `deferred:default-callback')
;; errorback : an errorback function (default `deferred:default-errorback')
;; cancel : a canceling function (default `deferred:default-cancel')
;; next : a next chained deferred object (default nil)
;; status : if 'ok or 'ng, this deferred has a result (error) value. (default nil)
;; value : saved value (default nil)
;;
(cl-defstruct deferred
(callback 'deferred:default-callback)
(errorback 'deferred:default-errorback)
(cancel 'deferred:default-cancel)
next status value)
(defun deferred:default-callback (i)
"[internal] Default callback function."
(identity i))
(defun deferred:default-errorback (err)
"[internal] Default errorback function."
(deferred:resignal err))
(defun deferred:resignal (err)
"[internal] Safely resignal ERR as an Emacs condition.
If ERR is a cons (ERROR-SYMBOL . DATA) where ERROR-SYMBOL has an
`error-conditions' property, it is re-signaled unchanged. If ERR
is a string, it is signaled as a generic error using `error'.
Otherwise, ERR is formatted into a string as if by `print' before
raising with `error'."
(cond ((and (listp err)
(symbolp (car err))
(get (car err) 'error-conditions))
(signal (car err) (cdr err)))
((stringp err)
(error "%s" err))
(t
(error "%S" err))))
(defun deferred:default-cancel (d)
"[internal] Default canceling function."
(deferred:message "CANCEL : %s" d)
(setf (deferred-callback d) 'deferred:default-callback)
(setf (deferred-errorback d) 'deferred:default-errorback)
(setf (deferred-next d) nil)
d)
(defvar deferred:onerror nil
"Default error handler. This value is nil or a function that
have one argument for the error message.")
(defun deferred:exec-task (d which &optional arg)
"[internal] Executing deferred task. If the deferred object has
next deferred task or the return value is a deferred object, this
function adds the task to the execution queue.
D is a deferred object. WHICH is a symbol, `ok' or `ng'. ARG is
an argument value for execution of the deferred task."
(deferred:message "EXEC : %s / %s / %s" d which arg)
(when (null d) (error "deferred:exec-task was given a nil."))
(let ((callback (if (eq which 'ok)
(deferred-callback d)
(deferred-errorback d)))
(next-deferred (deferred-next d)))
(cond
(callback
(deferred:condition-case err
(let ((value (deferred:call-lambda callback arg)))
(cond
((deferred-p value)
(deferred:message "WAIT NEST : %s" value)
(if next-deferred
(deferred:set-next value next-deferred)
value))
(t
(if next-deferred
(deferred:post-task next-deferred 'ok value)
(setf (deferred-status d) 'ok)
(setf (deferred-value d) value)
value))))
(error
(cond
(next-deferred
(deferred:post-task next-deferred 'ng err))
(deferred:onerror
(deferred:call-lambda deferred:onerror err))
(t
(deferred:message "ERROR : %S" err)
(message "deferred error : %S" err)
(setf (deferred-status d) 'ng)
(setf (deferred-value d) err)
err)))))
(t ; <= (null callback)
(cond
(next-deferred
(deferred:exec-task next-deferred which arg))
((eq which 'ok) arg)
(t ; (eq which 'ng)
(deferred:resignal arg)))))))
(defun deferred:set-next (prev next)
"[internal] Connect deferred objects."
(setf (deferred-next prev) next)
(cond
((eq 'ok (deferred-status prev))
(setf (deferred-status prev) nil)
(let ((ret (deferred:exec-task
next 'ok (deferred-value prev))))
(if (deferred-p ret) ret
next)))
((eq 'ng (deferred-status prev))
(setf (deferred-status prev) nil)
(let ((ret (deferred:exec-task next 'ng (deferred-value prev))))
(if (deferred-p ret) ret
next)))
(t
next)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Basic functions for deferred objects
(defun deferred:new (&optional callback)
"Create a deferred object."
(if callback
(make-deferred :callback callback)
(make-deferred)))
(defun deferred:callback (d &optional arg)
"Start deferred chain with a callback message."
(deferred:exec-task d 'ok arg))
(defun deferred:errorback (d &optional arg)
"Start deferred chain with an errorback message."
(deferred:exec-task d 'ng arg))
(defun deferred:callback-post (d &optional arg)
"Add the deferred object to the execution queue."
(deferred:post-task d 'ok arg))
(defun deferred:errorback-post (d &optional arg)
"Add the deferred object to the execution queue."
(deferred:post-task d 'ng arg))
(defun deferred:cancel (d)
"Cancel all callbacks and deferred chain in the deferred object."
(deferred:message "CANCEL : %s" d)
(funcall (deferred-cancel d) d)
d)
(defun deferred:status (d)
"Return a current status of the deferred object. The returned value means following:
`ok': the callback was called and waiting for next deferred.
`ng': the errorback was called and waiting for next deferred.
nil: The neither callback nor errorback was not called."
(deferred-status d))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Basic utility functions
(defun deferred:succeed (&optional arg)
"Create a synchronous deferred object."
(let ((d (deferred:new)))
(deferred:exec-task d 'ok arg)
d))
(defun deferred:fail (&optional arg)
"Create a synchronous deferred object."
(let ((d (deferred:new)))
(deferred:exec-task d 'ng arg)
d))
(defun deferred:next (&optional callback arg)
"Create a deferred object and schedule executing. This function
is a short cut of following code:
(deferred:callback-post (deferred:new callback))."
(let ((d (if callback
(make-deferred :callback callback)
(make-deferred))))
(deferred:callback-post d arg)
d))
(defun deferred:nextc (d callback)
"Create a deferred object with OK callback and connect it to the given deferred object."
(let ((nd (make-deferred :callback callback)))
(deferred:set-next d nd)))
(defun deferred:error (d callback)
"Create a deferred object with errorback and connect it to the given deferred object."
(let ((nd (make-deferred :errorback callback)))
(deferred:set-next d nd)))
(defun deferred:watch (d callback)
"Create a deferred object with watch task and connect it to the given deferred object.
The watch task CALLBACK can not affect deferred chains with
return values. This function is used in following purposes,
simulation of try-finally block in asynchronous tasks, progress
monitoring of tasks."
(let* ((callback callback)
(normal (lambda (x) (ignore-errors (deferred:call-lambda callback x)) x))
(err (lambda (e)
(ignore-errors (deferred:call-lambda callback e))
(deferred:resignal e))))
(let ((nd (make-deferred :callback normal :errorback err)))
(deferred:set-next d nd))))
(defun deferred:wait (msec)
"Return a deferred object scheduled at MSEC millisecond later."
(let ((d (deferred:new)) (start-time (float-time)) timer)
(deferred:message "WAIT : %s" msec)
(setq timer (deferred:setTimeout
(lambda ()
(deferred:exec-task d 'ok
(* 1000.0 (- (float-time) start-time)))
nil) msec))
(setf (deferred-cancel d)
(lambda (x)
(deferred:cancelTimeout timer)
(deferred:default-cancel x)))
d))
(defun deferred:wait-idle (msec)
"Return a deferred object which will run when Emacs has been
idle for MSEC millisecond."
(let ((d (deferred:new)) (start-time (float-time)) timer)
(deferred:message "WAIT-IDLE : %s" msec)
(setq timer
(deferred:run-with-idle-timer
(/ msec 1000.0)
(lambda ()
(deferred:exec-task d 'ok
(* 1000.0 (- (float-time) start-time)))
nil)))
(setf (deferred-cancel d)
(lambda (x)
(deferred:cancelTimeout timer)
(deferred:default-cancel x)))
d))
(defun deferred:call (f &rest args)
"Call the given function asynchronously."
(deferred:next
(lambda (_x)
(apply f args))))
(defun deferred:apply (f &optional args)
"Call the given function asynchronously."
(deferred:next
(lambda (_x)
(apply f args))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Utility functions
(defun deferred:empty-p (times-or-seq)
"[internal] Return non-nil if TIMES-OR-SEQ is the number zero or nil."
(or (and (numberp times-or-seq) (<= times-or-seq 0))
(and (sequencep times-or-seq) (= (length times-or-seq) 0))))
(defun deferred:loop (times-or-seq func)
"Return a iteration deferred object."
(deferred:message "LOOP : %s" times-or-seq)
(if (deferred:empty-p times-or-seq) (deferred:next)
(let* (items (rd
(cond
((numberp times-or-seq)
(cl-loop for i from 0 below times-or-seq
with ld = (deferred:next)
do
(push ld items)
(setq ld
(let ((i i))
(deferred:nextc ld
(lambda (_x) (deferred:call-lambda func i)))))
finally return ld))
((sequencep times-or-seq)
(cl-loop for i in (append times-or-seq nil) ; seq->list
with ld = (deferred:next)
do
(push ld items)
(setq ld
(let ((i i))
(deferred:nextc ld
(lambda (_x) (deferred:call-lambda func i)))))
finally return ld)))))
(setf (deferred-cancel rd)
(lambda (x) (deferred:default-cancel x)
(cl-loop for i in items
do (deferred:cancel i))))
rd)))
(defun deferred:trans-multi-args (args self-func list-func main-func)
"[internal] Check the argument values and dispatch to methods."
(cond
((and (= 1 (length args)) (consp (car args)) (not (functionp (car args))))
(let ((lst (car args)))
(cond
((or (null lst) (null (car lst)))
(deferred:next))
((deferred:aand lst (car it) (or (functionp it) (deferred-p it)))
;; a list of deferred objects
(funcall list-func lst))
((deferred:aand lst (consp it))
;; an alist of deferred objects
(funcall main-func lst))
(t (error "Wrong argument type. %s" args)))))
(t (funcall self-func args))))
(defun deferred:parallel-array-to-alist (lst)
"[internal] Translation array to alist."
(cl-loop for d in lst
for i from 0 below (length lst)
collect (cons i d)))
(defun deferred:parallel-alist-to-array (alst)
"[internal] Translation alist to array."
(cl-loop for pair in
(sort alst (lambda (x y)
(< (car x) (car y))))
collect (cdr pair)))
(defun deferred:parallel-func-to-deferred (alst)
"[internal] Normalization for parallel and earlier arguments."
(cl-loop for pair in alst
for d = (cdr pair)
collect
(progn
(unless (deferred-p d)
(setf (cdr pair) (deferred:next d)))
pair)))
(defun deferred:parallel-main (alst)
"[internal] Deferred alist implementation for `deferred:parallel'. "
(deferred:message "PARALLEL<KEY . VALUE>" )
(let ((nd (deferred:new))
(len (length alst))
values)
(cl-loop for pair in
(deferred:parallel-func-to-deferred alst)
with cd ; current child deferred
do
(let ((name (car pair)))
(setq cd
(deferred:nextc (cdr pair)
(lambda (x)
(push (cons name x) values)
(deferred:message "PARALLEL VALUE [%s/%s] %s"
(length values) len (cons name x))
(when (= len (length values))
(deferred:message "PARALLEL COLLECTED")
(deferred:post-task nd 'ok (nreverse values)))
nil)))
(deferred:error cd
(lambda (e)
(push (cons name e) values)
(deferred:message "PARALLEL ERROR [%s/%s] %s"
(length values) len (cons name e))
(when (= (length values) len)
(deferred:message "PARALLEL COLLECTED")
(deferred:post-task nd 'ok (nreverse values)))
nil))))
nd))
(defun deferred:parallel-list (lst)
"[internal] Deferred list implementation for `deferred:parallel'. "
(deferred:message "PARALLEL<LIST>" )
(let* ((pd (deferred:parallel-main (deferred:parallel-array-to-alist lst)))
(rd (deferred:nextc pd 'deferred:parallel-alist-to-array)))
(setf (deferred-cancel rd)
(lambda (x) (deferred:default-cancel x)
(deferred:cancel pd)))
rd))
(defun deferred:parallel (&rest args)
"Return a deferred object that calls given deferred objects or
functions in parallel and wait for all callbacks. The following
deferred task will be called with an array of the return
values. ARGS can be a list or an alist of deferred objects or
functions."
(deferred:message "PARALLEL : %s" args)
(deferred:trans-multi-args args
'deferred:parallel 'deferred:parallel-list 'deferred:parallel-main))
(defun deferred:earlier-main (alst)
"[internal] Deferred alist implementation for `deferred:earlier'. "
(deferred:message "EARLIER<KEY . VALUE>" )
(let ((nd (deferred:new))
(len (length alst))
value results)
(cl-loop for pair in
(deferred:parallel-func-to-deferred alst)
with cd ; current child deferred
do
(let ((name (car pair)))
(setq cd
(deferred:nextc (cdr pair)
(lambda (x)
(push (cons name x) results)
(cond
((null value)
(setq value (cons name x))
(deferred:message "EARLIER VALUE %s" (cons name value))
(deferred:post-task nd 'ok value))
(t
(deferred:message "EARLIER MISS [%s/%s] %s" (length results) len (cons name value))
(when (eql (length results) len)
(deferred:message "EARLIER COLLECTED"))))
nil)))
(deferred:error cd
(lambda (e)
(push (cons name e) results)
(deferred:message "EARLIER ERROR [%s/%s] %s" (length results) len (cons name e))
(when (and (eql (length results) len) (null value))
(deferred:message "EARLIER FAILED")
(deferred:post-task nd 'ok nil))
nil))))
nd))
(defun deferred:earlier-list (lst)
"[internal] Deferred list implementation for `deferred:earlier'. "
(deferred:message "EARLIER<LIST>" )
(let* ((pd (deferred:earlier-main (deferred:parallel-array-to-alist lst)))
(rd (deferred:nextc pd (lambda (x) (cdr x)))))
(setf (deferred-cancel rd)
(lambda (x) (deferred:default-cancel x)
(deferred:cancel pd)))
rd))
(defun deferred:earlier (&rest args)
"Return a deferred object that calls given deferred objects or
functions in parallel and wait for the first callback. The
following deferred task will be called with the first return
value. ARGS can be a list or an alist of deferred objects or
functions."
(deferred:message "EARLIER : %s" args)
(deferred:trans-multi-args args
'deferred:earlier 'deferred:earlier-list 'deferred:earlier-main))
(defmacro deferred:timeout (timeout-msec timeout-form d)
"Time out macro on a deferred task D. If the deferred task D
does not complete within TIMEOUT-MSEC, this macro cancels the
deferred task and return the TIMEOUT-FORM."
`(deferred:earlier
(deferred:nextc (deferred:wait ,timeout-msec)
(lambda (x) ,timeout-form))
,d))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Application functions
(defvar deferred:uid 0 "[internal] Sequence number for some utilities. See the function `deferred:uid'.")
(defun deferred:uid ()
"[internal] Generate a sequence number."
(cl-incf deferred:uid))
(defun deferred:buffer-string (strformat buf)
"[internal] Return a string in the buffer with the given format."
(format strformat
(with-current-buffer buf (buffer-string))))
(defun deferred:process (command &rest args)
"A deferred wrapper of `start-process'. Return a deferred
object. The process name and buffer name of the argument of the
`start-process' are generated by this function automatically.
The next deferred object receives stdout and stderr string from
the command process."
(deferred:process-gen 'start-process command args))
(defun deferred:process-shell (command &rest args)
"A deferred wrapper of `start-process-shell-command'. Return a deferred
object. The process name and buffer name of the argument of the
`start-process-shell-command' are generated by this function automatically.
The next deferred object receives stdout and stderr string from
the command process."
(deferred:process-gen 'start-process-shell-command command args))
(defun deferred:process-buffer (command &rest args)
"A deferred wrapper of `start-process'. Return a deferred
object. The process name and buffer name of the argument of the
`start-process' are generated by this function automatically.
The next deferred object receives stdout and stderr buffer from
the command process."
(deferred:process-buffer-gen 'start-process command args))
(defun deferred:process-shell-buffer (command &rest args)
"A deferred wrapper of `start-process-shell-command'. Return a deferred
object. The process name and buffer name of the argument of the
`start-process-shell-command' are generated by this function automatically.
The next deferred object receives stdout and stderr buffer from
the command process."
(deferred:process-buffer-gen 'start-process-shell-command command args))
(defun deferred:process-gen (f command args)
"[internal]"
(let ((pd (deferred:process-buffer-gen f command args)) d)
(setq d (deferred:nextc pd
(lambda (buf)
(prog1
(with-current-buffer buf (buffer-string))
(kill-buffer buf)))))
(setf (deferred-cancel d)
(lambda (_x)
(deferred:default-cancel d)
(deferred:default-cancel pd)))
d))
(defun deferred:process-buffer-gen (f command args)
"[internal]"
(let ((d (deferred:next)) (uid (deferred:uid)))
(let ((proc-name (format "*deferred:*%s*:%s" command uid))
(buf-name (format " *deferred:*%s*:%s" command uid))
(pwd default-directory)
(env process-environment)
(con-type process-connection-type)
(nd (deferred:new)) proc-buf proc)
(deferred:nextc d
(lambda (_x)
(setq proc-buf (get-buffer-create buf-name))
(condition-case err
(let ((default-directory pwd)
(process-environment env)
(process-connection-type con-type))
(setq proc
(if (null (car args))
(apply f proc-name buf-name command nil)
(apply f proc-name buf-name command args)))
(set-process-sentinel
proc
(lambda (proc event)
(unless (process-live-p proc)
(if (zerop (process-exit-status proc))
(deferred:post-task nd 'ok proc-buf)
(let ((msg (format "Deferred process exited abnormally:\n command: %s\n exit status: %s %s\n event: %s\n buffer contents: %S"
command
(process-status proc)
(process-exit-status proc)
(string-trim-right event)
(if (buffer-live-p proc-buf)
(with-current-buffer proc-buf
(buffer-string))
"(unavailable)"))))
(kill-buffer proc-buf)
(deferred:post-task nd 'ng msg))))))
(setf (deferred-cancel nd)
(lambda (x) (deferred:default-cancel x)
(when proc
(kill-process proc)
(kill-buffer proc-buf)))))
(error (deferred:post-task nd 'ng err)))
nil))
nd)))
(defmacro deferred:processc (d command &rest args)
"Process chain of `deferred:process'."
`(deferred:nextc ,d
(lambda (,(cl-gensym)) (deferred:process ,command ,@args))))
(defmacro deferred:process-bufferc (d command &rest args)
"Process chain of `deferred:process-buffer'."
`(deferred:nextc ,d
(lambda (,(cl-gensym)) (deferred:process-buffer ,command ,@args))))
(defmacro deferred:process-shellc (d command &rest args)
"Process chain of `deferred:process'."
`(deferred:nextc ,d
(lambda (,(cl-gensym)) (deferred:process-shell ,command ,@args))))
(defmacro deferred:process-shell-bufferc (d command &rest args)
"Process chain of `deferred:process-buffer'."
`(deferred:nextc ,d
(lambda (,(cl-gensym)) (deferred:process-shell-buffer ,command ,@args))))
;; Special variables defined in url-vars.el.
(defvar url-request-data)
(defvar url-request-method)
(defvar url-request-extra-headers)
(declare-function url-http-symbol-value-in-buffer "url-http"
(symbol buffer &optional unbound-value))
(declare-function deferred:url-param-serialize "request" (params))
(declare-function deferred:url-escape "request" (val))
(eval-after-load "url"
;; for url package
;; TODO: proxy, charaset
;; List of gloabl variables to preserve and restore before url-retrieve call
'(let ((url-global-variables '(url-request-data
url-request-method
url-request-extra-headers)))
(defun deferred:url-retrieve (url &optional cbargs silent inhibit-cookies)
"A wrapper function for url-retrieve. The next deferred
object receives the buffer object that URL will load
into. Values of dynamically bound 'url-request-data', 'url-request-method' and
'url-request-extra-headers' are passed to url-retrieve call."
(let ((nd (deferred:new))
buf
(local-values (mapcar (lambda (symbol) (symbol-value symbol)) url-global-variables)))
(deferred:next
(lambda (_x)
(cl-progv url-global-variables local-values
(condition-case err
(setq buf
(url-retrieve
url (lambda (_xx) (deferred:post-task nd 'ok buf))
cbargs silent inhibit-cookies))
(error (deferred:post-task nd 'ng err)))
nil)))
(setf (deferred-cancel nd)
(lambda (_x)
(when (buffer-live-p buf)
(kill-buffer buf))))
nd))
(defun deferred:url-delete-header (buf)
(with-current-buffer buf
(let ((pos (url-http-symbol-value-in-buffer
'url-http-end-of-headers buf)))
(when pos
(delete-region (point-min) (1+ pos)))))
buf)
(defun deferred:url-delete-buffer (buf)
(when (and buf (buffer-live-p buf))
(kill-buffer buf))
nil)
(defun deferred:url-get (url &optional params &rest args)
"Perform a HTTP GET method with `url-retrieve'. PARAMS is
a parameter list of (key . value) or key. ARGS will be appended
to deferred:url-retrieve args list. The next deferred
object receives the buffer object that URL will load into."
(when params
(setq url
(concat url "?" (deferred:url-param-serialize params))))
(let ((d (deferred:$
(apply 'deferred:url-retrieve url args)
(deferred:nextc it 'deferred:url-delete-header))))
(deferred:set-next
d (deferred:new 'deferred:url-delete-buffer))
d))
(defun deferred:url-post (url &optional params &rest args)
"Perform a HTTP POST method with `url-retrieve'. PARAMS is
a parameter list of (key . value) or key. ARGS will be appended
to deferred:url-retrieve args list. The next deferred
object receives the buffer object that URL will load into."
(let ((url-request-method "POST")
(url-request-extra-headers
(append url-request-extra-headers
'(("Content-Type" . "application/x-www-form-urlencoded"))))
(url-request-data (deferred:url-param-serialize params)))
(let ((d (deferred:$
(apply 'deferred:url-retrieve url args)
(deferred:nextc it 'deferred:url-delete-header))))
(deferred:set-next
d (deferred:new 'deferred:url-delete-buffer))
d)))
(defun deferred:url-escape (val)
"[internal] Return a new string that is VAL URI-encoded."
(unless (stringp val)
(setq val (format "%s" val)))
(url-hexify-string
(encode-coding-string val 'utf-8)))
(defun deferred:url-param-serialize (params)
"[internal] Serialize a list of (key . value) cons cells
into a query string."
(when params
(mapconcat
'identity
(cl-loop for p in params
collect
(cond
((consp p)
(concat
(deferred:url-escape (car p)) "="
(deferred:url-escape (cdr p))))
(t
(deferred:url-escape p))))
"&")))
))
(provide 'deferred)
;;; deferred.el ends here

View File

@@ -0,0 +1,6 @@
*.gif filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
core/aria2-ng/index.html linguist-vendored
core/js/dark_mode.js linguist-vendored
core/js/caret_browsing.js linguist-vendored
app/mindmap/*.js linguist-vendored

View File

@@ -0,0 +1,28 @@
---
name: EAF Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Ensure you're on the latest master branch, then note the steps to reproduce the behavior.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Versions (please complete the following info):**
- Distro and DE/WM:
- Versions of Dependencies:
- M-x emacs-version:
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.

View File

@@ -0,0 +1,20 @@
---
name: EAF Feature request
about: Suggest an idea for the Emacs Application Framework
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of an alternative solutions or features you've considered, if any.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@@ -0,0 +1,11 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "npm" # See https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates#package-ecosystem
directory: "/" # Location of package manifests
schedule:
interval: "daily"

8
lisp/emacs-application-framework/.gitignore vendored Executable file
View File

@@ -0,0 +1,8 @@
*.elc
*.pyc
/.log/
/app/js-video-player/node_modules/
/app/mermaid/node_modules/
/app/terminal/node_modules/
/app/image-viewer/node_modules/
/app/markdown-previewer/node_modules/

View File

@@ -6,7 +6,7 @@ EAF is a GUI application framework that revolutionizes Emacs graphical capabilit
## EAF Application Overview
EAF is an extensible framework, one can develop any Qt5 application and integrate it into Emacs.
| Browser | Markdown Previewer |
| Browser | Markdown Previewer (support Mermaid and PlantUML) |
| :--------: | :----: |
| <img src="./screenshot/browser.gif" width="400"> | <img src="./screenshot/markdown_previewer.gif" width="400"> |
@@ -31,7 +31,7 @@ EAF is an extensible framework, one can develop any Qt5 application and integrat
| <img src="./screenshot/air_share.png" width="400"> | <img src="./screenshot/org_previewer.gif" width="400"> |
| | |
| Terminal Emulator | RSS Reader |
| Terminal Emulator | RSS Reader (Integrated with Elfeed) |
| :--------: | :------: |
| <img src="./screenshot/terminal.gif" width="400"> | <img src="./screenshot/rss_reader.gif" width="400"> |
| | |
@@ -42,92 +42,101 @@ EAF is an extensible framework, one can develop any Qt5 application and integrat
| | |
| Mermaid | EAF Interleave |
| Jupyter | EAF Interleave |
| :--------: | :----------: |
| <img src="./screenshot/mermaid.gif" width="400"> | <img src="./screenshot/eaf-interleave.gif" width="400"> |
| <img src="./screenshot/jupyter.png" width="400"> | <img src="./screenshot/eaf-interleave.gif" width="400"> |
| | |
## EmacsConf2020 - Extend Emacs to Modern GUI Applications with EAF
- EAF talk & demo at EmacsConf2020, presented to you by [MatthewZMD](https://github.com/MatthewZMD)
- Homepage (Q&A included): https://emacsconf.org/2020/talks/34/
- Youtube link:
[![EmacsConf2020 - Extend Emacs to Modern GUI Applications with EAF](https://img.youtube.com/vi/HK_f8KTuR0s/0.jpg)](https://www.youtube.com/watch?v=HK_f8KTuR0s)
## Install
1. Install EAF dependencies, an explaination of each dependency can be found in [Dependency List](#dependency-list).
The following line is for *Archlinux*, same packages should be available to install on your particular distribution:
1. Download EAF
```Bash
yay -S python-pyqt5 python-pyqt5-sip python-pyqtwebengine python-qrcode python-feedparser python-dbus python-pyinotify python-markdown nodejs aria2 libreoffice python-pymupdf python-grip filebrowser-bin
git clone --depth=1 -b master https://github.com/manateelazycat/emacs-application-framework.git ~/.emacs.d/site-lisp/emacs-application-framework/
```
2. Clone this repository.
```Bash
git clone https://github.com/manateelazycat/emacs-application-framework.git --depth=1
Alternatively, you can use a [Quelpa recipe](https://github.com/quelpa/quelpa)
```Emacs-lisp
(quelpa '(eaf (:fetcher github
:repo "manateelazycat/emacs-application-framework"
:files ("*"))))
```
3. Add the full path to the EAF installation directory to your Emacs ```load-path```, then add the following to `init.el`:
2. Install EAF dependencies using `M-x install-eaf-dependencies`.
If you prefer to manually call the installation script in the terminal,
- If you use GNU/Linux,
```Bash
cd emacs-application-framework
chmod +x ./install-eaf.sh
./install-eaf.sh
```
- If you use Windows,
```shell
cd emacs-application-framework
node ./install-eaf-win32.js
```
Feel free to inspect the install script yourself. An explanation of each dependency can be found at [Dependency List](#dependency-list).
3. Install Elisp packages:
- [emacs-ctable](https://github.com/kiwanami/emacs-ctable)
- [emacs-deferred](https://github.com/kiwanami/emacs-deferred)
- [emacs-epc](https://github.com/kiwanami/emacs-epc)
- [s.el](https://github.com/magnars/s.el)
4. From here on, you can either add the full path to the EAF installation directory to your Emacs ```load-path```, then add the following to `init.el`:
```Elisp
(add-to-list 'load-path "~/.emacs.d/site-lisp/emacs-application-framework/")
(require 'eaf)
```
If you use [use-package](https://github.com/jwiegley/use-package), a sample configuration has been provided.
or, if you use [use-package](https://github.com/jwiegley/use-package), you can use the following *sample* configuration for your convenience.
```Elisp
(use-package eaf
:load-path "~/.emacs.d/site-lisp/emacs-application-framework" ; Set to "/usr/share/emacs/site-lisp/eaf" if installed from AUR
:init
(use-package epc :defer t)
(use-package ctable :defer t)
(use-package deferred :defer t)
(use-package s :defer t :ensure t)
:custom
(eaf-find-alternate-file-in-dired t)
(eaf-browser-continue-where-left-off t)
:config
(eaf-setq eaf-browser-enable-adblocker "true")
(eaf-bind-key scroll_up "C-n" eaf-pdf-viewer-keybinding)
(eaf-bind-key scroll_down "C-p" eaf-pdf-viewer-keybinding)
(eaf-bind-key take_photo "p" eaf-camera-keybinding))
(eaf-bind-key take_photo "p" eaf-camera-keybinding)
(eaf-bind-key nil "M-q" eaf-browser-keybinding)) ;; unbind, see more in the Wiki
```
---
If you use [Chemacs](https://github.com/plexus/chemacs), a sample `init.el` is provided as follows:
```Elisp
;; An example of init.el
(defun add-subdirs-to-load-path (dir)
"Recursive add directories to `load-path'."
(let ((default-directory (file-name-as-directory dir)))
(add-to-list 'load-path dir)
(normal-top-level-add-subdirs-to-load-path)))
(add-subdirs-to-load-path "~/.emacs.d/")
(require 'eaf)
```
During EAF installation, you only need to modify the parameter value when calling function `add-subdirs-to-load-path`, change it into your full path to the EAF installation directory. The sample gives a situation when you install EAF at `~/.emacs.d/`.
After EAF installation, let's assume your EAF installation directory is `~/eaf`, you only have Emacs configurations at `~/.emacs.d` and EAF installed on your computer, and you have installed Chemacs, then the `.emacs-profiles.el` should look like this:
```Elisp
(("default" . ((user-emacs-directory . "~/.emacs.d")))
("EAF" . ((user-emacs-directory . "~/eaf"))))
```
When you want to use EAF by chemacs, you can excute `emacs --with-profile EAF` in the terminal.
### Dependency List
Packages listed as **Core** are mandatory for EAF to work, whereas other packages are optional - install if you want to use corresponding EAF Application.
| Package | Dependent | Description |
| :-------- | :------ | :------ |
| python-pyqt5, python-pyqt5-sip | Core | Essential GUI library |
| python-dbus | Core | DBus IPC to connect Python with Elisp |
| python-pyqtwebengine | Core | Chromium based web rendering engine |
| wmctrl | Core | Activate Emacs window input focus |
| python-pymupdf | PDF Viewer | PDF rendering engine |
| python-grip | Markdown Previewer | Markdown rendering server |
| python-qrcode | File Sender, File Receiver, Airshare | Render QR code pointing to local files |
| python-feedparser | RSS Reader | Parse RSS feeds |
| python-pyinotify | Mermaid | Monitor *.mmd file change status |
| python-markdown | Mermaid | Covert markdown format to mermaid html format |
| nodejs | Terminal | Communicate between browser and local TTY |
| aria2 | Browser | Download files from the web |
| libreoffice | Doc Viewer | Convert doc file to pdf |
| filebrowser-bin | File Browser | Share files between computer and smartphone |
| qtconsole | Jupyter | Provide RichJupyterWidget |
| java-openjdk | Markdown Previewer | Make mume.js can render PlantUML content |
## Launch EAF Applications
| Application Name | Launch |
@@ -145,62 +154,59 @@ Packages listed as **Core** are mandatory for EAF to work, whereas other package
| File Sender | `M-x eaf-file-sender-qrcode` or `eaf-file-sender-qrcode-in-dired` |
| File Browser | `M-x eaf-file-browser-qrcode` |
| Airshare | `M-x eaf-open-airshare` |
| RSS Reader | `M-x eaf-open-rss-reader` |
| Mindmap | `M-x eaf-create-mindmap` or `M-x eaf-open-mindmap` |
| MS Office Viewer | `M-x eaf-open-office` |
| Mermaid | `M-x eaf-open` Mermaid file (*.mmd) |
| Jupyter | `M-x eaf-open-jupyter` |
| Demo | `M-x eaf-open-demo` to verify basic functionality |
- To open the file under the cursor in `dired` using appropriate EAF Application, use `eaf-open-this-from-dired` instead.
- EAF Browser and PDF Viewer support Emacs built-in bookmark operation, with `M-x bookmark-set` (defaulted to `C-x r m`) and `M-x bookmark-bmenu-list` (defaulted to `C-x r l`).
```
NOTE:
EAF use DBus' session bus, it must run by a general user.
Please don't use EAF when Emacs is started with sudo or root user, a root user can only access DBus's system bus.
```
## Wiki
It is **highly** suggested to read the [Wiki](https://github.com/manateelazycat/emacs-application-framework/wiki) first before using EAF.
Wiki consists of documentations on Keybinding, Customization, Design and TODOLIST. There also are some helpful tips to make EAF work with Docker, Helm, etc.
Wiki consists of documentations on:
* Keybindings
* Customization
* Design
* TODOLIST
There also are some helpful tips to make EAF work with Docker, Helm, etc.
## FAQ
### How does EAF work?
EAF implements three major functionalities:
1. Integrate PyQt program window into Emacs frame using QWindow Reparent technology.
2. Listen to EAF buffer's keyboard event flow and control the keyboard input of PyQt program via DBus IPC.
2. Listen to EAF buffer's keyboard event flow and control the keyboard input of PyQt program via Python EPC.
3. Create a window compositer to make a PyQt program window adapt Emacs's Window/Buffer design.
Learn more from the [Wiki](https://github.com/manateelazycat/emacs-application-framework/wiki/Hacking)!
### EAF vs EXWM?
1. EAF and EXWM share a common goal: enhance collaboration between the standard GNU Emacs with other GUI tools.
2. EXWM is an X11 Window Manager, it manages and controls other software using a keyboard, but it cannot modify, customize and extend the behavior of other software. For example, it cannot modify the behavior when you press a key in Chrome or a PDF viewer.
2. EXWM is an X11 Window Manager, it manages and controls other software using a keyboard, but it cannot modify, customize and extend the behavior of other software.
3. EAF is *not* a Window Manager, it utilizes the windows managing capabilities of Emacs to display its applications.
4. The intention of EAF is to provide a framework to write PyQt5 applications that extends the multimedia experience of Emacs. From the perspective of Emacs' buffer/mode design, EAF is not different from any other package, with the former uses Qt for drawing contents while the latter uses Emacs' built-in text libraries instead.
5. Through EAF's design, one can use Elisp to control Python and vice versa, and even able to use Elisp to control JavaScript in EAF Browser. EAF enables Emacs to the world of **multi-language scripting**.
Both projects are similar in terms of interface, but they are two completely different projects with different goals in mind. Sometimes one may find EAF is more suitable than EXWM, sometimes it's the other way around. Please do not meaninglessly compare them.
### EAF is (currently) Linux only. Why?
1. DBus is Linux-specific technology, it's difficult to support DBus in other operating systems.
2. Qt5's QGraphicsScene technology does not work on MacOS.
### EAF can't works on MacOS. Why?
1. Qt5's QGraphicsScene technology does not work on MacOS.
2. QWindow Reparent technology need use the original API of Mac platform for replacement.
If you've figure them out, PRs are always welcome!
### `[EAF] *eaf* aborted (core dumped)` error
Please check the `*eaf*` buffer, something is wrong on the Python side. Usually due to Python dependencies are not installed correctly.
### EAF js-video-player can't play some video on Windows. Why?
`js-video-player` requires that qtwebengine built against ffmpeg to support proprietary codecs like `h264/aac`.
If you're sure Python dependences are installed correctly, please create an issue with the `*eaf*` buffer content, it contains many clues that can help us locate the problem faster.
### Why doesn't EAF receive input events on WM?
EAF confirms that the desktop environment or window manager you can work includes: KDE, GNOME2, GNOME3, Mate, Xfce, LXDE, I3, QTILE, Xpra.
### What is Github Personal Access Tokens?
If you use EAF Markdown Previewer, to get consistent previewing, you need to access [Github Personal access token site](https://github.com/settings/tokens/new?scopes=), fill something in "Token description" and click button "Generate token" to get your personal token. Then set the token:
We suspect there are some issues with how all the Window Managers implement their x11 protocols.
```Elisp
(setq eaf-grip-token "yourtokencode")
```
Although Markdown Previewer works for the first few times by entering empty string when prompted, eventually it stops working and gives "GitHub Rate Limit Reached" error.
One workaround is to name of command `wmctrl -m` to the elisp list `eaf-wm-focus-fix-wms`. Fill an issue if it still doesn't work.
### Proxy
If you need to use proxy to access internet, one can configure the proxy settings.
@@ -227,11 +233,13 @@ If we missed your package, please make a PR to add it to the list.
## Report bug
For any installation and configuration assistance, please read the [Wiki](https://github.com/manateelazycat/emacs-application-framework/wiki) first!
### For any installation and configuration assistance, please read the [Wiki](https://github.com/manateelazycat/emacs-application-framework/wiki) first!
If you encounter any problem with EAF, please use command `emacs -q` with a minimal setup that only contains EAF and verify the bug is reproducible. If `emacs -q` works fine, probably something is wrong with your Emacs config.
If you encounter a problem with EAF, and it occured after pulling the latest commit, please check the [Discussions](https://github.com/manateelazycat/emacs-application-framework/discussions/527) page first.
If the problem persists, please [report bug here](https://github.com/manateelazycat/emacs-application-framework/issues/new).
For any other problems, please use `emacs -q` and load a minimal setup with only EAF to verify that the bug is reproducible. If `emacs -q` works fine, probably something is wrong with your Emacs config.
If the problem persists, please report it [here](https://github.com/manateelazycat/emacs-application-framework/issues/new) with `*eaf*` buffer content, it contains many clues that can help us locate the problem faster.
If you got segfault error, please use the following way to collect crash information:
1. Install gdb and turn on option `eaf-enable-debug`

View File

@@ -6,7 +6,7 @@ EAF 是一个全新的图形应用框架通过扩展Emacs的多媒体能力
## EAF 应用展示
EAF是一个可编程扩展的框架你可以开发自己的Qt5应用并集成在Emacs中。
| 浏览器 | Markdown预览程序 |
| 浏览器 | Markdown预览(支持Mermaid和PlantUML) |
| :--------: | :----: |
| <img src="./screenshot/browser.gif" width="400"> | <img src="./screenshot/markdown_previewer.gif" width="400"> |
@@ -31,7 +31,7 @@ EAF是一个可编程扩展的框架你可以开发自己的Qt5应用并集
| <img src="./screenshot/air_share.png" width="400"> | <img src="./screenshot/org_previewer.gif" width="400"> |
| | |
| 终端模拟器 | RSS阅读器 |
| 终端模拟器 | RSS阅读器 (集成Elfeed) |
| :--------: | :------: |
| <img src="./screenshot/terminal.gif" width="400"> | <img src="./screenshot/rss_reader.gif" width="400"> |
| | |
@@ -41,92 +41,102 @@ EAF是一个可编程扩展的框架你可以开发自己的Qt5应用并集
| <img src="./screenshot/aria2.gif" width="400"> | <img src="./screenshot/mindmap.gif" width="400"> |
| | |
| 流程图 | 笔记管理系统 |
| Jupyter | 笔记管理系统 |
| :--------: | :--------: |
| <img src="./screenshot/mermaid.gif" width="400"> | <img src="./screenshot/eaf-interleave.gif" width="400"> |
| <img src="./screenshot/jupyter.png" width="400"> | <img src="./screenshot/eaf-interleave.gif" width="400"> |
| | |
## EmacsConf2020 - Extend Emacs to Modern GUI Applications with EAF暂无中文字幕
- 由[MatthewZMD](https://github.com/MatthewZMD)在EmacsConf2020的演讲和demo。
- 官网内附Q&A: https://emacsconf.org/2020/talks/34/
- Youtube链接:
[![EmacsConf2020 - Extend Emacs to Modern GUI Applications with EAF](https://img.youtube.com/vi/HK_f8KTuR0s/0.jpg)](https://www.youtube.com/watch?v=HK_f8KTuR0s)
## 安装
1. 安装EAF依赖,对于每个依赖的解释可以在[依赖列表](#依赖列表)找到。
以下这行是针对*Archlinux*,同样的包的安装方式在别的发行版略有不同,请善用搜索引擎:
1. 下载EAF:
```Bash
yay -S python-pyqt5 python-pyqt5-sip python-pyqtwebengine python-qrcode python-feedparser python-dbus python-pyinotify python-markdown nodejs aria2 libreoffice python-pymupdf python-grip filebrowser-bin
git clone --depth=1 -b master https://github.com/manateelazycat/emacs-application-framework.git ~/.emacs.d/site-lisp/emacs-application-framework/
```
2. 使用 ```git clone``` 下载这个仓库.
```Bash
git clone https://github.com/manateelazycat/emacs-application-framework.git --depth=1
你也可以通过[Quelpa](https://github.com/quelpa/quelpa)来下载
```Emacs-lisp
(quelpa '(eaf (:fetcher github
:repo "manateelazycat/emacs-application-framework"
:files ("*"))))
```
3. 把EAF加入Emacs的 ```load-path```,然后在 `init.el` 中写入:
2. 通过`M-x install-eaf-dependencies`安装EAF依赖
如果你更喜欢手动执行安装脚本,
- GNU/Linux用户
```Bash
cd emacs-application-framework
chmod +x ./install-eaf.sh
./install-eaf.sh
```
- Windows用户
```shell
>>>>>>> Update README and eaf-install-dependencies (need Windows testing)
cd emacs-application-framework
node ./install-eaf-win32.js
```
脚本里安装的每一个依赖的解释可以在[依赖列表](#依赖列表)找到。
3. 安装Elisp依赖包:
- [emacs-ctable](https://github.com/kiwanami/emacs-ctable)
- [emacs-deferred](https://github.com/kiwanami/emacs-deferred)
- [emacs-epc](https://github.com/kiwanami/emacs-epc)
- [s.el](https://github.com/magnars/s.el)
4. 从这里开始你可以把EAF加入Emacs的 ```load-path```,然后在 `init.el` 中写入:
```Elisp
(add-to-list 'load-path "~/.emacs.d/site-lisp/emacs-application-framework/")
(require 'eaf)
```
如果你使用[use-package](https://github.com/jwiegley/use-package),下面有一个简单的配置文件供你参考:
或者,如果你使用[use-package](https://github.com/jwiegley/use-package),下面有一个简单的配置文件供你参考:
```Elisp
(use-package eaf
:load-path "~/.emacs.d/site-lisp/emacs-application-framework" ; Set to "/usr/share/emacs/site-lisp/eaf" if installed from AUR
:init
(use-package epc :defer t)
(use-package ctable :defer t)
(use-package deferred :defer t)
(use-package s :defer t :ensure t)
:custom
(eaf-find-alternate-file-in-dired t)
(eaf-browser-continue-where-left-off t)
:config
(eaf-setq eaf-browser-enable-adblocker "true")
(eaf-bind-key scroll_up "C-n" eaf-pdf-viewer-keybinding)
(eaf-bind-key scroll_down "C-p" eaf-pdf-viewer-keybinding)
(eaf-bind-key take_photo "p" eaf-camera-keybinding))
(eaf-bind-key take_photo "p" eaf-camera-keybinding)
(eaf-bind-key nil "M-q" eaf-browser-keybinding)) ;; unbind, see more in the Wiki
```
---
如果你使用[Chemacs](https://github.com/plexus/chemacs) 下面是一份 `init.el`的配置示例:
```Elisp
;; An example of init.el
(defun add-subdirs-to-load-path (dir)
"Recursive add directories to `load-path'."
(let ((default-directory (file-name-as-directory dir)))
(add-to-list 'load-path dir)
(normal-top-level-add-subdirs-to-load-path)))
(add-subdirs-to-load-path "~/.emacs.d/")
(require 'eaf)
```
在安装EAF时你仅需要修改此文件中调用函数`add-subdirs-to-load-path`的参数值将其更改为你的EAF安装位置的绝对路径。 配置示例中的EAF是安装在`~/.emacs.d/`.
在EAF安装完成后假设你将它安装到了`~/eaf`,你电脑中只有存放在`~/.emacs.d`的Emacs配置文件和安装的EAF并且你电脑中安装了Chemacs此时`.emacs-profiles.el`应该这样配置:
```Elisp
(("default" . ((user-emacs-directory . "~/.emacs.d")))
("EAF" . ((user-emacs-directory . "~/eaf"))))
```
当你想使用Chemacs打开EAF只需在终端中执行`emacs --with-profile EAF`
### 依赖列表
**核心** 分类代表必备依赖这些包必须安装好EAF才能工作。其余依赖都可选若想其使用对应的应用时你才需要安装这些依赖。当然我们推荐先把所有依赖都安装好等到真正使用的时候就不用再次折腾。
| 包名 | 依赖 | 解释 |
| :-------- | :------ | :------ |
| python-pyqt5, python-pyqt5-sip | 核心 | GUI图形库 |
| python-dbus | 核心 | DBus库用于在Emacs和Python进程间通讯 |
| python-pyqtwebengine | 核心 | 基于Chromium的浏览器引擎 |
| wmctrl | 核心 | 激活Emacs窗口输入焦点 |
| python-pymupdf | PDF阅读器 | 解析PDF文件 |
| python-grip | Markdown预览 | 建立Markdown文件的HTML服务 |
| python-qrcode | 文件上传,文件下载,文字传输 | 根据文件信息生成二维码 |
| python-feedparser | RSS阅读器 | 解析RSS/Atom信息 |
| python-pyinotify | 流程图 | 监听 mmd 格式文件的变动 |
| python-markdown | 流程图 | 转换 mmd 格式为 mermaid 识别的 html 格式 |
| aria2 | 浏览器 | 下载网络文件 |
| nodejs | 终端模拟器 | 通过浏览器与本地TTY交互 |
| libreoffice | 办公文档阅读器 | 转换doc文件为pdf格式 |
| filebrowser-bin | 文件浏览器 | 在电脑和手机之间快速共享文件 |
| qtconsole | jupyter | 提供RichJupyterWidget |
| java-openjdk | Markdown 预览 | 正常渲染Markdown文档中的PlantUML代码 |
## EAF应用启动命令
| 应用名称 | 启动命令 |
@@ -144,36 +154,37 @@ git clone https://github.com/manateelazycat/emacs-application-framework.git --de
| 二维码下载文件 | `M-x eaf-file-sender-qrcode` or `eaf-file-sender-qrcode-in-dired` |
| 二维码在线浏览器 | `M-x eaf-file-browser-qrcode` |
| 无线分享 | `M-x eaf-open-airshare` 输入要分享给手机的字符串 |
| RSS新闻阅读器 | `M-x eaf-open-rss-reader` |
| 思维导图 | `M-x eaf-create-mindmap` or `M-x eaf-open-mindmap` |
| 微软Office阅读器 | `M-x eaf-open-office` |
| 流程图 | `M-x eaf-open` 输入 mmd 格式文件 |
| jupyter | `M-x eaf-open-jupyter` |
| 演示程序 | `M-x eaf-open-demo` |
-`dired`文件管理器中,建议绑定按键到命令 `eaf-open-this-from-dired` 它会自动用合适的EAF应用来打开文件。
- EAF浏览器以及PDF浏览器支持Emacs内置书签操作通过使用`M-x bookmark-set`(默认`C-x r m`)以及`M-x bookmark-bmenu-list`(默认`C-x r l`)。
```
注意:
EAF使用DBus的普通权限总线 (session bus),请不要用 sudo 来启动EAFroot用户只能访问系统权限总线 (system bus)
```
## Wiki
强烈建议使用EAF之前浏览一遍[Wiki](https://github.com/manateelazycat/emacs-application-framework/wiki)。
Wiki包括架构设计按键绑定自定义选项和任务列表等文档。你还会在Wiki发现很多有用的技巧比如DockerHelm等
Wiki囊括了各种你想了解的EAF相关文档包括了
1. 按键绑定
2. 自定义选项
3. 架构设计
4. 任务列表
你还会在Wiki发现很多有用的技巧如DockerHelm等
## 常用问题
### EAF是怎么工作的
EAF主要实现这几个功能
1. 利用QWindow的Reparent技术来实现PyQt应用进程的窗口粘贴到Emacs对应的Buffer区域
2. 通过DBus IPC来实现Emacs进程和Python进程的控制指令和跨进程消息通讯
2. 通过Python EPC来实现Emacs进程和Python进程的控制指令和跨进程消息通讯
3. 通过Qt5的QGraphicsScene来实现镜像窗口以对应Emacs的Buffer/Window模型
若想了解更多EAF设计背景请看[Wiki](https://github.com/manateelazycat/emacs-application-framework/wiki/Hacking)
### EAF vs EXWM?
1. EAF和EXWM的共同点都是“提升Emacs和别的程序的协作效率“
2. EXWM是一个X11窗口管理器通过X11协议来控制Emacs和其他程序但是EXWM只是管理其他程序但是它并不会修改别的程序。比如它没法修改ChromePDF阅读器等GUI程序内在行为
2. EXWM是一个X11窗口管理器通过X11协议来控制Emacs和其他程序但是EXWM只是管理其他程序但是它并不会修改应用程序内在行为
3. EAF不是一个窗口管理器EAF只是依赖Emacs自身的窗口管理功能显示自己
4. EAF的目标是通过PyQt创造新的应用来扩展Emacs的多媒体能力。从Emacs本身的Buffer/Mode设计上看它和你平常用的 `xx-mode` 插件没有啥区别只是它用Qt来绘制内容而不是Emacs自身的文本库来绘制内容
5. EAF通过造轮子的方式把大多数程序员常用的应用写出来以后达到Live in Emacs的最终目标
@@ -181,23 +192,19 @@ EAF主要实现这几个功能
或许EAF和EXWM看起来有点相似但它们在设计和理念上是两个完全不同的项目。所以请大家多多学习X11和Qt的区别理解技术的本质避免无意义的比较和争论。
### 为什么EAF只能在Linux下工作?
1. DBus是Linux下专用的进程间通讯技术其他操作系统可能无法支持DBus
2. Qt5的QGraphicsScene技术无法在MacOS下正常工作也就无法实现Qt5应用的镜像窗口以支持Emacs的Buffer/Window模型
### 为什么EAF无法在MacOS下工作?
1. Qt5的QGraphicsScene技术无法在MacOS下正常工作也就无法实现Qt5应用的镜像窗口以支持Emacs的Buffer/Window模型
2. QWindow Reparent技术无法在MacOS下正常工作也就无法实现Qt应用进程的窗口粘贴到Emacs对应的Buffer区域
欢迎操作系统级别黑客移植EAF目前为止我知道的主要的迁移障碍就只有两个DBusQGraphicsScene
### 为什么EAF的js-video-player在Windows下无法播放视频文件
`js-video-player`需要qtwebengine编译时链接到ffmpeg才能支持额外的编码如`h264/aac`.
### `[EAF] *eaf* aborted (core dumped)` 奔溃了怎么办
请检查 `*eaf*` 这个窗口的内容。通常是EAF的Python依赖没有安装好如果你确定依赖没有问题请附带 `*eaf*` 窗口的内容给我们提交issue那里面有很多线索可以帮助我们排查问题。
### 为什么通过窗口管理器使用EAF无法接收输入信息
EAF确认可以工作的桌面环境或者窗口管理器包括KDE、Gnome2、Gnome3、Mate、XFce、LXDE、i3、QTile、Xpra.
### Github 个人访问标记干什么用的?
Markdown预览程序依赖grip你需要访问[Github Personal access token](https://github.com/settings/tokens/new?scopes=)去获取你个人的标记然后通过下面的命令设置标记后grip才能正常的工作
我们认为不同的窗口管理器对于X11协议的支持不够完善才导致这样的问题。
```Elisp
(setq eaf-grip-token "yourtokencode")
```
尽管不设置访问标记一开始也能成功使用但Github过段时间会弹出 "GitHub Rate Limit Reached" 的错误。
现在的解决方案是将命令`wmctrl -m`中Name的值加入`eaf-wm-focus-fix-wms`如果还有问题请在Github提出issue。
### 代理
可以通过下面设置来通过代理访问互联网:
@@ -224,9 +231,11 @@ Markdown预览程序依赖grip你需要访问[Github Personal access token](h
### 反馈安装和配置问题之前,请一定先阅读[Wiki](https://github.com/manateelazycat/emacs-application-framework/wiki)!!!
如果你遇到任何问题,请先用命令 `emacs -q` 并只添加EAF配置做一个对比测试如果 `emacs -q` 的时候可以工作,请检查你个人的配置文件
如果你使用中遇到任何问题,并且问题是`git pull`后出现的,请先阅读[Discussions](https://github.com/manateelazycat/emacs-application-framework/discussions/527)页面
如果```emacs -q```环境下问题依旧,请到[这里](https://github.com/manateelazycat/emacs-application-framework/issues/new)反馈
关于其他问题,请用命令 `emacs -q` 并只添加EAF配置做一个对比测试如果 `emacs -q` 可以正常工作,请检查你个人的配置文件
如果`emacs -q`环境下问题依旧,请到[这里](https://github.com/manateelazycat/emacs-application-framework/issues/new)反馈, 并附带 `*eaf*` 窗口的内容给我们提交issue那里面有很多线索可以帮助我们排查问题。。
如果你遇到崩溃的问题, 请用下面的方式来收集崩溃信息:
1. 先安装gdb并打开选项 `eaf-enable-debug`

View File

@@ -49,12 +49,18 @@ class AppBuffer(BrowserBuffer):
self.buffer_widget.translate_selected_text.connect(self.translate_text)
self.buffer_widget.open_url_in_new_tab.connect(self.open_url_in_new_tab)
self.buffer_widget.duplicate_page_in_new_tab.connect(self.duplicate_page_in_new_tab)
self.buffer_widget.open_url_in_background_tab.connect(self.open_url_in_background_tab)
# Reset to default zoom when page init or url changed.
self.reset_default_zoom()
self.buffer_widget.urlChanged.connect(self.set_adblocker)
self.buffer_widget.urlChanged.connect(self.caret_exit)
# Record url when url changed.
self.buffer_widget.urlChanged.connect(self.update_url)
def update_url(self, url):
self.reset_default_zoom()
self.url = self.buffer_widget.url().toString()
def set_adblocker(self, url):
if self.emacs_var_dict["eaf-browser-enable-adblocker"] == "true" and not self.page_closed:
self.buffer_widget.load_adblocker()

View File

@@ -24,7 +24,7 @@ from PyQt5.QtGui import QBrush
from PyQt5.QtGui import QColor
from PyQt5.QtMultimedia import QCameraInfo, QCamera, QCameraImageCapture
from PyQt5.QtMultimediaWidgets import QGraphicsVideoItem
from PyQt5.QtWidgets import QGraphicsScene, QGraphicsView
from PyQt5.QtWidgets import QGraphicsScene, QGraphicsView, QFrame
from PyQt5.QtWidgets import QWidget, QVBoxLayout
from core.buffer import Buffer
from pathlib import Path
@@ -34,6 +34,9 @@ import os
class AppBuffer(Buffer):
def __init__(self, buffer_id, url, config_dir, arguments, emacs_var_dict, module_path):
Buffer.__init__(self, buffer_id, url, arguments, emacs_var_dict, module_path, True)
self.background_color = QColor(0, 0, 0)
self.add_widget(CameraWidget(QColor(0, 0, 0, 255)))
def all_views_hide(self):
@@ -46,9 +49,12 @@ class AppBuffer(Buffer):
def take_photo(self):
if os.path.exists(os.path.expanduser(self.emacs_var_dict["eaf-camera-save-path"])):
self.buffer_widget.take_photo(self.emacs_var_dict["eaf-camera-save-path"])
location = self.emacs_var_dict["eaf-camera-save-path"]
else:
self.buffer_widget.take_photo("~/Downloads")
location = "~/Downloads"
result = self.buffer_widget.take_photo(location)
if result:
self.message_to_emacs.emit("Captured Photo at " + location)
def destroy_buffer(self):
self.buffer_widget.stop_camera()
@@ -65,13 +71,13 @@ class CameraWidget(QWidget):
self.graphics_view = QGraphicsView(self.scene)
self.graphics_view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.graphics_view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.graphics_view.setFrameStyle(0)
self.graphics_view.setStyleSheet("QGraphicsView {background: transparent; border: 3px; outline: none;}")
self.graphics_view.setFrameStyle(QFrame.NoFrame)
self.graphics_view.scale(-1, 1) # this make live video from camero mirror.
self.video_item = QGraphicsVideoItem()
self.scene.addItem(self.video_item)
self.layout = QVBoxLayout(self)
self.layout.setSpacing(0)
self.layout.setContentsMargins(0, 0, 0, 0)
self.layout.addWidget(self.graphics_view)
@@ -93,9 +99,8 @@ class CameraWidget(QWidget):
def take_photo(self, camera_save_path):
image_capture = QCameraImageCapture(self.camera)
save_path = str(Path(os.path.expanduser(camera_save_path)))
photo_path = os.path.join(save_path, "EAF_Camera_Photo_" + time.strftime("%Y-%m-%d_%H:%M:%S", time.localtime(int(time.time()))))
image_capture.capture(photo_path)
self.message_to_emacs.emit("Captured Photo at " + photo_path)
photo_path = os.path.join(save_path, "EAF_Camera_Photo_" + time.strftime("%Y%m%d_%H%M%S", time.localtime(int(time.time()))))
return image_capture.capture(photo_path)
def stop_camera(self):
self.camera.stop()

View File

@@ -28,6 +28,7 @@ import subprocess
import os
import qrcode
import signal
import tempfile
from core.buffer import Buffer
@@ -76,7 +77,7 @@ class FileUploaderWidget(QWidget):
self.file_name_font.setPointSize(24)
self.file_name_label = QLabel(self)
self.file_name_label.setText("Your file will be share at\n{0}".format(url))
self.file_name_label.setText("Your smartphone file will be shared at\n{0}".format(url))
self.file_name_label.setFont(self.file_name_font)
self.file_name_label.setAlignment(Qt.AlignCenter)
self.file_name_label.setStyleSheet("color: #eee")
@@ -86,7 +87,7 @@ class FileUploaderWidget(QWidget):
self.notify_font = QFont()
self.notify_font.setPointSize(12)
self.notify_label = QLabel(self)
self.notify_label.setText("Scan QR code above to upload a file from your smartphone.\nMake sure the smartphone is connected to the same WiFi network as this computer.")
self.notify_label.setText("Scan the QR code above to upload a file from your smartphone.\nMake sure the smartphone is connected to the same WiFi network as this computer.")
self.notify_label.setFont(self.notify_font)
self.notify_label.setAlignment(Qt.AlignCenter)
self.notify_label.setStyleSheet("color: #eee")
@@ -107,8 +108,10 @@ class FileUploaderWidget(QWidget):
self.qrcode_label.setPixmap(qrcode.make(self.address, image_factory=Image).pixmap())
tmp_db_file = os.path.join(tempfile.gettempdir(), "filebrowser.db")
self.background_process = subprocess.Popen(
"cd {0} && filebrowser --noauth -d /tmp/filebrowser.db --address {1} -p {2}".format(url, self.local_ip, self.port),
"filebrowser --noauth -d {0} --address {1} -p {2}".format(tmp_db_file, self.local_ip, self.port),
cwd=url,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
shell=True)

View File

@@ -22,7 +22,7 @@
from PyQt5.QtCore import QUrl
from PyQt5.QtGui import QColor
from core.browser import BrowserBuffer
from core.utils import PostGui
from core.utils import interactive
from pathlib import Path
import os
@@ -30,16 +30,20 @@ class AppBuffer(BrowserBuffer):
def __init__(self, buffer_id, url, config_dir, arguments, emacs_var_dict, module_path):
BrowserBuffer.__init__(self, buffer_id, url, config_dir, arguments, emacs_var_dict, module_path, False)
self.index_file = os.path.join(os.path.dirname(__file__), "index.html")
self.load_image(url)
def load_image(self, url):
self.url = url
self.parent_dir = os.path.abspath(os.path.join(url, os.pardir))
self.image_name = os.path.basename(url)
self.buffer_widget.setUrl(QUrl("file://" + self.url))
with open(self.index_file, "r") as f:
html = f.read().replace("%1", os.path.join(os.path.dirname(__file__))).replace("%2", os.path.join("file://", url))
self.buffer_widget.setHtml(html, QUrl("file://"))
def is_image_file(self, f):
return Path(f).suffix.lower() in ["jpg", "jpeg", "png", "bmp", "gif", "svg", "webp"]
return Path(f).suffix[1:].lower() in ["jpg", "jpeg", "png", "bmp", "gif", "svg", "webp"]
def get_same_dir_images(self):
files = [f for f in os.listdir(self.parent_dir) if os.path.isfile(os.path.join(self.parent_dir, f))]
@@ -54,7 +58,9 @@ class AppBuffer(BrowserBuffer):
else:
image_index += 1
self.load_image(os.path.join(self.parent_dir, images[image_index]))
self.image_name = images[image_index]
self.load_image(os.path.join(self.parent_dir, self.image_name))
self.change_title(self.image_name)
def load_prev_image(self):
images = self.get_same_dir_images()
@@ -65,4 +71,39 @@ class AppBuffer(BrowserBuffer):
else:
image_index -= 1
self.load_image(os.path.join(self.parent_dir, images[image_index]))
self.image_name = images[image_index]
self.load_image(os.path.join(self.parent_dir, self.image_name))
self.change_title(self.image_name)
@interactive
def rotate_left(self):
self.buffer_widget.eval_js("rotate_left();")
@interactive
def rotate_right(self):
self.buffer_widget.eval_js("rotate_right();")
@interactive
def zoom_out(self):
self.buffer_widget.eval_js("zoom_out();")
@interactive
def zoom_in(self):
self.buffer_widget.eval_js("zoom_in();")
@interactive
def zoom_reset(self):
self.buffer_widget.eval_js("zoom_reset();")
@interactive
def zoom_toggle(self):
self.buffer_widget.eval_js("zoom_toggle();")
@interactive
def flip_horizontal(self):
self.buffer_widget.eval_js("flip_horizontal();")
@interactive
def flip_vertical(self):
self.buffer_widget.eval_js("flip_vertical();")

View File

@@ -0,0 +1,88 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="text/javascript" src="%1/node_modules/viewerjs/dist/viewer.js"></script>
<link rel="stylesheet" href="%1/node_modules/viewerjs/dist/viewer.css" />
<style>
html, body {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
background: #7F7F7F;
}
img {
display: none;
background: #7F7F7F;
}
.viewer-fullscreen, .viewer-fullscreen-exit {
display: none;
}
</style>
</head>
<body>
<div>
<img id="image" src="%2">
</div>
<script type="text/javascript">
// View an image
const viewer = new Viewer(document.getElementById('image'), {
inline: true,
navbar: false,
transition: false,
toolbar: {
zoomIn: 4,
zoomOut: 4,
oneToOne: 4,
reset: 4,
rotateLeft: 4,
rotateRight: 4,
flipHorizontal: 4,
flipVertical: 4,
},
view() {
viewer.full();
},
});
function rotate_left() {
viewer.rotate(-90);
}
function rotate_right() {
viewer.rotate(90);
}
function zoom_out() {
viewer.zoom(-0.1, true);
}
function zoom_in() {
viewer.zoom(0.1, true);
}
function zoom_reset() {
viewer.reset();
}
function zoom_toggle() {
viewer.toggle();
}
function flip_horizontal() {
viewer.scaleX(-viewer.imageData.scaleX || -1);
}
function flip_vertical() {
viewer.scaleY(-viewer.imageData.scaleY || -1);
}
</script>
</body>
</html>

View File

@@ -0,0 +1,13 @@
{
"name": "eaf-image-viewer",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"viewerjs": {
"version": "1.9.0",
"resolved": "https://registry.npm.taobao.org/viewerjs/download/viewerjs-1.9.0.tgz",
"integrity": "sha1-bfr1REDDsvdpG4Vma6bSdwjtlZI="
}
}
}

View File

@@ -0,0 +1,23 @@
{
"name": "eaf-image-viewer",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/manateelazycat/emacs-application-framework.git"
},
"author": "",
"license": "GPL-3.0-or-later",
"bugs": {
"url": "https://github.com/manateelazycat/emacs-application-framework/issues"
},
"homepage": "https://github.com/manateelazycat/emacs-application-framework#readme",
"dependencies": {
"viewerjs": "^1.9.0"
}
}

View File

@@ -193,7 +193,7 @@ buffer."
))
(defun eaf-interleave-add-file-url ()
"Add new url on note if property is none. else modify current url"
"Add a new url on note if the property is none, else modify current url."
(interactive)
(let ((url (read-file-name "Please specify path: " nil nil t)))
(org-entry-put (point) eaf-interleave--url-prop url)))
@@ -212,7 +212,7 @@ This show the previous notes and synchronizes the PDF to the right page number."
(eaf-interleave-sync-current-note))
(defun eaf-interleave-open-notes-file ()
"Find current EAF url corresponding note files if exists"
"Find current EAF url corresponding note files if it exists."
(interactive)
(if (derived-mode-p 'eaf-mode)
(cond ((equal eaf--buffer-app-name "pdf-viewer")
@@ -463,12 +463,12 @@ Consider a headline with property PROPERTY as parent headline."
(defun eaf-interleave--pdf-viewer-current-page (url)
"get current page index."
(let ((id (buffer-local-value 'eaf--buffer-id (eaf-interleave--find-buffer url))))
(string-to-number (eaf-call "call_function" id "current_page"))))
(string-to-number (eaf-call-sync "call_function" id "current_page"))))
(defun eaf-interleave--pdf-viewer-goto-page (url page)
"goto page"
(let ((id (buffer-local-value 'eaf--buffer-id (eaf-interleave--find-buffer url))))
(eaf-call "handle_input_message" id "jump_page" page)))
(eaf-call-async "handle_input_response" id "jump_page" page)))
(defun eaf-interleave--ensure-buffer-window (buffer)
"If BUFFER don't display, will use other window display"

View File

@@ -36,8 +36,8 @@ class AppBuffer(BrowserBuffer):
BrowserBuffer.__init__(self, buffer_id, url, config_dir, arguments, emacs_var_dict, module_path, False)
self.url = url
index_file = "file://" + (os.path.join(os.path.dirname(__file__), "index.html"))
self.buffer_widget.setUrl(QUrl(index_file))
index_file = os.path.join(os.path.dirname(__file__), "index.html")
self.buffer_widget.setUrl(QUrl.fromLocalFile(index_file))
for method_name in ["toggle_play", "forward", "backward", "restart", "increase_volume", "decrease_volume"]:
self.build_js_method(method_name)
@@ -55,7 +55,7 @@ class AppBuffer(BrowserBuffer):
self.buffer_widget.eval_js("set_current_time('{}');".format(self.position))
def play_video(self):
self.buffer_widget.eval_js("play('{}');".format("file://" + self.url))
self.buffer_widget.eval_js("play(\"{}\");".format(QUrl.fromLocalFile(self.url).toString()))
def build_js_method(self, method_name):
def _do ():

View File

@@ -1,19 +0,0 @@
Copyright (c) 2014-2020 Denis Pushkarev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -1,58 +0,0 @@
# core-js
[![Sponsors on Open Collective](https://opencollective.com/core-js/sponsors/badge.svg)](#sponsors) [![Backers on Open Collective](https://opencollective.com/core-js/backers/badge.svg)](#backers) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/zloirock/core-js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![version](https://img.shields.io/npm/v/core-js.svg)](https://www.npmjs.com/package/core-js) [![npm downloads](https://img.shields.io/npm/dm/core-js.svg)](http://npm-stat.com/charts.html?package=core-js&author=&from=2014-11-18) [![Build Status](https://travis-ci.org/zloirock/core-js.svg)](https://travis-ci.org/zloirock/core-js) [![devDependency status](https://david-dm.org/zloirock/core-js/dev-status.svg)](https://david-dm.org/zloirock/core-js?type=dev)
> Modular standard library for JavaScript. Includes polyfills for [ECMAScript up to 2019](https://github.com/zloirock/core-js#ecmascript): [promises](https://github.com/zloirock/core-js#ecmascript-promise), [symbols](https://github.com/zloirock/core-js#ecmascript-symbol), [collections](https://github.com/zloirock/core-js#ecmascript-collections), iterators, [typed arrays](https://github.com/zloirock/core-js#ecmascript-typed-arrays), many other features, [ECMAScript proposals](https://github.com/zloirock/core-js#ecmascript-proposals), [some cross-platform WHATWG / W3C features and proposals](#web-standards) like [`URL`](https://github.com/zloirock/core-js#url-and-urlsearchparams). You can load only required features or use it without global namespace pollution.
## As advertising: the author is looking for a good job -)
## [core-js@3, babel and a look into the future](https://github.com/zloirock/core-js/tree/master/docs/2019-03-19-core-js-3-babel-and-a-look-into-the-future.md)
## Raising funds
`core-js` isn't backed by a company, so the future of this project depends on you. Become a sponsor or a backer [**on Open Collective**](https://opencollective.com/core-js) or [**on Patreon**](https://www.patreon.com/zloirock) if you are interested in `core-js`.
---
<a href="https://opencollective.com/core-js/sponsor/0/website" target="_blank"><img src="https://opencollective.com/core-js/sponsor/0/avatar.svg"></a><a href="https://opencollective.com/core-js/sponsor/1/website" target="_blank"><img src="https://opencollective.com/core-js/sponsor/1/avatar.svg"></a><a href="https://opencollective.com/core-js/sponsor/2/website" target="_blank"><img src="https://opencollective.com/core-js/sponsor/2/avatar.svg"></a><a href="https://opencollective.com/core-js/sponsor/3/website" target="_blank"><img src="https://opencollective.com/core-js/sponsor/3/avatar.svg"></a><a href="https://opencollective.com/core-js/sponsor/4/website" target="_blank"><img src="https://opencollective.com/core-js/sponsor/4/avatar.svg"></a><a href="https://opencollective.com/core-js/sponsor/5/website" target="_blank"><img src="https://opencollective.com/core-js/sponsor/5/avatar.svg"></a><a href="https://opencollective.com/core-js/sponsor/6/website" target="_blank"><img src="https://opencollective.com/core-js/sponsor/6/avatar.svg"></a><a href="https://opencollective.com/core-js/sponsor/7/website" target="_blank"><img src="https://opencollective.com/core-js/sponsor/7/avatar.svg"></a><a href="https://opencollective.com/core-js/sponsor/8/website" target="_blank"><img src="https://opencollective.com/core-js/sponsor/8/avatar.svg"></a><a href="https://opencollective.com/core-js/sponsor/9/website" target="_blank"><img src="https://opencollective.com/core-js/sponsor/9/avatar.svg"></a><a href="https://opencollective.com/core-js/sponsor/10/website" target="_blank"><img src="https://opencollective.com/core-js/sponsor/10/avatar.svg"></a><a href="https://opencollective.com/core-js/sponsor/11/website" target="_blank"><img src="https://opencollective.com/core-js/sponsor/11/avatar.svg"></a>
---
<a href="https://opencollective.com/core-js#backers" target="_blank"><img src="https://opencollective.com/core-js/backers.svg?width=890"></a>
---
[*Example*](http://goo.gl/a2xexl):
```js
import 'core-js'; // <- at the top of your entry point
Array.from(new Set([1, 2, 3, 2, 1])); // => [1, 2, 3]
[1, [2, 3], [4, [5]]].flat(2); // => [1, 2, 3, 4, 5]
Promise.resolve(32).then(x => console.log(x)); // => 32
```
*You can load only required features*:
```js
import 'core-js/features/array/from'; // <- at the top of your entry point
import 'core-js/features/array/flat'; // <- at the top of your entry point
import 'core-js/features/set'; // <- at the top of your entry point
import 'core-js/features/promise'; // <- at the top of your entry point
Array.from(new Set([1, 2, 3, 2, 1])); // => [1, 2, 3]
[1, [2, 3], [4, [5]]].flat(2); // => [1, 2, 3, 4, 5]
Promise.resolve(32).then(x => console.log(x)); // => 32
```
*Or use it without global namespace pollution*:
```js
import from from 'core-js-pure/features/array/from';
import flat from 'core-js-pure/features/array/flat';
import Set from 'core-js-pure/features/set';
import Promise from 'core-js-pure/features/promise';
from(new Set([1, 2, 3, 2, 1])); // => [1, 2, 3]
flat([1, [2, 3], [4, [5]]], 2); // => [1, 2, 3, 4, 5]
Promise.resolve(32).then(x => console.log(x)); // => 32
```
**It's a global version (first 2 examples), for more info see [`core-js` documentation](https://github.com/zloirock/core-js/blob/master/README.md).**

View File

@@ -1,23 +0,0 @@
var has = require('./internals/has');
var isArray = require('./internals/is-array');
var isForced = require('./internals/is-forced');
var shared = require('./internals/shared-store');
var data = isForced.data;
var normalize = isForced.normalize;
var USE_FUNCTION_CONSTRUCTOR = 'USE_FUNCTION_CONSTRUCTOR';
var ASYNC_ITERATOR_PROTOTYPE = 'AsyncIteratorPrototype';
var setAggressivenessLevel = function (object, constant) {
if (isArray(object)) for (var i = 0; i < object.length; i++) data[normalize(object[i])] = constant;
};
module.exports = function (options) {
if (typeof options == 'object') {
setAggressivenessLevel(options.useNative, isForced.NATIVE);
setAggressivenessLevel(options.usePolyfill, isForced.POLYFILL);
setAggressivenessLevel(options.useFeatureDetection, null);
if (has(options, USE_FUNCTION_CONSTRUCTOR)) shared[USE_FUNCTION_CONSTRUCTOR] = !!options[USE_FUNCTION_CONSTRUCTOR];
if (has(options, ASYNC_ITERATOR_PROTOTYPE)) shared[USE_FUNCTION_CONSTRUCTOR] = options[ASYNC_ITERATOR_PROTOTYPE];
}
};

View File

@@ -1 +0,0 @@
This folder contains entry points for [stable ECMAScript features](https://github.com/zloirock/core-js/tree/v3#ecmascript) with dependencies.

View File

@@ -1,5 +0,0 @@
require('../../modules/es.array-buffer.constructor');
require('../../modules/es.object.to-string');
var path = require('../../internals/path');
module.exports = path.ArrayBuffer;

View File

@@ -1,7 +0,0 @@
require('../../modules/es.array-buffer.constructor');
require('../../modules/es.array-buffer.is-view');
require('../../modules/es.array-buffer.slice');
require('../../modules/es.object.to-string');
var path = require('../../internals/path');
module.exports = path.ArrayBuffer;

View File

@@ -1,4 +0,0 @@
require('../../modules/es.array-buffer.is-view');
var path = require('../../internals/path');
module.exports = path.ArrayBuffer.isView;

View File

@@ -1 +0,0 @@
require('../../modules/es.array-buffer.slice');

View File

@@ -1,4 +0,0 @@
require('../../modules/es.array.concat');
var entryUnbind = require('../../internals/entry-unbind');
module.exports = entryUnbind('Array', 'concat');

View File

@@ -1,4 +0,0 @@
require('../../modules/es.array.copy-within');
var entryUnbind = require('../../internals/entry-unbind');
module.exports = entryUnbind('Array', 'copyWithin');

View File

@@ -1,4 +0,0 @@
require('../../modules/es.array.iterator');
var entryUnbind = require('../../internals/entry-unbind');
module.exports = entryUnbind('Array', 'entries');

View File

@@ -1,4 +0,0 @@
require('../../modules/es.array.every');
var entryUnbind = require('../../internals/entry-unbind');
module.exports = entryUnbind('Array', 'every');

View File

@@ -1,4 +0,0 @@
require('../../modules/es.array.fill');
var entryUnbind = require('../../internals/entry-unbind');
module.exports = entryUnbind('Array', 'fill');

View File

@@ -1,4 +0,0 @@
require('../../modules/es.array.filter');
var entryUnbind = require('../../internals/entry-unbind');
module.exports = entryUnbind('Array', 'filter');

View File

@@ -1,4 +0,0 @@
require('../../modules/es.array.find-index');
var entryUnbind = require('../../internals/entry-unbind');
module.exports = entryUnbind('Array', 'findIndex');

View File

@@ -1,4 +0,0 @@
require('../../modules/es.array.find');
var entryUnbind = require('../../internals/entry-unbind');
module.exports = entryUnbind('Array', 'find');

View File

@@ -1,5 +0,0 @@
require('../../modules/es.array.flat-map');
require('../../modules/es.array.unscopables.flat-map');
var entryUnbind = require('../../internals/entry-unbind');
module.exports = entryUnbind('Array', 'flatMap');

View File

@@ -1,5 +0,0 @@
require('../../modules/es.array.flat');
require('../../modules/es.array.unscopables.flat');
var entryUnbind = require('../../internals/entry-unbind');
module.exports = entryUnbind('Array', 'flat');

View File

@@ -1,4 +0,0 @@
require('../../modules/es.array.for-each');
var entryUnbind = require('../../internals/entry-unbind');
module.exports = entryUnbind('Array', 'forEach');

View File

@@ -1,5 +0,0 @@
require('../../modules/es.string.iterator');
require('../../modules/es.array.from');
var path = require('../../internals/path');
module.exports = path.Array.from;

View File

@@ -1,4 +0,0 @@
require('../../modules/es.array.includes');
var entryUnbind = require('../../internals/entry-unbind');
module.exports = entryUnbind('Array', 'includes');

View File

@@ -1,4 +0,0 @@
require('../../modules/es.array.index-of');
var entryUnbind = require('../../internals/entry-unbind');
module.exports = entryUnbind('Array', 'indexOf');

View File

@@ -1,33 +0,0 @@
require('../../modules/es.string.iterator');
require('../../modules/es.array.from');
require('../../modules/es.array.is-array');
require('../../modules/es.array.of');
require('../../modules/es.array.concat');
require('../../modules/es.array.copy-within');
require('../../modules/es.array.every');
require('../../modules/es.array.fill');
require('../../modules/es.array.filter');
require('../../modules/es.array.find');
require('../../modules/es.array.find-index');
require('../../modules/es.array.flat');
require('../../modules/es.array.flat-map');
require('../../modules/es.array.for-each');
require('../../modules/es.array.includes');
require('../../modules/es.array.index-of');
require('../../modules/es.array.iterator');
require('../../modules/es.array.join');
require('../../modules/es.array.last-index-of');
require('../../modules/es.array.map');
require('../../modules/es.array.reduce');
require('../../modules/es.array.reduce-right');
require('../../modules/es.array.reverse');
require('../../modules/es.array.slice');
require('../../modules/es.array.some');
require('../../modules/es.array.sort');
require('../../modules/es.array.species');
require('../../modules/es.array.splice');
require('../../modules/es.array.unscopables.flat');
require('../../modules/es.array.unscopables.flat-map');
var path = require('../../internals/path');
module.exports = path.Array;

View File

@@ -1,4 +0,0 @@
require('../../modules/es.array.is-array');
var path = require('../../internals/path');
module.exports = path.Array.isArray;

View File

@@ -1,4 +0,0 @@
require('../../modules/es.array.iterator');
var entryUnbind = require('../../internals/entry-unbind');
module.exports = entryUnbind('Array', 'values');

View File

@@ -1,4 +0,0 @@
require('../../modules/es.array.join');
var entryUnbind = require('../../internals/entry-unbind');
module.exports = entryUnbind('Array', 'join');

View File

@@ -1,4 +0,0 @@
require('../../modules/es.array.iterator');
var entryUnbind = require('../../internals/entry-unbind');
module.exports = entryUnbind('Array', 'keys');

View File

@@ -1,4 +0,0 @@
require('../../modules/es.array.last-index-of');
var entryUnbind = require('../../internals/entry-unbind');
module.exports = entryUnbind('Array', 'lastIndexOf');

View File

@@ -1,4 +0,0 @@
require('../../modules/es.array.map');
var entryUnbind = require('../../internals/entry-unbind');
module.exports = entryUnbind('Array', 'map');

View File

@@ -1,4 +0,0 @@
require('../../modules/es.array.of');
var path = require('../../internals/path');
module.exports = path.Array.of;

View File

@@ -1,4 +0,0 @@
require('../../modules/es.array.reduce-right');
var entryUnbind = require('../../internals/entry-unbind');
module.exports = entryUnbind('Array', 'reduceRight');

View File

@@ -1,4 +0,0 @@
require('../../modules/es.array.reduce');
var entryUnbind = require('../../internals/entry-unbind');
module.exports = entryUnbind('Array', 'reduce');

View File

@@ -1,4 +0,0 @@
require('../../modules/es.array.reverse');
var entryUnbind = require('../../internals/entry-unbind');
module.exports = entryUnbind('Array', 'reverse');

View File

@@ -1,4 +0,0 @@
require('../../modules/es.array.slice');
var entryUnbind = require('../../internals/entry-unbind');
module.exports = entryUnbind('Array', 'slice');

View File

@@ -1,4 +0,0 @@
require('../../modules/es.array.some');
var entryUnbind = require('../../internals/entry-unbind');
module.exports = entryUnbind('Array', 'some');

View File

@@ -1,4 +0,0 @@
require('../../modules/es.array.sort');
var entryUnbind = require('../../internals/entry-unbind');
module.exports = entryUnbind('Array', 'sort');

View File

@@ -1,4 +0,0 @@
require('../../modules/es.array.splice');
var entryUnbind = require('../../internals/entry-unbind');
module.exports = entryUnbind('Array', 'splice');

View File

@@ -1,4 +0,0 @@
require('../../modules/es.array.iterator');
var entryUnbind = require('../../internals/entry-unbind');
module.exports = entryUnbind('Array', 'values');

View File

@@ -1,4 +0,0 @@
require('../../../modules/es.array.concat');
var entryVirtual = require('../../../internals/entry-virtual');
module.exports = entryVirtual('Array').concat;

View File

@@ -1,4 +0,0 @@
require('../../../modules/es.array.copy-within');
var entryVirtual = require('../../../internals/entry-virtual');
module.exports = entryVirtual('Array').copyWithin;

View File

@@ -1,4 +0,0 @@
require('../../../modules/es.array.iterator');
var entryVirtual = require('../../../internals/entry-virtual');
module.exports = entryVirtual('Array').entries;

View File

@@ -1,4 +0,0 @@
require('../../../modules/es.array.every');
var entryVirtual = require('../../../internals/entry-virtual');
module.exports = entryVirtual('Array').every;

View File

@@ -1,4 +0,0 @@
require('../../../modules/es.array.fill');
var entryVirtual = require('../../../internals/entry-virtual');
module.exports = entryVirtual('Array').fill;

View File

@@ -1,4 +0,0 @@
require('../../../modules/es.array.filter');
var entryVirtual = require('../../../internals/entry-virtual');
module.exports = entryVirtual('Array').filter;

View File

@@ -1,4 +0,0 @@
require('../../../modules/es.array.find-index');
var entryVirtual = require('../../../internals/entry-virtual');
module.exports = entryVirtual('Array').findIndex;

View File

@@ -1,4 +0,0 @@
require('../../../modules/es.array.find');
var entryVirtual = require('../../../internals/entry-virtual');
module.exports = entryVirtual('Array').find;

View File

@@ -1,5 +0,0 @@
require('../../../modules/es.array.flat-map');
require('../../../modules/es.array.unscopables.flat-map');
var entryVirtual = require('../../../internals/entry-virtual');
module.exports = entryVirtual('Array').flatMap;

View File

@@ -1,5 +0,0 @@
require('../../../modules/es.array.flat');
require('../../../modules/es.array.unscopables.flat');
var entryVirtual = require('../../../internals/entry-virtual');
module.exports = entryVirtual('Array').flat;

View File

@@ -1,4 +0,0 @@
require('../../../modules/es.array.for-each');
var entryVirtual = require('../../../internals/entry-virtual');
module.exports = entryVirtual('Array').forEach;

View File

@@ -1,4 +0,0 @@
require('../../../modules/es.array.includes');
var entryVirtual = require('../../../internals/entry-virtual');
module.exports = entryVirtual('Array').includes;

View File

@@ -1,4 +0,0 @@
require('../../../modules/es.array.index-of');
var entryVirtual = require('../../../internals/entry-virtual');
module.exports = entryVirtual('Array').indexOf;

View File

@@ -1,29 +0,0 @@
require('../../../modules/es.array.concat');
require('../../../modules/es.array.copy-within');
require('../../../modules/es.array.every');
require('../../../modules/es.array.fill');
require('../../../modules/es.array.filter');
require('../../../modules/es.array.find');
require('../../../modules/es.array.find-index');
require('../../../modules/es.array.flat');
require('../../../modules/es.array.flat-map');
require('../../../modules/es.array.for-each');
require('../../../modules/es.array.includes');
require('../../../modules/es.array.index-of');
require('../../../modules/es.array.iterator');
require('../../../modules/es.array.join');
require('../../../modules/es.array.last-index-of');
require('../../../modules/es.array.map');
require('../../../modules/es.array.reduce');
require('../../../modules/es.array.reduce-right');
require('../../../modules/es.array.reverse');
require('../../../modules/es.array.slice');
require('../../../modules/es.array.some');
require('../../../modules/es.array.sort');
require('../../../modules/es.array.species');
require('../../../modules/es.array.splice');
require('../../../modules/es.array.unscopables.flat');
require('../../../modules/es.array.unscopables.flat-map');
var entryVirtual = require('../../../internals/entry-virtual');
module.exports = entryVirtual('Array');

View File

@@ -1,4 +0,0 @@
require('../../../modules/es.array.iterator');
var entryVirtual = require('../../../internals/entry-virtual');
module.exports = entryVirtual('Array').values;

View File

@@ -1,4 +0,0 @@
require('../../../modules/es.array.join');
var entryVirtual = require('../../../internals/entry-virtual');
module.exports = entryVirtual('Array').join;

View File

@@ -1,4 +0,0 @@
require('../../../modules/es.array.iterator');
var entryVirtual = require('../../../internals/entry-virtual');
module.exports = entryVirtual('Array').keys;

View File

@@ -1,4 +0,0 @@
require('../../../modules/es.array.last-index-of');
var entryVirtual = require('../../../internals/entry-virtual');
module.exports = entryVirtual('Array').lastIndexOf;

View File

@@ -1,4 +0,0 @@
require('../../../modules/es.array.map');
var entryVirtual = require('../../../internals/entry-virtual');
module.exports = entryVirtual('Array').map;

View File

@@ -1,4 +0,0 @@
require('../../../modules/es.array.reduce-right');
var entryVirtual = require('../../../internals/entry-virtual');
module.exports = entryVirtual('Array').reduceRight;

View File

@@ -1,4 +0,0 @@
require('../../../modules/es.array.reduce');
var entryVirtual = require('../../../internals/entry-virtual');
module.exports = entryVirtual('Array').reduce;

View File

@@ -1,4 +0,0 @@
require('../../../modules/es.array.reverse');
var entryVirtual = require('../../../internals/entry-virtual');
module.exports = entryVirtual('Array').reverse;

View File

@@ -1,4 +0,0 @@
require('../../../modules/es.array.slice');
var entryVirtual = require('../../../internals/entry-virtual');
module.exports = entryVirtual('Array').slice;

View File

@@ -1,4 +0,0 @@
require('../../../modules/es.array.some');
var entryVirtual = require('../../../internals/entry-virtual');
module.exports = entryVirtual('Array').some;

View File

@@ -1,4 +0,0 @@
require('../../../modules/es.array.sort');
var entryVirtual = require('../../../internals/entry-virtual');
module.exports = entryVirtual('Array').sort;

View File

@@ -1,4 +0,0 @@
require('../../../modules/es.array.splice');
var entryVirtual = require('../../../internals/entry-virtual');
module.exports = entryVirtual('Array').splice;

View File

@@ -1,4 +0,0 @@
require('../../../modules/es.array.iterator');
var entryVirtual = require('../../../internals/entry-virtual');
module.exports = entryVirtual('Array').values;

View File

@@ -1,5 +0,0 @@
require('../../modules/es.data-view');
require('../../modules/es.object.to-string');
var path = require('../../internals/path');
module.exports = path.DataView;

View File

@@ -1,8 +0,0 @@
require('../../modules/es.date.now');
require('../../modules/es.date.to-json');
require('../../modules/es.date.to-iso-string');
require('../../modules/es.date.to-string');
require('../../modules/es.date.to-primitive');
var path = require('../../internals/path');
module.exports = path.Date;

View File

@@ -1,4 +0,0 @@
require('../../modules/es.date.now');
var path = require('../../internals/path');
module.exports = path.Date.now;

View File

@@ -1,5 +0,0 @@
require('../../modules/es.date.to-iso-string');
require('../../modules/es.date.to-json');
var entryUnbind = require('../../internals/entry-unbind');
module.exports = entryUnbind('Date', 'toISOString');

View File

@@ -1,4 +0,0 @@
require('../../modules/es.date.to-json');
var entryUnbind = require('../../internals/entry-unbind');
module.exports = entryUnbind('Date', 'toJSON');

View File

@@ -1,6 +0,0 @@
require('../../modules/es.date.to-primitive');
var toPrimitive = require('../../internals/date-to-primitive');
module.exports = function (it, hint) {
return toPrimitive.call(it, hint);
};

View File

@@ -1,6 +0,0 @@
require('../../modules/es.date.to-string');
var dateToString = Date.prototype.toString;
module.exports = function toString(it) {
return dateToString.call(it);
};

View File

@@ -1,4 +0,0 @@
require('../../modules/es.function.bind');
var entryUnbind = require('../../internals/entry-unbind');
module.exports = entryUnbind('Function', 'bind');

View File

@@ -1,4 +0,0 @@
require('../../modules/es.function.has-instance');
var wellKnownSymbol = require('../../internals/well-known-symbol');
module.exports = Function[wellKnownSymbol('hasInstance')];

View File

@@ -1,6 +0,0 @@
require('../../modules/es.function.bind');
require('../../modules/es.function.name');
require('../../modules/es.function.has-instance');
var path = require('../../internals/path');
module.exports = path.Function;

View File

@@ -1 +0,0 @@
require('../../modules/es.function.name');

View File

@@ -1,4 +0,0 @@
require('../../../modules/es.function.bind');
var entryVirtual = require('../../../internals/entry-virtual');
module.exports = entryVirtual('Function').bind;

View File

@@ -1,4 +0,0 @@
require('../../../modules/es.function.bind');
var entryVirtual = require('../../../internals/entry-virtual');
module.exports = entryVirtual('Function');

View File

@@ -1,3 +0,0 @@
require('../modules/es.global-this');
module.exports = require('../internals/global');

View File

@@ -1,210 +0,0 @@
require('../modules/es.symbol');
require('../modules/es.symbol.async-iterator');
require('../modules/es.symbol.description');
require('../modules/es.symbol.has-instance');
require('../modules/es.symbol.is-concat-spreadable');
require('../modules/es.symbol.iterator');
require('../modules/es.symbol.match');
require('../modules/es.symbol.match-all');
require('../modules/es.symbol.replace');
require('../modules/es.symbol.search');
require('../modules/es.symbol.species');
require('../modules/es.symbol.split');
require('../modules/es.symbol.to-primitive');
require('../modules/es.symbol.to-string-tag');
require('../modules/es.symbol.unscopables');
require('../modules/es.object.assign');
require('../modules/es.object.create');
require('../modules/es.object.define-property');
require('../modules/es.object.define-properties');
require('../modules/es.object.entries');
require('../modules/es.object.freeze');
require('../modules/es.object.from-entries');
require('../modules/es.object.get-own-property-descriptor');
require('../modules/es.object.get-own-property-descriptors');
require('../modules/es.object.get-own-property-names');
require('../modules/es.object.get-prototype-of');
require('../modules/es.object.is');
require('../modules/es.object.is-extensible');
require('../modules/es.object.is-frozen');
require('../modules/es.object.is-sealed');
require('../modules/es.object.keys');
require('../modules/es.object.prevent-extensions');
require('../modules/es.object.seal');
require('../modules/es.object.set-prototype-of');
require('../modules/es.object.values');
require('../modules/es.object.to-string');
require('../modules/es.object.define-getter');
require('../modules/es.object.define-setter');
require('../modules/es.object.lookup-getter');
require('../modules/es.object.lookup-setter');
require('../modules/es.function.bind');
require('../modules/es.function.name');
require('../modules/es.function.has-instance');
require('../modules/es.global-this');
require('../modules/es.array.from');
require('../modules/es.array.is-array');
require('../modules/es.array.of');
require('../modules/es.array.concat');
require('../modules/es.array.copy-within');
require('../modules/es.array.every');
require('../modules/es.array.fill');
require('../modules/es.array.filter');
require('../modules/es.array.find');
require('../modules/es.array.find-index');
require('../modules/es.array.flat');
require('../modules/es.array.flat-map');
require('../modules/es.array.for-each');
require('../modules/es.array.includes');
require('../modules/es.array.index-of');
require('../modules/es.array.join');
require('../modules/es.array.last-index-of');
require('../modules/es.array.map');
require('../modules/es.array.reduce');
require('../modules/es.array.reduce-right');
require('../modules/es.array.reverse');
require('../modules/es.array.slice');
require('../modules/es.array.some');
require('../modules/es.array.sort');
require('../modules/es.array.splice');
require('../modules/es.array.species');
require('../modules/es.array.unscopables.flat');
require('../modules/es.array.unscopables.flat-map');
require('../modules/es.array.iterator');
require('../modules/es.string.from-code-point');
require('../modules/es.string.raw');
require('../modules/es.string.code-point-at');
require('../modules/es.string.ends-with');
require('../modules/es.string.includes');
require('../modules/es.string.match');
require('../modules/es.string.match-all');
require('../modules/es.string.pad-end');
require('../modules/es.string.pad-start');
require('../modules/es.string.repeat');
require('../modules/es.string.replace');
require('../modules/es.string.search');
require('../modules/es.string.split');
require('../modules/es.string.starts-with');
require('../modules/es.string.trim');
require('../modules/es.string.trim-start');
require('../modules/es.string.trim-end');
require('../modules/es.string.iterator');
require('../modules/es.string.anchor');
require('../modules/es.string.big');
require('../modules/es.string.blink');
require('../modules/es.string.bold');
require('../modules/es.string.fixed');
require('../modules/es.string.fontcolor');
require('../modules/es.string.fontsize');
require('../modules/es.string.italics');
require('../modules/es.string.link');
require('../modules/es.string.small');
require('../modules/es.string.strike');
require('../modules/es.string.sub');
require('../modules/es.string.sup');
require('../modules/es.regexp.constructor');
require('../modules/es.regexp.exec');
require('../modules/es.regexp.flags');
require('../modules/es.regexp.sticky');
require('../modules/es.regexp.test');
require('../modules/es.regexp.to-string');
require('../modules/es.parse-int');
require('../modules/es.parse-float');
require('../modules/es.number.constructor');
require('../modules/es.number.epsilon');
require('../modules/es.number.is-finite');
require('../modules/es.number.is-integer');
require('../modules/es.number.is-nan');
require('../modules/es.number.is-safe-integer');
require('../modules/es.number.max-safe-integer');
require('../modules/es.number.min-safe-integer');
require('../modules/es.number.parse-float');
require('../modules/es.number.parse-int');
require('../modules/es.number.to-fixed');
require('../modules/es.number.to-precision');
require('../modules/es.math.acosh');
require('../modules/es.math.asinh');
require('../modules/es.math.atanh');
require('../modules/es.math.cbrt');
require('../modules/es.math.clz32');
require('../modules/es.math.cosh');
require('../modules/es.math.expm1');
require('../modules/es.math.fround');
require('../modules/es.math.hypot');
require('../modules/es.math.imul');
require('../modules/es.math.log10');
require('../modules/es.math.log1p');
require('../modules/es.math.log2');
require('../modules/es.math.sign');
require('../modules/es.math.sinh');
require('../modules/es.math.tanh');
require('../modules/es.math.to-string-tag');
require('../modules/es.math.trunc');
require('../modules/es.date.now');
require('../modules/es.date.to-json');
require('../modules/es.date.to-iso-string');
require('../modules/es.date.to-string');
require('../modules/es.date.to-primitive');
require('../modules/es.json.stringify');
require('../modules/es.json.to-string-tag');
require('../modules/es.promise');
require('../modules/es.promise.all-settled');
require('../modules/es.promise.finally');
require('../modules/es.map');
require('../modules/es.set');
require('../modules/es.weak-map');
require('../modules/es.weak-set');
require('../modules/es.array-buffer.constructor');
require('../modules/es.array-buffer.is-view');
require('../modules/es.array-buffer.slice');
require('../modules/es.data-view');
require('../modules/es.typed-array.int8-array');
require('../modules/es.typed-array.uint8-array');
require('../modules/es.typed-array.uint8-clamped-array');
require('../modules/es.typed-array.int16-array');
require('../modules/es.typed-array.uint16-array');
require('../modules/es.typed-array.int32-array');
require('../modules/es.typed-array.uint32-array');
require('../modules/es.typed-array.float32-array');
require('../modules/es.typed-array.float64-array');
require('../modules/es.typed-array.from');
require('../modules/es.typed-array.of');
require('../modules/es.typed-array.copy-within');
require('../modules/es.typed-array.every');
require('../modules/es.typed-array.fill');
require('../modules/es.typed-array.filter');
require('../modules/es.typed-array.find');
require('../modules/es.typed-array.find-index');
require('../modules/es.typed-array.for-each');
require('../modules/es.typed-array.includes');
require('../modules/es.typed-array.index-of');
require('../modules/es.typed-array.iterator');
require('../modules/es.typed-array.join');
require('../modules/es.typed-array.last-index-of');
require('../modules/es.typed-array.map');
require('../modules/es.typed-array.reduce');
require('../modules/es.typed-array.reduce-right');
require('../modules/es.typed-array.reverse');
require('../modules/es.typed-array.set');
require('../modules/es.typed-array.slice');
require('../modules/es.typed-array.some');
require('../modules/es.typed-array.sort');
require('../modules/es.typed-array.subarray');
require('../modules/es.typed-array.to-locale-string');
require('../modules/es.typed-array.to-string');
require('../modules/es.reflect.apply');
require('../modules/es.reflect.construct');
require('../modules/es.reflect.define-property');
require('../modules/es.reflect.delete-property');
require('../modules/es.reflect.get');
require('../modules/es.reflect.get-own-property-descriptor');
require('../modules/es.reflect.get-prototype-of');
require('../modules/es.reflect.has');
require('../modules/es.reflect.is-extensible');
require('../modules/es.reflect.own-keys');
require('../modules/es.reflect.prevent-extensions');
require('../modules/es.reflect.set');
require('../modules/es.reflect.set-prototype-of');
var path = require('../internals/path');
module.exports = path;

Some files were not shown because too many files have changed in this diff Show More