Skip to content

Improve code quality, adaptive redraw, and CI coverage#1

Merged
dakra merged 8 commits intomainfrom
claude/suggest-improvements-L2tCR
Mar 31, 2026
Merged

Improve code quality, adaptive redraw, and CI coverage#1
dakra merged 8 commits intomainfrom
claude/suggest-improvements-L2tCR

Conversation

@dakra
Copy link
Copy Markdown
Owner

@dakra dakra commented Mar 31, 2026

  • Extract shared OSC terminator helper (findOscTerminator + OscScanner)
    to deduplicate terminator detection across OSC 7, 52, and 133 parsers
  • Check ghostty_terminal_set return values via terminalSet helper instead
    of silently discarding them
  • Add adaptive frame rate: shorter initial delay after idle for responsive
    interactive feedback, configurable via ghostel-adaptive-fps
  • Add native module build job to CI using Zig 0.14

https://claude.ai/code/session_01DvxJHyLFPaaZTNYcEFN2hQ

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves reliability and UX around the Ghostty-backed Emacs terminal by deduplicating OSC parsing logic, handling native terminal option set failures more explicitly, introducing adaptive redraw timing, and expanding CI to build the native module.

Changes:

  • Added a terminalSet helper and updated Terminal setter APIs to return errors instead of discarding ghostty_terminal_set results.
  • Refactored OSC 7/52/133 parsing to share terminator scanning via findOscTerminator + OscScanner.
  • Added native module auto-download/load support and adaptive redraw timing in Elisp, plus a new CI job to build the native module with Zig.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
src/terminal.zig Adds terminalSet helper and converts several setters to !void so failures can be handled by callers.
src/module.zig Updates callback/color setup to handle new error-returning APIs; refactors OSC parsing via shared scanner helpers.
ghostel.el Adds module auto-download helpers, adaptive redraw delay logic, and updates module loading behavior.
build.zig Makes Emacs include path configurable and fixes installed artifact suffix based on platform.
.github/workflows/ci.yml Adds a matrix job to build and upload the native module on Linux/macOS using Zig 0.14.1.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread ghostel.el Outdated
Comment on lines +114 to +117
(url (format "%s/download/%s/%s"
ghostel-github-release-url
(if version (concat "v" version) "latest")
asset-name))
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constructed download URL uses /releases/download/latest/... when ghostel--package-version returns nil. GitHub's 'latest release' download URLs use /releases/latest/download/<asset> (or a concrete tag), so this path will 404 and prevent auto-download for non-package installs (and any case where the version can't be resolved).

Suggested change
(url (format "%s/download/%s/%s"
ghostel-github-release-url
(if version (concat "v" version) "latest")
asset-name))
(url (if version
(format "%s/download/v%s/%s"
ghostel-github-release-url
version
asset-name)
(format "%s/latest/download/%s"
ghostel-github-release-url
asset-name)))

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3f7d349 — now uses /releases/latest/download/<asset> for the fallback URL.


Generated by Claude Code

Comment thread ghostel.el Outdated
Comment on lines +106 to +123
(defun ghostel--maybe-download-module (dir)
"Try to download a pre-built module into DIR if available.
Downloads from the latest GitHub release matching the current platform.
Does nothing if the platform is unsupported or the download fails."
(condition-case err
(let ((asset-name (ghostel--module-asset-name)))
(when asset-name
(let* ((version (ghostel--package-version))
(url (format "%s/download/%s/%s"
ghostel-github-release-url
(if version (concat "v" version) "latest")
asset-name))
(dest (expand-file-name
(concat "ghostel-module" module-file-suffix) dir)))
(message "ghostel: downloading native module from %s..." url)
(when (ghostel--download-file url dest)
(message "ghostel: native module downloaded to %s" dest)))))
(error
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change introduces automatic download + module-load of a prebuilt native module from the network without any integrity/authenticity verification (checksum/signature/pinned commit). That is a significant supply-chain risk; consider requiring explicit user confirmation before downloading/loading, and/or verifying an expected hash/signature published with the release before calling module-load.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed: auto-download is now opt-in via ghostel-auto-download-module (default nil). Users must explicitly enable it or use M-x ghostel-download-module. Also added HTTPS enforcement — non-HTTPS URLs are rejected before downloading.


Generated by Claude Code

Comment thread ghostel.el Outdated
Comment on lines +176 to +184
(defun ghostel-module-compile ()
"Build the native module from source using build.sh."
(interactive)
(let ((default-directory (file-name-directory
(or load-file-name
(locate-library "ghostel")
buffer-file-name))))
(compile "./build.sh")))

Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ghostel-module-compile is now defined twice in this file (once here, and again near the end under "Module compilation"). The later definition will overwrite this one, which is confusing and can break expectations (different default-directory, different compile invocation). Please keep a single definition and remove/merge the duplicate.

Suggested change
(defun ghostel-module-compile ()
"Build the native module from source using build.sh."
(interactive)
(let ((default-directory (file-name-directory
(or load-file-name
(locate-library "ghostel")
buffer-file-name))))
(compile "./build.sh")))

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3f7d349 — removed the duplicate, keeping the pre-existing definition.


Generated by Claude Code

Comment thread ghostel.el Outdated
Comment on lines +139 to +150
(with-current-buffer (url-retrieve-synchronously url t t 30)
(set-buffer-multibyte nil)
(goto-char (point-min))
(when (re-search-forward "^HTTP/[0-9.]+ 200" nil t)
(when (re-search-forward "\r?\n\r?\n" nil t)
(let ((coding-system-for-write 'binary)
(start (point)))
(when (< start (point-max))
(write-region start (point-max) dest nil 'silent)
;; Make executable on Unix
(set-file-modes dest #o755)
t))))))
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ghostel--download-file doesn't kill the buffer returned by url-retrieve-synchronously, which can leak temporary buffers over time (and during retries). Consider binding the result to a variable and using unwind-protect to ensure the buffer is killed after extracting/writing the payload.

Suggested change
(with-current-buffer (url-retrieve-synchronously url t t 30)
(set-buffer-multibyte nil)
(goto-char (point-min))
(when (re-search-forward "^HTTP/[0-9.]+ 200" nil t)
(when (re-search-forward "\r?\n\r?\n" nil t)
(let ((coding-system-for-write 'binary)
(start (point)))
(when (< start (point-max))
(write-region start (point-max) dest nil 'silent)
;; Make executable on Unix
(set-file-modes dest #o755)
t))))))
(let ((buf (url-retrieve-synchronously url t t 30)))
(when buf
(unwind-protect
(with-current-buffer buf
(set-buffer-multibyte nil)
(goto-char (point-min))
(when (re-search-forward "^HTTP/[0-9.]+ 200" nil t)
(when (re-search-forward "\r?\n\r?\n" nil t)
(let ((coding-system-for-write 'binary)
(start (point)))
(when (< start (point-max))
(write-region start (point-max) dest nil 'silent)
;; Make executable on Unix
(set-file-modes dest #o755)
t)))))
(when (buffer-live-p buf)
(kill-buffer buf))))))

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3f7d349 — now uses unwind-protect to always kill the buffer.


Generated by Claude Code

Comment thread ghostel.el Outdated
(current-buffer)))))
(let ((delay (if (and ghostel-adaptive-fps ghostel--last-output-time)
(let ((idle-secs (float-time
(time-subtract nil
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

time-subtract expects two time values; passing nil here will raise an error on the second and subsequent redraw scheduling when ghostel-adaptive-fps is enabled (breaking redraw). Use (current-time) (or time-since) instead of nil when computing idle-secs.

Suggested change
(time-subtract nil
(time-subtract (current-time)

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3f7d349 — changed to (current-time).


Generated by Claude Code

Comment thread ghostel.el Outdated
Comment on lines +190 to +196
(unless (file-exists-p mod)
(ghostel--maybe-download-module dir))
(if (file-exists-p mod)
(module-load mod)
(display-warning 'ghostel
(concat "Native module not found: " mod
"\nRun ./build.sh or M-x ghostel-download-module")))))
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this code now may auto-download the native module, module-load failures (corrupt download, wrong architecture/suffix, partial write) will throw an error during (require 'ghostel) and can prevent the package from loading at all. Consider wrapping module-load in condition-case and falling back to a warning + instructions, rather than erroring out during load.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3f7d349module-load is now wrapped in condition-case, falling back to a display-warning with instructions instead of erroring out.


Generated by Claude Code

Comment thread src/module.zig Outdated
while (scanner.next()) |match| {
if (match.payload.len > 0) {
const gs = gt.GhosttyString{ .ptr = match.payload.ptr, .len = match.payload.len };
_ = gt.c.ghostty_terminal_set(term.terminal, gt.OPT_PWD, &gs);
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still discards the return value from ghostty_terminal_set when setting OPT_PWD. Since the PR is introducing terminalSet to detect failures, this path should also check the result (and decide how to handle failure) so PWD updates don't silently fail.

Suggested change
_ = gt.c.ghostty_terminal_set(term.terminal, gt.OPT_PWD, &gs);
const rc = gt.c.ghostty_terminal_set(term.terminal, gt.OPT_PWD, &gs);
if (rc != 0) {
std.log.err("ghostty_terminal_set(OPT_PWD) failed with error code {d}", .{rc});
}

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3f7d349 — added Terminal.setPwd method that checks the return code, used by extractAndSetPwd.


Generated by Claude Code

Comment thread .github/workflows/ci.yml
uses: mlugg/setup-zig@v2
with:
version: 0.14.1

Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The build-native job runs ./build.sh but doesn't install Emacs development headers or set EMACS_INCLUDE_DIR. Since src/emacs.zig includes emacs-module.h, this build is likely to be environment-dependent and can fail on fresh runners. Consider installing an Emacs dev package (or using purcell/setup-emacs here and exporting the include dir) so the job is reproducible.

Suggested change
- name: Install Emacs headers
run: |
if [ "${RUNNER_OS}" = "Linux" ]; then
sudo apt-get update
sudo apt-get install -y emacs-nox
echo "EMACS_INCLUDE_DIR=/usr/include" >> "$GITHUB_ENV"
elif [ "${RUNNER_OS}" = "macOS" ]; then
brew install emacs
EMACS_PREFIX="$(brew --prefix emacs)"
echo "EMACS_INCLUDE_DIR=${EMACS_PREFIX}/include" >> "$GITHUB_ENV"
else
echo "Unsupported OS for installing Emacs headers: ${RUNNER_OS}" >&2
exit 1
fi

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — CI now uses purcell/setup-emacs and a targeted header search instead of raw apt/brew installs.


Generated by Claude Code

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/module.zig Outdated
Comment on lines +83 to +95
term.setUserdata(term) catch {
env.signalError("ghostel: failed to set terminal userdata");
return env.nil();
};
term.setWritePty(&writePtyCallback) catch {
env.signalError("ghostel: failed to set write_pty callback");
return env.nil();
};
term.setBell(&bellCallback) catch {
env.signalError("ghostel: failed to set bell callback");
return env.nil();
};
term.setTitleChanged(&titleChangedCallback) catch {
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In these error paths, the allocated term (and its ghostty resources) is leaked: after Terminal.init succeeds, returning env.nil() from any of these catch blocks skips term.deinit() / destroy(term). Consider errdefer term.deinit(); errdefer std.heap.c_allocator.destroy(term); after init (or manually cleanup in each catch) before signaling the Emacs error.

Suggested change
term.setUserdata(term) catch {
env.signalError("ghostel: failed to set terminal userdata");
return env.nil();
};
term.setWritePty(&writePtyCallback) catch {
env.signalError("ghostel: failed to set write_pty callback");
return env.nil();
};
term.setBell(&bellCallback) catch {
env.signalError("ghostel: failed to set bell callback");
return env.nil();
};
term.setTitleChanged(&titleChangedCallback) catch {
term.setUserdata(term) catch {
term.deinit();
std.heap.c_allocator.destroy(term);
env.signalError("ghostel: failed to set terminal userdata");
return env.nil();
};
term.setWritePty(&writePtyCallback) catch {
term.deinit();
std.heap.c_allocator.destroy(term);
env.signalError("ghostel: failed to set write_pty callback");
return env.nil();
};
term.setBell(&bellCallback) catch {
term.deinit();
std.heap.c_allocator.destroy(term);
env.signalError("ghostel: failed to set bell callback");
return env.nil();
};
term.setTitleChanged(&titleChangedCallback) catch {
term.deinit();
std.heap.c_allocator.destroy(term);

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Fixed — the callback setup now uses a labeled block pattern. If any setter fails, term.deinit() and destroy(term) are called before signaling the error.


Generated by Claude Code

Comment thread ghostel.el Outdated
Comment on lines +141 to +145
"Return ghostel package version string, or nil."
(let ((pkg (and (fboundp 'package-desc-version)
(car (alist-get 'ghostel package-alist)))))
(when pkg
(package-version-join (package-desc-version pkg)))))
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ghostel--package-version uses package-alist without checking it’s bound. When ghostel is loaded outside package.el (e.g., via -L . / straight clone), this can raise void-variable package-alist and make auto-download always fail noisily. Guard with (boundp 'package-alist) (and/or (require 'package)), returning nil when unavailable.

Suggested change
"Return ghostel package version string, or nil."
(let ((pkg (and (fboundp 'package-desc-version)
(car (alist-get 'ghostel package-alist)))))
(when pkg
(package-version-join (package-desc-version pkg)))))
"Return ghostel package version string, or nil.
If `package.el' is not available or `package-alist' is unbound,
return nil without signaling an error."
(when (and (require 'package nil t)
(boundp 'package-alist))
(let ((pkg (car (alist-get 'ghostel package-alist))))
(when pkg
(package-version-join (package-desc-version pkg))))))

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — now guarded with (require 'package nil t) and (boundp 'package-alist), returning nil when package.el is unavailable.


Generated by Claude Code

Comment thread ghostel.el Outdated
Comment on lines +115 to +135
(defun ghostel--maybe-download-module (dir)
"Try to download a pre-built module into DIR if available.
Downloads from the latest GitHub release matching the current platform.
Does nothing if the platform is unsupported or the download fails."
(condition-case err
(let ((asset-name (ghostel--module-asset-name)))
(when asset-name
(let* ((version (ghostel--package-version))
(url (if version
(format "%s/download/v%s/%s"
ghostel-github-release-url
version
asset-name)
(format "%s/latest/download/%s"
ghostel-github-release-url
asset-name)))
(dest (expand-file-name
(concat "ghostel-module" module-file-suffix) dir)))
(message "ghostel: downloading native module from %s..." url)
(when (ghostel--download-file url dest)
(message "ghostel: native module downloaded to %s" dest)))))
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Auto-downloading and then module-loading a native binary lacks any integrity/authenticity checks. At minimum, consider enforcing an https:// URL (reject/ignore non-HTTPS if ghostel-github-release-url is customized) and/or verifying a checksum/signature from the release before writing and loading the file.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed with two mitigations:

  1. Auto-download is opt-in (ghostel-auto-download-module defaults to nil)
  2. HTTPS is now enforced — the download function rejects non-HTTPS URLs before fetching

Checksum/signature verification would be a good future addition but is out of scope for this PR since there are no releases with published checksums yet.


Generated by Claude Code

Comment thread .github/workflows/ci.yml Outdated
Comment on lines +108 to +112
HEADER=$(find / -name emacs-module.h -print -quit 2>/dev/null || true)
if [ -z "$HEADER" ]; then
echo "::error::emacs-module.h not found"
exit 1
fi
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This step does find / -name emacs-module.h, which can be very slow on GitHub-hosted runners and may hit permission-denied noise/timeouts. Consider limiting the search to likely prefixes (e.g. $(dirname $(which emacs))/../include, /usr/include, /usr/local/include, Homebrew prefix on macOS), or use an Emacs/brew query to derive the include dir directly.

Suggested change
HEADER=$(find / -name emacs-module.h -print -quit 2>/dev/null || true)
if [ -z "$HEADER" ]; then
echo "::error::emacs-module.h not found"
exit 1
fi
set -e
# Derive candidate include directories from the Emacs binary and common prefixes.
EMACS_BIN="$(command -v emacs || true)"
EMACS_BIN_DIR=""
if [ -n "$EMACS_BIN" ]; then
EMACS_BIN_DIR="$(dirname "$EMACS_BIN")"
fi
CANDIDATES=""
if [ -n "$EMACS_BIN_DIR" ]; then
CANDIDATES="$CANDIDATES \"$EMACS_BIN_DIR/../include\""
CANDIDATES="$CANDIDATES \"$EMACS_BIN_DIR/../share/emacs/include\""
fi
# Common system include directories on Linux and macOS.
CANDIDATES="$CANDIDATES /usr/include /usr/local/include /opt/homebrew/include /opt/local/include"
HEADER=""
for dir in $CANDIDATES; do
# Skip non-existent directories.
if [ ! -d "$dir" ]; then
continue
fi
# Try direct path first.
if [ -f "$dir/emacs-module.h" ]; then
HEADER="$dir/emacs-module.h"
break
fi
# Fall back to a depth-limited search within the candidate directory.
FOUND="$(find "$dir" -maxdepth 5 -name emacs-module.h -print -quit 2>/dev/null || true)"
if [ -n "$FOUND" ]; then
HEADER="$FOUND"
break
fi
done
if [ -z "$HEADER" ]; then
echo "::error::emacs-module.h not found"
exit 1
fi

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — replaced find / with a targeted search that checks directories relative to the Emacs binary (including Nix store symlinks), then falls back to common system prefixes with maxdepth 5.


Generated by Claude Code

Comment thread .github/workflows/ci.yml Outdated
Comment on lines +74 to +80
build-native:
# Full native build requires the ghostty submodule which needs
# Zig >= 0.15.2 (not yet released as a stable version).
# For now, build on macOS and Linux using Zig 0.15.1 and skip the
# ghostty submodule build — only verify our module compiles against
# a pre-built libghostty-vt if available.
# Once Zig 0.15.2+ is released, switch to: ./build.sh
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI pins Zig to 0.15.1, but the PR description (and ghostel.el commentary) mentions Zig 0.14+. Please align the version/documentation: either set the workflow to the intended 0.14.x baseline, or update the PR description/docs and remove/update the inline comments about 0.15.2 being "not yet released" to avoid stale guidance.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — updated the CI comment to clarify: ghostel itself needs Zig 0.14+, the vendored ghostty submodule needs Zig >= 0.15.2, and CI uses 0.15.1 with zig build check (compile-only, no linking) as a compromise until 0.15.2+ is released.


Generated by Claude Code

@dakra dakra force-pushed the claude/suggest-improvements-L2tCR branch 2 times, most recently from fd90512 to 2f5cd94 Compare March 31, 2026 20:46
claude and others added 8 commits March 31, 2026 23:07
- Extract shared OSC terminator helper (findOscTerminator + OscScanner)
  to deduplicate terminator detection across OSC 7, 52, and 133 parsers
- Check ghostty_terminal_set return values via terminalSet helper instead
  of silently discarding them
- Add adaptive frame rate: shorter initial delay after idle for responsive
  interactive feedback, configurable via ghostel-adaptive-fps
- Add native module build job to CI using Zig 0.14
- Make build.zig platform-aware: detect macOS vs Linux for Emacs header
  paths and output file suffix (.dylib vs .so).  Support EMACS_INCLUDE_DIR
  env var override for custom Emacs installations.
- Add automatic module download: when the native module is missing,
  attempt to fetch a pre-built binary from the GitHub releases page
  matching the current platform (x86_64-linux, aarch64-macos, etc.)
- Add M-x ghostel-download-module for interactive download and
  M-x ghostel-module-compile for building from source
- Update CI to build native module on both Ubuntu and macOS, uploading
  build artifacts for potential use in GitHub releases
- Fix GitHub release URL format: use /releases/latest/download/ for the
  latest release fallback instead of /releases/download/latest/
- Add ghostel-auto-download-module defcustom (default nil) so module
  auto-download requires explicit opt-in, addressing supply-chain concern
- Remove duplicate ghostel-module-compile definition (keep existing one)
- Fix buffer leak in ghostel--download-file: use unwind-protect to always
  kill the url-retrieve-synchronously buffer
- Fix time-subtract call: use (current-time) instead of nil which would
  error on Emacs versions that don't accept nil
- Wrap module-load in condition-case so corrupt/incompatible downloads
  produce a warning instead of breaking (require 'ghostel)
- Check OPT_PWD return value via new Terminal.setPwd method instead of
  discarding it with _ =
- Add Emacs header installation step to CI build-native job so
  emacs-module.h is available on fresh runners
- Fix duplicate `const resolved` declaration in build.zig that caused
  Zig compilation error
- Use purcell/setup-emacs in build-native CI job (same as lint/test
  jobs) to reliably provide emacs-module.h on both platforms
- Dynamically find emacs-module.h location and export EMACS_INCLUDE_DIR

Fix CI: rewrite SSH submodule URLs to HTTPS for CI checkout

The ghostty submodule uses git@github.com: (SSH) which fails in CI
without SSH keys.  Add a step to rewrite SSH URLs to HTTPS before
cloning submodules, so the public ghostty repo can be fetched with
the default GITHUB_TOKEN.

Fix CI: use Zig nightly for ghostty submodule compatibility

The ghostty submodule requires minimum_zig_version = 0.15.2 which
is not yet a stable release.  Use Zig nightly (master) until a
compatible stable version is available.

Fix CI: use zig ast-check instead of full build

The ghostty submodule requires Zig >= 0.15.2 which is not yet a
stable release, so a full ./build.sh is not possible in CI currently.
Instead, verify Zig source files parse correctly using zig ast-check.
The full build job can be restored once Zig 0.15.2+ is released.

Fix CI: compile native module with zig build check

Add a "check" build step to build.zig that compiles all Zig source
files against ghostty and Emacs headers without linking.  This catches
type errors, import issues, and API misuse without needing the
pre-built libghostty-vt static libraries (which require Zig >= 0.15.2
to build).

Also add vendor/ghostty/include as an include path so headers can be
found from the source tree without running the ghostty build first.
- Fix memory leak in fnNew: clean up terminal on callback setup
  failure using a labeled block pattern instead of leaking
- Guard package-alist access with (boundp) and (require 'package)
  to avoid void-variable errors when loaded outside package.el
- Enforce HTTPS for module download URLs to mitigate supply-chain risk
- Replace slow `find /` with targeted header search in CI using
  Emacs binary path, Nix store symlinks, and common system prefixes
- Clarify CI Zig version comment: ghostel needs 0.14+, ghostty
  submodule needs 0.15.2+, CI uses 0.15.1 for compile-only check
Replace ghostel-auto-download-module with ghostel-module-auto-install
which supports four modes:
- 'ask (default): prompt with [d]ownload / [c]ompile / [s]kip
- 'download: silently download pre-built binary from GitHub releases
- 'compile: build from source via ghostel-module-compile
- nil: do nothing, user installs manually

The interactive prompt shows the download URL and mentions Zig as
a requirement for compilation.  Compile mode runs build.sh
synchronously and reports results in *ghostel-build* buffer.
- Rename unused `dir` arg to `_dir` in ghostel--ask-install-action
- Add declare-function for package-version-join and package-desc-version
- Add declare-function for ghostel--scroll-top and ghostel--scroll-bottom
- Move ghostel--copy-mode-active defvar-local before first use
The interactive read-char-choice prompt in ghostel--ensure-module
causes an end-of-file error when Emacs runs in batch mode (e.g. CI
tests).  Skip the prompt when noninteractive is set.
@dakra dakra force-pushed the claude/suggest-improvements-L2tCR branch from 5931f4f to e7b1189 Compare March 31, 2026 21:07
@dakra dakra merged commit e7b1189 into main Mar 31, 2026
8 checks passed
@dakra dakra deleted the claude/suggest-improvements-L2tCR branch April 3, 2026 11:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants