Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,15 @@ jobs:
steps:
- uses: actions/checkout@v4

- uses: purcell/setup-emacs@master
- uses: cachix/install-nix-action@v31.10.6
with:
version: 29.4
github_access_token: ${{ github.token }}

- name: Install Emacs 29.4
run: |
nix profile install --accept-flake-config \
github:purcell/nix-emacs-ci/868482a78575138aebb1b5c16a9266b95c800f2e#emacs-29-4
emacs --version

- name: Install Emacs package dependencies
run: |
Expand Down
10 changes: 8 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,15 @@ jobs:
steps:
- uses: actions/checkout@v4

- uses: purcell/setup-emacs@master
- uses: cachix/install-nix-action@v31.10.6
with:
version: 29.4
github_access_token: ${{ github.token }}

- name: Install Emacs 29.4
run: |
nix profile install --accept-flake-config \
github:purcell/nix-emacs-ci/868482a78575138aebb1b5c16a9266b95c800f2e#emacs-29-4
emacs --version

- name: Install Emacs package dependencies
run: |
Expand Down
56 changes: 49 additions & 7 deletions README.org
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

[[https://github.com/Jamie-Cui/git-overleaf/actions/workflows/test.yml][https://github.com/Jamie-Cui/git-overleaf/actions/workflows/test.yml/badge.svg]]
[[https://github.com/Jamie-Cui/git-overleaf/actions/workflows/coverage.yml][https://github.com/Jamie-Cui/git-overleaf/actions/workflows/coverage.yml/badge.svg]]
[[https://github.com/Jamie-Cui/git-overleaf/actions/workflows/melpazoid.yml/badge.svg][https://github.com/Jamie-Cui/git-overleaf/actions/workflows/melpazoid.yml]]
[[https://melpa.org/packages/git-overleaf-badge.svg][https://melpa.org/#/git-overleaf]]

Chinese README: [[file:README.zh.org][README.zh.org]]

Expand Down Expand Up @@ -68,29 +70,57 @@ runtime.

* Installation

** VC package install
** MELPA

~git-overleaf~ is not yet available from MELPA. Install it
directly from the Git repository with =use-package=:
~git-overleaf~ is available from MELPA. The recommended installation
path is Emacs' built-in package manager.

If MELPA is not already configured, add it to ~package-archives~:

#+begin_src elisp
(use-package git-overleaf
:vc (:url "https://github.com/Jamie-Cui/git-overleaf" :rev "main"))
(require 'package)
(add-to-list 'package-archives
'("melpa" . "https://melpa.org/packages/") t)
(package-initialize)
#+end_src

Without =use-package=, use Emacs' package manager directly:
Then refresh package contents and install ~git-overleaf~:

#+begin_example
M-x package-vc-install RET https://github.com/Jamie-Cui/git-overleaf.git RET
M-x package-refresh-contents RET
M-x package-install RET git-overleaf RET
#+end_example

With =use-package=:

#+begin_src elisp
(use-package git-overleaf
:ensure t)
#+end_src

For a self-hosted Overleaf instance, set the server URL before using the
interactive commands:

#+begin_src elisp
(setopt git-overleaf-url "https://latex.example.edu")
#+end_src

** Development version

To install unreleased changes from the Git repository, use
=use-package= with VC support:

#+begin_src elisp
(use-package git-overleaf
:vc (:url "https://github.com/Jamie-Cui/git-overleaf" :rev "main"))
#+end_src

Without =use-package=, use Emacs' VC package installer directly:

#+begin_example
M-x package-vc-install RET https://github.com/Jamie-Cui/git-overleaf.git RET
#+end_example

** Manual checkout

If using a local checkout, install the Elisp dependencies separately and
Expand Down Expand Up @@ -399,6 +429,18 @@ Cancellation interrupts external processes where possible, clears async
locks, and drops pending foreground callbacks. Work already completed
locally or remotely is not rolled back.

* Download Progress and Proxy Environment

Snapshot downloads echo curl progress in the minibuffer as
=Downloading project ... N%= when ~git-overleaf-log-echo~ is non-nil.

Downloads are performed by ~git-overleaf-curl-executable~, so proxy
configuration comes from Emacs' ~process-environment~, not necessarily
from the shell that runs =emacsclient=. If Overleaf downloads are slow
in GUI Emacs, check =(getenv "HTTPS_PROXY")= and =(getenv "HTTP_PROXY")=
inside Emacs, then copy the needed proxy variables with =setenv= or a
tool such as =exec-path-from-shell=.

* Magit Integration

To display an Overleaf section in =magit-status=, load the optional
Expand Down
52 changes: 45 additions & 7 deletions README.zh.org
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

[[https://github.com/Jamie-Cui/git-overleaf/actions/workflows/test.yml][https://github.com/Jamie-Cui/git-overleaf/actions/workflows/test.yml/badge.svg]]
[[https://github.com/Jamie-Cui/git-overleaf/actions/workflows/coverage.yml][https://github.com/Jamie-Cui/git-overleaf/actions/workflows/coverage.yml/badge.svg]]
[[https://melpa.org/#/git-overleaf][https://melpa.org/packages/git-overleaf-badge.svg]]

English README: [[file:README.org][README.org]]

Expand Down Expand Up @@ -56,28 +57,54 @@ Emacs 构建支持 SQLite。

* 安装

** VC 包安装
** MELPA

~git-overleaf~ 目前尚未发布到 MELPA。可使用 =use-package= 直接从
Git 仓库安装:
~git-overleaf~ 已发布到 MELPA。推荐通过 Emacs 内置包管理器安装。

如果尚未配置 MELPA,请先将它加入 ~package-archives~:

#+begin_src elisp
(use-package git-overleaf
:vc (:url "https://github.com/Jamie-Cui/git-overleaf" :rev "main"))
(require 'package)
(add-to-list 'package-archives
'("melpa" . "https://melpa.org/packages/") t)
(package-initialize)
#+end_src

如果不使用 =use-package=,可直接调用 Emacs 的包管理器
然后刷新包列表并安装 ~git-overleaf~

#+begin_example
M-x package-vc-install RET https://github.com/Jamie-Cui/git-overleaf.git RET
M-x package-refresh-contents RET
M-x package-install RET git-overleaf RET
#+end_example

使用 =use-package= 时:

#+begin_src elisp
(use-package git-overleaf
:ensure t)
#+end_src

如果使用自建 Overleaf 实例,请在使用交互命令前设置服务器地址:

#+begin_src elisp
(setopt git-overleaf-url "https://latex.example.edu")
#+end_src

** 开发版安装

如需安装 Git 仓库中的未发布改动,可使用 =use-package= 的 VC 支持:

#+begin_src elisp
(use-package git-overleaf
:vc (:url "https://github.com/Jamie-Cui/git-overleaf" :rev "main"))
#+end_src

如果不使用 =use-package=,可直接调用 Emacs 的 VC 包安装器:

#+begin_example
M-x package-vc-install RET https://github.com/Jamie-Cui/git-overleaf.git RET
#+end_example

** 手动 checkout

如果使用本地 checkout,请单独安装 Elisp 依赖,并将本仓库加入
Expand Down Expand Up @@ -360,6 +387,17 @@ M-x git-overleaf-force-stop
取消会尽可能中断外部进程、清理 async 锁,并丢弃尚未执行的前台回调。已
经在本地或远端完成的工作不会自动回滚。

* 下载进度与代理环境

快照下载会在 ~git-overleaf-log-echo~ 为非 nil 时,在 minibuffer 中显示
=Downloading project ... N%= 形式的 curl 进度。

下载由 ~git-overleaf-curl-executable~ 指定的 curl 执行,因此代理配置来自
Emacs 的 ~process-environment~,不一定等同于运行 =emacsclient= 的 shell。
如果 GUI Emacs 中下载 Overleaf 很慢,先在 Emacs 里检查
=(getenv "HTTPS_PROXY")= 和 =(getenv "HTTP_PROXY")=,再按需用 =setenv= 或
=exec-path-from-shell= 之类的工具把代理变量复制进 Emacs。

* Magit 集成

若要在 =magit-status= 中显示 Overleaf section,请加载可选集成:
Expand Down
156 changes: 145 additions & 11 deletions git-overleaf-http.el
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,13 @@ If REFRESH is non-nil, bypass the cached token and fetch a fresh one."
'("--fail" "--silent" "--show-error" "--location")
(git-overleaf--curl-timeout-args)))

(defun git-overleaf--curl-download-base-args ()
"Return common arguments shared by Overleaf project zip downloads."
(defun git-overleaf--curl-download-base-args (&optional progress)
"Return common arguments shared by Overleaf project zip downloads.
When PROGRESS is non-nil, allow curl to emit a parseable progress bar."
(append
'("--fail" "--silent" "--show-error" "--location")
(if progress
'("--fail" "--show-error" "--location" "--progress-bar")
'("--fail" "--silent" "--show-error" "--location"))
(git-overleaf--curl-download-timeout-args)))

(defun git-overleaf--socket-cookies ()
Expand All @@ -161,18 +164,145 @@ If REFRESH is non-nil, bypass the cached token and fetch a fresh one."
(format "%s; %s" cookies gclb-cookie)
cookies)))

(defun git-overleaf--curl-download-args (url output-file headers)
"Return curl argument list to download URL into OUTPUT-FILE with HEADERS."
(defun git-overleaf--curl-download-args
(url output-file headers &optional progress)
"Return curl argument list to download URL into OUTPUT-FILE with HEADERS.
When PROGRESS is non-nil, ask curl to emit progress output."
(append
(git-overleaf--curl-download-base-args)
(git-overleaf--curl-download-base-args progress)
(apply #'append (mapcar (lambda (h) (list "-H" h)) headers))
(list "--output" output-file url)))

(defun git-overleaf--curl-download (url output-file headers)
(defconst git-overleaf--curl-download-progress-regexp
"\\([0-9]+\\(?:\\.[0-9]+\\)?\\)%"
"Regexp matching curl progress percentage output.")

(defun git-overleaf--curl-download-progress-percent (text)
"Return the last curl progress percentage in TEXT, or nil."
(let ((start 0)
(percent nil))
(while (string-match git-overleaf--curl-download-progress-regexp
text
start)
(setq percent (floor (string-to-number (match-string 1 text))))
(setq start (match-end 0)))
(when percent
(min 100 (max 0 percent)))))

(defun git-overleaf--curl-download-progress-message (label percent)
"Echo download progress for LABEL at PERCENT without logging it."
(when git-overleaf-log-echo
(message "%s"
(format "[git-overleaf] Downloading %s... %d%%"
label
percent))))

(defmacro git-overleaf--with-optional-mutex (mutex &rest body)
"Evaluate BODY while holding MUTEX when MUTEX is non-nil."
(declare (indent 1) (debug (form body)))
(let ((mutex-var (make-symbol "mutex")))
`(let ((,mutex-var ,mutex))
(if ,mutex-var
(progn
(mutex-lock ,mutex-var)
(unwind-protect
(progn ,@body)
(mutex-unlock ,mutex-var)))
(progn ,@body)))))

(defun git-overleaf--curl-download-run-with-progress
(program args progress-label)
"Run curl PROGRAM with ARGS and echo progress for PROGRESS-LABEL."
(let* ((mutex (and (fboundp 'make-mutex)
(make-mutex "git-overleaf-curl-download")))
(process nil)
(done nil)
(status nil)
(output-chunks nil)
(progress-tail "")
(last-percent nil)
(result nil))
(unwind-protect
(setq
result
(progn
(git-overleaf--curl-download-progress-message progress-label 0)
(setq process
(make-process
:name "git-overleaf-curl-download"
:buffer nil
:command (cons program args)
:connection-type 'pipe
:noquery t
:filter (lambda (_proc string)
(git-overleaf--with-optional-mutex mutex
(push string output-chunks)
(setq progress-tail
(concat progress-tail string))
(when (> (length progress-tail) 200)
(setq progress-tail
(substring progress-tail
(- (length progress-tail)
200))))
(when-let* ((percent
(git-overleaf--curl-download-progress-percent
progress-tail)))
(unless (equal percent last-percent)
(setq last-percent percent)
(git-overleaf--curl-download-progress-message
progress-label
percent)))))
:sentinel (lambda (proc _event)
(when (memq (process-status proc)
'(exit signal))
(git-overleaf--with-optional-mutex mutex
(unless done
(setq status
(process-exit-status proc))
(setq done t)))))))
(git-overleaf--async-register-process process)
(while (not done)
(accept-process-output process 0.05)
(when (fboundp 'thread-yield)
(thread-yield)))
(let ((output nil))
(git-overleaf--with-optional-mutex mutex
(setq output
(string-trim-right
(apply #'concat (nreverse output-chunks)))))
(prog1
(git-overleaf--command-result-or-error
program
args
status
output
nil)
(unless (equal last-percent 100)
(git-overleaf--curl-download-progress-message
progress-label
100))))))
(when (and process (process-live-p process))
(ignore-errors (delete-process process)))
(when process
(git-overleaf--async-unregister-process process)))
result))

(defun git-overleaf--curl-download
(url output-file headers &optional progress-label)
"Download URL into OUTPUT-FILE with HEADERS using curl."
(git-overleaf--run git-overleaf-curl-executable
(git-overleaf--curl-download-args
url output-file headers)))
(let ((program (git-overleaf--ensure-executable git-overleaf-curl-executable))
(args
(git-overleaf--curl-download-args
url
output-file
headers
progress-label)))
(if progress-label
(git-overleaf--curl-download-run-with-progress
program
args
progress-label)
(git-overleaf--run program args))))

(defun git-overleaf--curl-request (method url headers &optional body)
"Run a curl request with METHOD to URL using HEADERS and optional BODY."
Expand Down Expand Up @@ -270,7 +400,11 @@ The response body is discarded."
(prog1
(progn
(git-overleaf--message "Downloading project %s..." project-id)
(git-overleaf--curl-download url zipfile headers)
(git-overleaf--curl-download
url
zipfile
headers
(format "project %s" project-id))
(git-overleaf--run
git-overleaf-unzip-executable
(list "-q" "-o" zipfile "-d" temp-dir))
Expand Down
Loading
Loading