diff --git a/.circleci/config.yml b/.circleci/config.yml index 31918ba..f03a87f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,36 +1,120 @@ version: 2.1 +orbs: + win: circleci/windows@2.2.0 + # Default actions to perform on each Emacs version -default: &default-steps - steps: - - checkout - - run: apt-get update && apt-get install make - - run: make test +commands: + setup: + steps: + - checkout + - run: + name: Install Eldev + command: curl -fsSL https://raw.github.com/doublep/eldev/master/webinstall/circle-eldev > x.sh && source ./x.sh + + macos-setup: + steps: + - checkout + - run: + name: Install Emacs latest + command: | + echo "HOMEBREW_NO_AUTO_UPDATE=1" >> $BASH_ENV + brew install homebrew/cask/emacs + - run: + name: Install Eldev + command: curl -fsSL https://raw.github.com/doublep/eldev/master/webinstall/circle-eldev > x.sh && source ./x.sh + + setup-windows: + steps: + - checkout + - run: + name: Install Eldev + command: | + # Remove expired DST Root CA X3 certificate. Workaround + # for https://debbugs.gnu.org/cgi/bugreport.cgi?bug=51038 + # bug on Emacs 27.2. + gci cert:\LocalMachine\Root\DAC9024F54D8F6DF94935FB1732638CA6AD77C13 + gci cert:\LocalMachine\Root\DAC9024F54D8F6DF94935FB1732638CA6AD77C13 | Remove-Item + (iwr https://raw.github.com/doublep/eldev/master/webinstall/circle-eldev.ps1).Content | powershell -command - + test: + steps: + - run: + name: Run regression tests + command: eldev -dtT -p test + lint: + steps: + - run: + name: Run Elisp-lint + command: eldev lint + - run: + name: Byte-compile `.el' files + command: eldev -dtT compile --warnings-as-errors -# Enumerated list of Emacs versions jobs: - test-emacs-26: + test-ubuntu-emacs-28: + docker: + - image: silex/emacs:28-ci + entrypoint: bash + steps: + - setup + - test + + test-ubuntu-emacs-29: docker: - - image: silex/emacs:26-ci-cask + - image: silex/emacs:29-ci entrypoint: bash - <<: *default-steps + steps: + - setup + - test - test-emacs-27: + test-ubuntu-emacs-30: docker: - - image: silex/emacs:27-ci-cask + - image: silex/emacs:30-ci entrypoint: bash - <<: *default-steps + steps: + - setup + - test - test-emacs-master: + test-ubuntu-emacs-master: docker: - - image: silex/emacs:master-ci-cask + - image: silex/emacs:master-ci entrypoint: bash - <<: *default-steps + steps: + - setup + - test + + test-macos-emacs-latest: + macos: + xcode: "14.0.0" + steps: + - macos-setup + - test + + test-windows-emacs-latest: + executor: win/default + steps: + - run: + name: Install Emacs latest + command: | + choco install emacs + - setup-windows + - test + + test-lint: + docker: + - image: silex/emacs:30-ci + steps: + - setup + - lint workflows: version: 2 ci-test-matrix: jobs: - - test-emacs-26 - - test-emacs-27 - - test-emacs-master + - test-ubuntu-emacs-28 + - test-ubuntu-emacs-29 + - test-ubuntu-emacs-30 + - test-ubuntu-emacs-master + - test-lint + - test-macos-emacs-latest + - test-windows-emacs-latest diff --git a/.gitignore b/.gitignore index c724233..9e62e23 100644 --- a/.gitignore +++ b/.gitignore @@ -8,5 +8,10 @@ elpa* TAGS .DS_STORE dist +.eldev .vagrant/ .dir-locals?.el + +# ELPA-generated files +/inf-clojure-autoloads.el +/inf-clojure-pkg.el diff --git a/CHANGELOG.md b/CHANGELOG.md index e5c87a3..f3c3e90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,111 +2,146 @@ ## master (unreleased) +## 3.3.0 (2025-05-25) + +### New features + +- [#202](https://github.com/clojure-emacs/inf-clojure/issues/202): Add ClojureCLR support. +- [#210](https://github.com/clojure-emacs/inf-clojure/pull/210) Include `inf-clojure-socket-repl` to create a socket REPL and connect to it from inside Emacs. +- [#217](https://github.com/clojure-emacs/inf-clojure/pull/217): Add `clojure-ts-mode` support. + +### Changes + +- Improve support for multiple forms in the same line by replacing `beginning-of-defun` fn. +- [#204](https://github.com/clojure-emacs/inf-clojure/issues/204): Scroll repl buffer on insert commands +- [#208](https://github.com/clojure-emacs/inf-clojure/pull/208) Display message after setting REPL. +- Require Emacs 28. +- Drop support for Lumo. + +## 3.2.1 (2022-07-22) + +### Bugs fixed + +- Address some small issues with NonGNU ELPA (e.g. missing maintainer metadata). + +## 3.2.0 (2022-07-15) + +### New features + +- [#168](https://github.com/clojure-emacs/inf-clojure/pull/197): New helper function `inf-clojure-switch-to-recent-buffer` to select the last buffer an inf-clojure process buffer was swapped to from. +- [#187](https://github.com/clojure-emacs/inf-clojure/pull/197): New defcustom `inf-clojure-enable-eldoc` to disable eldoc interaction. + +### Bugs fixed + +- [#185](https://github.com/clojure-emacs/inf-clojure/issues/185): Improve cmd string splitting. +- [#193](https://github.com/clojure-emacs/inf-clojure/pull/193): Set syntax table in REPL buffer. +- Fix `inf-clojure-display-version` (it wasn't extracting properly the package version). + ## 3.1.0 (2021-07-23) ### New features -* [#190](https://github.com/clojure-emacs/inf-clojure/pull/190): Helper function `inf-clojure-set-repl` to select inf-clojure process buffer. -* Auto-enable `inf-clojure-minor-mode` after invoking `inf-clojure`. This behaviour is controlled via `inf-clojure-auto-mode`. -* Include the project name automatically in the REPL buffer name. +- [#190](https://github.com/clojure-emacs/inf-clojure/pull/190): Helper function `inf-clojure-set-repl` to select inf-clojure process buffer. +- Auto-enable `inf-clojure-minor-mode` after invoking `inf-clojure`. This behaviour is controlled via `inf-clojure-auto-mode`. +- Include the project name automatically in the REPL buffer name. ### Bugs fixed -* [#152](https://github.com/clojure-emacs/inf-clojure/issues/152): Sanitize should only remove whitespace at the end of a command. -* [#188](https://github.com/clojure-emacs/inf-clojure/pull/188): Handle newlines between forms for `inf-clojure-eval-buffer`. -* [#189](https://github.com/clojure-emacs/inf-clojure/pull/189): Font-lock code inserted in the REPL from a source buffer. +- [#152](https://github.com/clojure-emacs/inf-clojure/issues/152): Sanitize should only remove whitespace at the end of a command. +- [#188](https://github.com/clojure-emacs/inf-clojure/pull/188): Handle newlines between forms for `inf-clojure-eval-buffer`. +- [#189](https://github.com/clojure-emacs/inf-clojure/pull/189): Font-lock code inserted in the REPL from a source buffer. ## 3.0.0 (2020-08-01) ### New features -* [#174](https://github.com/clojure-emacs/inf-clojure/pull/174): Invoke `inf-clojure` with a prefix argument to prevent using `inf-clojure-custom-startup` and `inf-clojure-custom-repl-type`. -* Made it possible to add user-defined REPL types (by modifying `inf-clojure-repl-features`). +- [#174](https://github.com/clojure-emacs/inf-clojure/pull/174): Invoke `inf-clojure` with a prefix argument to prevent using `inf-clojure-custom-startup` and `inf-clojure-custom-repl-type`. +- Made it possible to add user-defined REPL types (by modifying `inf-clojure-repl-features`). ### Changes -* **(Breaking)** Restructure massively the configuration. See `inf-clojure-repl-features` for details. -* [#174](https://github.com/clojure-emacs/inf-clojure/pull/174): Set REPL type from startup form or prompt at startup, introduce `inf-clojure-custom-repl-type` defcustom. -* [#173](https://github.com/clojure-emacs/inf-clojure/issues/173): Use clojure-mode's project detection instead of duplicate version in inf-clojure. +- **(Breaking)*- Restructure massively the configuration. See `inf-clojure-repl-features` for details. +- [#174](https://github.com/clojure-emacs/inf-clojure/pull/174): Set REPL type from startup form or prompt at startup, introduce `inf-clojure-custom-repl-type` defcustom. +- [#173](https://github.com/clojure-emacs/inf-clojure/issues/173): Use clojure-mode's project detection instead of duplicate version in inf-clojure. ### Bugs fixed -* [#178](https://github.com/clojure-emacs/inf-clojure/issues/178): Ensure a valid directory is used when starting process. +- [#178](https://github.com/clojure-emacs/inf-clojure/issues/178): Ensure a valid directory is used when starting process. ## 2.2.0 (2020-04-15) ### New features -* [#170](https://github.com/clojure-emacs/inf-clojure/pull/170): Add insert defun and last sexp commands. -* [#160](https://github.com/clojure-emacs/inf-clojure/pull/160): Support [Joker](https://joker-lang.org/). +- [#170](https://github.com/clojure-emacs/inf-clojure/pull/170): Add insert defun and last sexp commands. +- [#160](https://github.com/clojure-emacs/inf-clojure/pull/160): Support [Joker](https://joker-lang.org/). ### Bugs fixed -* [#164](https://github.com/clojure-emacs/inf-clojure/pull/164): Fix for eldoc-mode on ClojureCLR. -* [#135](https://github.com/clojure-emacs/inf-clojure/pull/135): Improve command sanitation code. -* Fix `info-clojure-apropos`. +- [#164](https://github.com/clojure-emacs/inf-clojure/pull/164): Fix for eldoc-mode on ClojureCLR. +- [#135](https://github.com/clojure-emacs/inf-clojure/pull/135): Improve command sanitation code. +- Fix `info-clojure-apropos`. ## 2.1.0 (2018-01-02) ### New Features -* [#114](https://github.com/clojure-emacs/inf-clojure/pull/114): Introduce `inf-clojure-project-type` defcustom. -* [#117](https://github.com/clojure-emacs/inf-clojure/pull/117): Introduce `tools.deps` project type and `inf-clojure-tools-deps-cmd`. -* [#122](https://github.com/clojure-emacs/inf-clojure/pull/122): Introduce `inf-clojure-completions-fn` defcustom. -* [#128](https://github.com/clojure-emacs/inf-clojure/pull/128): Expose `inf-clojure-apropos` as `C-c C-S-a` in `inf-clojure-mode` (the REPL). -* [#125](https://github.com/clojure-emacs/inf-clojure/pull/125): Avoid throwing an error for frequent operations like completion. -* [#130](https://github.com/clojure-emacs/inf-clojure/pull/130): Support loading directory locals in our buffers. -* [#129](https://github.com/clojure-emacs/inf-clojure/pull/129): Improve the completion bounds detection (now with keywords). -* [#132](https://github.com/clojure-emacs/inf-clojure/pull/132): Introduce inf-clojure-reload. +- [#114](https://github.com/clojure-emacs/inf-clojure/pull/114): Introduce `inf-clojure-project-type` defcustom. +- [#117](https://github.com/clojure-emacs/inf-clojure/pull/117): Introduce `tools.deps` project type and `inf-clojure-tools-deps-cmd`. +- [#122](https://github.com/clojure-emacs/inf-clojure/pull/122): Introduce `inf-clojure-completions-fn` defcustom. +- [#128](https://github.com/clojure-emacs/inf-clojure/pull/128): Expose `inf-clojure-apropos` as `C-c C-S-a` in `inf-clojure-mode` (the REPL). +- [#125](https://github.com/clojure-emacs/inf-clojure/pull/125): Avoid throwing an error for frequent operations like completion. +- [#130](https://github.com/clojure-emacs/inf-clojure/pull/130): Support loading directory locals in our buffers. +- [#129](https://github.com/clojure-emacs/inf-clojure/pull/129): Improve the completion bounds detection (now with keywords). +- [#132](https://github.com/clojure-emacs/inf-clojure/pull/132): Introduce inf-clojure-reload. ### Bugs Fixed -* [#79](https://github.com/clojure-emacs/inf-clojure/pull/82): Eldoc error when running boot repl. -* [#83](https://github.com/clojure-emacs/inf-clojure/pull/85): No such namespace: complete.core in lumo REPL. -* [#93](https://github.com/clojure-emacs/inf-clojure/pull/93): Slow response from inf-clojure (completions, arglists, ...). -* [#101](https://github.com/clojure-emacs/inf-clojure/pull/101): `inf-clojure-set-ns` hangs Emacs. -* [#119](https://github.com/clojure-emacs/inf-clojure/pull/119): Set inf-clojure-buffer REPL type on detect. -* [#120](https://github.com/clojure-emacs/inf-clojure/pull/120): Send REPL string always, even if empty. -* [#128](https://github.com/clojure-emacs/inf-clojure/pull/128): Fix inf-clojure-apropos. -* [#131](https://github.com/clojure-emacs/inf-clojure/pull/131): Add macroexpand forms for Lumo. +- [#79](https://github.com/clojure-emacs/inf-clojure/pull/82): Eldoc error when running boot repl. +- [#83](https://github.com/clojure-emacs/inf-clojure/pull/85): No such namespace: complete.core in lumo REPL. +- [#93](https://github.com/clojure-emacs/inf-clojure/pull/93): Slow response from inf-clojure (completions, arglists, ...). +- [#101](https://github.com/clojure-emacs/inf-clojure/pull/101): `inf-clojure-set-ns` hangs Emacs. +- [#119](https://github.com/clojure-emacs/inf-clojure/pull/119): Set inf-clojure-buffer REPL type on detect. +- [#120](https://github.com/clojure-emacs/inf-clojure/pull/120): Send REPL string always, even if empty. +- [#128](https://github.com/clojure-emacs/inf-clojure/pull/128): Fix inf-clojure-apropos. +- [#131](https://github.com/clojure-emacs/inf-clojure/pull/131): Add macroexpand forms for Lumo. ## 2.0.1 (2017-05-18) ### Bugs Fixed -* [#77](https://github.com/clojure-emacs/inf-clojure/pull/77): Fix request "Eval expression:" if arglists return is `nil`. +- [#77](https://github.com/clojure-emacs/inf-clojure/pull/77): Fix request "Eval expression:" if arglists return is `nil`. ## 2.0.0 (2017-05-01) ### New Features -* [#63](https://github.com/clojure-emacs/inf-clojure/pull/69): Fix spurious process output on init. -* [#57](https://github.com/clojure-emacs/inf-clojure/pull/68): Add `inf-clojure-connect`. -* [#66](https://github.com/clojure-emacs/inf-clojure/pull/56): Add Planck support. -* [#51](https://github.com/clojure-emacs/inf-clojure/pull/51): Commands do not prompt by default anymore, unless they receive a non-nil prefix argument. -* [#44](https://github.com/clojure-emacs/inf-clojure/pull/44): Add REPL types and Lumo support. -* [#50](https://github.com/clojure-emacs/inf-clojure/pull/50): Rename defcustoms to `inf-clojure-*-form` where appropriate. -* [#34](https://github.com/clojure-emacs/inf-clojure/pull/34): Add support for socket REPL connections. -* New interactive command `inf-clojure-display-version`. -* [#42](https://github.com/clojure-emacs/inf-clojure/issues/42): Add a defcustom controlling the window in which the REPL buffer is displayed (`inf-clojure-repl-use-same-window`). -* Font-lock the code in the REPL. -* Handle properly ANSI color escape sequences in the REPL. -* [#41](https://github.com/clojure-emacs/inf-clojure/issues/41): Add a command to quit the REPL (it's bound to `C-c C-q`). -* [#29](https://github.com/clojure-emacs/inf-clojure/issues/29): Add a command to restart the REPL. -* [#31](https://github.com/clojure-emacs/inf-clojure/issues/31): Invoke different init command based on the project type (boot, lein or generic). +- [#63](https://github.com/clojure-emacs/inf-clojure/pull/69): Fix spurious process output on init. +- [#57](https://github.com/clojure-emacs/inf-clojure/pull/68): Add `inf-clojure-connect`. +- [#66](https://github.com/clojure-emacs/inf-clojure/pull/56): Add Planck support. +- [#51](https://github.com/clojure-emacs/inf-clojure/pull/51): Commands do not prompt by default anymore, unless they receive a non-nil prefix argument. +- [#44](https://github.com/clojure-emacs/inf-clojure/pull/44): Add REPL types and Lumo support. +- [#50](https://github.com/clojure-emacs/inf-clojure/pull/50): Rename defcustoms to `inf-clojure-*-form` where appropriate. +- [#34](https://github.com/clojure-emacs/inf-clojure/pull/34): Add support for socket REPL connections. +- New interactive command `inf-clojure-display-version`. +- [#42](https://github.com/clojure-emacs/inf-clojure/issues/42): Add a defcustom controlling the window in which the REPL buffer is displayed (`inf-clojure-repl-use-same-window`). +- Font-lock the code in the REPL. +- Handle properly ANSI color escape sequences in the REPL. +- [#41](https://github.com/clojure-emacs/inf-clojure/issues/41): Add a command to quit the REPL (it's bound to `C-c C-q`). +- [#29](https://github.com/clojure-emacs/inf-clojure/issues/29): Add a command to restart the REPL. +- [#31](https://github.com/clojure-emacs/inf-clojure/issues/31): Invoke different init command based on the project type (boot, lein or generic). ### Changes -* Display the REPL in a different window by default (it used to be displayed in the current window). -* [#26](https://github.com/clojure-emacs/inf-clojure/issues/26): Make switching to the REPL optional on `inf-clojure-load-file` (it's now controlled via a prefix argument). -* Removed the `inf-clojure` alias `run-clojure`. +- Display the REPL in a different window by default (it used to be displayed in the current window). +- [#26](https://github.com/clojure-emacs/inf-clojure/issues/26): Make switching to the REPL optional on `inf-clojure-load-file` (it's now controlled via a prefix argument). +- Removed the `inf-clojure` alias `run-clojure`. ### Bugs Fixed -* [#35](https://github.com/clojure-emacs/inf-clojure/issues/35): Fix prompt being included in input history. +- [#35](https://github.com/clojure-emacs/inf-clojure/issues/35): Fix prompt being included in input history. ## 1.4.0 (2016-01-17) ### New Features -* [#22](https://github.com/clojure-emacs/inf-clojure/pull/22): Add ElDoc support. +- [#22](https://github.com/clojure-emacs/inf-clojure/pull/22): Add ElDoc support. diff --git a/Cask b/Cask deleted file mode 100644 index d2a5667..0000000 --- a/Cask +++ /dev/null @@ -1,11 +0,0 @@ -(source gnu) -(source melpa) - -(package-file "inf-clojure.el") - -(files "*.el" (:exclude ".dir-locals.el")) - -(development - (depends-on "clojure-mode") - (depends-on "buttercup") - (depends-on "assess")) diff --git a/Eldev b/Eldev new file mode 100644 index 0000000..80ae7b1 --- /dev/null +++ b/Eldev @@ -0,0 +1,10 @@ +; -*- mode: emacs-lisp; lexical-binding: t; no-byte-compile: t -*- + +(eldev-use-package-archive 'gnu) +(eldev-use-package-archive 'nongnu) +(eldev-use-package-archive 'melpa-stable) + +;; the tests depend on the assess library +(eldev-add-extra-dependencies 'test 'assess) + +(eldev-use-plugin 'autoloads) diff --git a/Makefile b/Makefile deleted file mode 100644 index d002f72..0000000 --- a/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -.PHONY: clean version test - -all: build - -clean: - rm -rf .cask - -.cask: - cask install - cask update - -version: - emacs --version - -build: version .cask - cask build - -test: version .cask - cask exec buttercup -L . diff --git a/README.md b/README.md index 135945b..c6322c5 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,25 @@ -[![License GPL 3][badge-license]][copying] +[![Circle CI][circleci-badge]][circleci] [![MELPA][melpa-badge]][melpa-package] [![MELPA Stable][melpa-stable-badge]][melpa-stable-package] -[![Circle CI][circleci-badge]][circleci] -[![Discord](https://img.shields.io/badge/chat-on%20discord-7289da.svg?sanitize=true)](https://discord.com/invite/nFPpynQPME) +[![NonGNU ELPA](https://elpa.nongnu.org/nongnu/inf-clojure.svg)](https://elpa.nongnu.org/nongnu/inf-clojure.html) +[![License GPL 3][badge-license]][copying] # inf-clojure This package provides basic interaction with a Clojure subprocess (REPL). It's based on ideas from the popular `inferior-lisp` package. -`inf-clojure` has two components - a nice REPL buffer (`inf-clojure`) and a REPL +`inf-clojure` has two components - a nice REPL buffer (`inf-clojure-mode`) and a REPL interaction minor mode (`inf-clojure-minor-mode`), which extends `clojure-mode` with commands to evaluate forms directly in the REPL. ------------ - -**This documentation tracks the `master` branch of `inf-clojure`. Some of -the features and settings discussed here might not be available in -older releases (including the current stable release). Please, consult -the relevant git tag (e.g. 2.2.0) if you need documentation for a -specific `inf-clojure` release.** -*** +> [!IMPORTANT] +> +> This documentation tracks the `master` branch of `inf-clojure`. Some of +> the features and settings discussed here might not be available in +> older releases (including the current stable release). Please, consult +> the relevant git tag (e.g. 2.2.0) if you need documentation for a +> specific `inf-clojure` release. ## Overview @@ -30,57 +29,67 @@ It supports the following REPLs: - Clojure - ClojureScript +- ClojureCLR (via [lein-clr](https://github.com/kumarshantanu/lein-clr)) - [Planck](http://planck-repl.org/) -- [Lumo](https://github.com/anmonteiro/lumo) - [Joker](https://joker-lang.org/) - [babashka](https://github.com/borkdude/babashka) `inf-clojure` provides a set of essential features for interactive -Clojure(Script) development: +Clojure/ClojureScript/ClojureCLR development: -* Enhanced REPL -* Interactive code evaluation -* Code completion -* Definition lookup -* Documentation lookup -* ElDoc -* Apropos -* Macroexpansion -* Reloading a namespace (via `require :reload`/`require :reload-all`) -* Connecting to socket REPLs +- Enhanced REPL +- Interactive code evaluation +- Code completion +- Definition lookup +- Documentation lookup +- ElDoc +- Apropos +- Macroexpansion +- Reloading a namespace (via `require :reload`/`require :reload-all`) +- Connecting to socket REPLs -For a more powerful/full-featured solution see [CIDER](https://github.com/clojure-emacs/cider). +For a more powerful/full-featured solution see [CIDER][]. ## Rationale -`inf-clojure`'s goal is to provide the simplest possible way to interact with a Clojure REPL. -In Emacs terminology "inferior" process is a subprocess started by Emacs (it being the "superior" process, of course). +`inf-clojure`'s goal is to provide the simplest possible way to interact with a +Clojure REPL. In Emacs terminology "inferior" process is a subprocess started +by Emacs (it being the "superior" process, of course). -`inf-clojure` doesn't require much of setup, as at its core it simply runs a terminal REPL process, pipes input to it, and -processes its output. As the Clojure socket REPL works in exactly the same manner `inf-clojure` can also interact with it. +`inf-clojure` doesn't require much of setup, as at its core it simply runs a +terminal REPL process, pipes input to it, and processes its output. As the +Clojure socket REPL works in exactly the same manner `inf-clojure` can also +interact with it. -Functionality like code completion and eldoc is powered by evaluation of predefined code snippets that provide the necessary results. -As different Clojure REPLs have different capabilities, `inf-clojure` tracks the type of a REPL and invokes -the right code for each REPL type. +Functionality like code completion and eldoc is powered by evaluation of +predefined code snippets that provide the necessary results. As different +Clojure REPLs have different capabilities, `inf-clojure` tracks the type of a +REPL and invokes the right code for each REPL type. -`inf-clojure` is built on top of Emacs's [comint](https://github.com/emacs-mirror/emacs/blob/master/lisp/comint.el). Unfortunately `comint` is pretty light on official documentation, but there is a good overview/tutorial [here](https://www.masteringemacs.org/article/comint-writing-command-interpreter). +`inf-clojure` is built on top of Emacs's +[comint](https://github.com/emacs-mirror/emacs/blob/master/lisp/comint.el). Unfortunately +`comint` is pretty light on official documentation, but there is a good +overview/tutorial +[here](https://www.masteringemacs.org/article/comint-writing-command-interpreter). ## Installation -**Note:** `inf-clojure` requires Emacs 25 or newer. +> [!IMPORTANT] +> +> `inf-clojure` requires Emacs 28 or newer. -`inf-clojure` is available on the community-maintained `package.el` repos - +`inf-clojure` is available on the official [NonGNU ELPA](https://elpa.nongnu.org/nongnu/inf-clojure.html) `package.el` repo and on the community-maintained [MELPA Stable][] and [MELPA][] repos. -MELPA Stable is recommended as it has the latest stable version. +NonGNU ELPA and MELPA Stable are recommended as they have the latest stable version. MELPA has a development snapshot for users who don't mind breakage but -don't want to run from a git checkout. +don't want to run `inf-clojure` from a git checkout. You can install `inf-clojure` using the following command: M-x package-install [RET] inf-clojure [RET] -or if you'd rather keep it in your dotfiles: +or if you'd rather keep it in your Emacs config: ```emacs-lisp (unless (package-installed-p 'inf-clojure) @@ -97,15 +106,22 @@ If the installation doesn't work try refreshing the package list: `nil`. You can also add the following to your Emacs config to enable -`inf-clojure-minor-mode` for Clojure source buffers, regardless of whether there's an `inf-clojure` REPL running: +`inf-clojure-minor-mode` for Clojure source buffers, regardless of whether +there's an `inf-clojure` REPL running: ```emacs-lisp (add-hook 'clojure-mode-hook #'inf-clojure-minor-mode) + +;; or if you're a `clojure-ts-mode' user: + +(add-hook 'clojure-ts-mode-hook #'inf-clojure-minor-mode) ``` -**Warning:** Don't enable `inf-clojure-minor-mode` and `cider-mode` at the same time. They -have overlapping functionality and keybindings and the result will be nothing -short of havoc. +> [!WARNING] +> +> Don't enable `inf-clojure-minor-mode` and `cider-mode` at the same time. They +> have overlapping functionality and keybindings and the result will be nothing +> short of havoc. ## Basic Usage @@ -115,17 +131,20 @@ common startup forms. You can select one of these or type in your own custom startup. This will start a REPL process for the current project and you can start interacting with it. +If you want to use a socket REPL server, use `M-x inf-clojure-socket-repl` +which will start a socket server and connect to it for you. + If you've already started a socket REPL server, use `M-x inf-clojure-connect` and enter its host and port numbers. Inf-clojure aims to be very simple and offer tooling that the REPL itself exposes. A few commands are: -- eval last sexp `C-x C-e` -- show arglists for function `C-c C-a` -- show var documentation `C-c C-v` -- show source `C-c C-s` -- insert top level form into REPL `C-c C-j d` +- eval last sexp (`C-x C-e`) +- show arglists for function (`C-c C-a`) +- show var documentation (`C-c C-v`) +- show source (`C-c C-s`) +- insert top level form into REPL (`C-c C-j d`) For a list of all available commands in `inf-clojure-mode` (a.k.a. the REPL) and `inf-clojure-minor-mode` you can either invoke `C-h f RET @@ -139,15 +158,13 @@ for the symbol you want to show the docstring for. ## Configuration -**Note:** The configuration options were changed massively in `inf-clojure` 3.0. - In the time-honoured Emacs tradition `inf-clojure`'s behaviour is extremely configurable. You can set custom values to `inf-clojure` variables on a per-project basis using [directory variables](https://www.gnu.org/software/emacs/manual/html_node/emacs/Directory-Variables.html) -or by setting them in in your init file. +or by setting them in in your [init file][Emacs init file]. You can see all the configuration options available using the command `M-x customize-group RET inf-clojure`. @@ -165,14 +182,16 @@ that goes with it. This is most easily achieved with the following `.dir-locals. (inf-clojure-custom-repl-type . clojure))) ``` -**Note:** This file has to be in the directory in which you're invoking `inf-clojure` or a parent -directory. +> [!IMPORTANT] +> +> This file has to be in the directory in which you're invoking +> `inf-clojure` or a parent directory. There are two important configuration variables here: 1. `inf-clojure-custom-startup`: Which startup command to use so inf-clojure can run the inferior Clojure process (REPL). -2. `inf-clojure-custom-repl-type`: The type of the REPL started by the above command (e.g. `lumo`). +2. `inf-clojure-custom-repl-type`: The type of the REPL started by the above command (e.g. `planck`). If these are set and you wish to prevent inf-clojure from using them, use a prefix arg when invoking `inf-clojure` (`C-u M-x inf-clojure`). @@ -209,7 +228,19 @@ If you want to update a specific form there is a function `inf-clojure-update-repl-feature` which can be used like so: ```emacs-lisp -(inf-clojure-update-feature 'clojure 'completion "(complete.core/completions \"%s\")") +(inf-clojure-update-feature 'clojure 'completion "(incomplete.core/completions \"%s\")") +``` + +### `clojure-ts-mode` support + +`inf-clojure` will try to use `clojure-ts-mode` by default if it's +available with fallback to `clojure-mode`. + +If you want to use `inf-clojure` with `clojure-mode` exclusively, you +can set it to: + +```emacs-lisp +(setopt inf-clojure-source-modes '(clojure-mode)) ``` #### Caveats @@ -224,7 +255,7 @@ If you decide _not_ to use the socket REPL, it is highly recommended you disable output coloring and/or `readline` facilities: `inf-clojure` does not filter out ASCII escape characters at the moment and will not behave correctly. -For Leiningen, there are no command-line switches and you need to add +For [Leiningen][], there are no command-line switches and you need to add a custom [`project.clj` option](https://github.com/technomancy/leiningen/blob/master/sample.project.clj): @@ -238,10 +269,12 @@ option](https://github.com/technomancy/leiningen/blob/master/sample.project.clj) If you have the new [Clojure CLI tools][] installed you can use the `clojure` command: -_do not use `clj` because it adds readline support_ +> [!IMPORTANT] +> +> Do not use `clj` because it adds readline support. -``` shellsession -$ clojure -J-Dclojure.server.repl="{:port 5555 :accept clojure.core.server/repl}" +``` shell +clojure -J-Dclojure.server.repl="{:port 5555 :accept clojure.core.server/repl}" ``` Then either `C-c M-c RET localhost RET 5555` from within Emacs or add the following to your `.dir-locals.el`: @@ -249,6 +282,7 @@ Then either `C-c M-c RET localhost RET 5555` from within Emacs or add the follow ```emacs-lisp ((nil . ((inf-clojure-custom-startup . ("localhost" . 5555))))) ``` + #### Leiningen Socket REPL For Leiningen, add the following option to your `~/.lein/profiles.clj` or your `project.clj`: @@ -266,19 +300,8 @@ information on how connect: ((nil (inf-clojure-custom-startup "localhost" . 5555))) ``` -The socket server REPL configuration options are described [here](https://clojure.org/reference/repl_and_main#_launching_a_socket_server). - -#### Lumo Socket REPL - -Lumo is decoupled from `inf-clojure-project-type` and therefore the command used depends on what you are using for dependency resolution. - -For example if a `project.clj` is present in the project root folder, `inf-clojure-lein-cmd` will be used. - -After you launch `lumo ... -n 5555`, as customary, either `C-c M-c RET localhost RET 5555` from within Emacs or add the following to your `.dir-locals.el`: - -```emacs-lisp -((nil (inf-clojure-custom-startup "localhost" . 5555))) -``` +The socket server REPL configuration options are described +[here](https://clojure.org/reference/repl_and_main#_launching_a_socket_server). #### Multiple Process Support @@ -289,9 +312,11 @@ process with another `inf-clojure`. It will be in a new buffer, named `*inf-clojure*`. You can switch between the different process buffers with `switch-to-buffer`. -**Note:** If you're starting `inf-clojure` within a Clojure project directory -the name of the project will be incorporated into the name of the REPL buffer -- e.g. `*inf-clojure my-project*`. +> [!NOTE] +> +> If you're starting `inf-clojure` within a Clojure project directory the name +> of the project will be incorporated into the name of the REPL buffer - +> e.g. `*inf-clojure my-project*`. Commands that send text from source buffers to Clojure processes (like `inf-clojure-eval-defun` or `inf-clojure-show-arglists`) have to choose a process to send to, when you have more than @@ -330,10 +355,12 @@ acceptable buffers to set as the REPL buffer. If called with a prefix, it will always give the list even if you are currently in an acceptable REPL buffer. -**Tip:** Renaming buffers will greatly improve the -functionality of this list; the list "project-1: clojure repl", -"project-2: cljs repl" is far more understandable than "inf-clojure", -"inf-clojure<2>". +> [!TIP] +> +> Renaming buffers will greatly improve the +> functionality of this list; the list "project-1: clojure repl", +> "project-2: cljs repl" is far more understandable than "inf-clojure", +> "inf-clojure<2>". #### REPL Type @@ -343,7 +370,7 @@ obtained from `inf-clojure-repl-features`: ```emacs-lisp (mapcar 'car inf-clojure-repl-features) -;; => (cljs lumo planck joker clojure babashka) +;; => (cljs planck joker clojure babashka) ``` What does it mean that a REPL type is supported? Well, it means that @@ -355,18 +382,19 @@ startup when using the `inf-clojure` command or is specified manually when using #### ElDoc -**Note:** You can skip this section if you're using Emacs 26.1+, as `eldoc-mode` -is enabled by default there. - `eldoc-mode` is supported in Clojure source buffers and `*inferior-clojure*` buffers which are running a Clojure REPL. -When ElDoc is enabled and there is an active REPL, it will show the -argument list of the function call you are currently editing in the -echo area. +When ElDoc is enabled and there is an active REPL, it will show the argument +list of the function call you are currently editing in the echo area. It +accomplishes this by evaluating forms to get the metadata for the vars under +your cursor. One side effect of this is that it can mess with repl vars like +`*1` and `*2`. You can disable inf-clojure's Eldoc functionality with `(setq +inf-clojure-enable-eldoc nil)`. -You can activate ElDoc with `M-x eldoc-mode` or by adding the -following to you Emacs config: +ElDoc should be enabled by default in Emacs 26.1+. If it is not active by +default, you can activate ElDoc with `M-x eldoc-mode` or by adding the following +to you Emacs config: ```emacs-lisp (add-hook 'clojure-mode-hook #'eldoc-mode) @@ -379,60 +407,51 @@ You can leave it enabled, it just won't show anything in the echo area. #### Code Completion Code completion is a tricky aspect if you are trying to be as close to -a generic REPL as possible. Planck and lumo REPL implementations +a generic REPL as possible. Some runtimes (e.g. Planck) explicitly provide completion functions in their REPL namespaces. For clojure, you will need to have a library on your classpath. If you are -using lein, you already have -[clojure-complete](https://github.com/ninjudd/clojure-complete). You +using a recent version of Leiningen, you already have +[incomplete](https://github.com/nrepl/incomplete). You could alternatively use `compliment {:mvn/version "0.3.10"}`. ```emacs-lisp -;; for clojure-complete -(inf-clojure-update-feature 'clojure 'completion "(complete.core/completions \"%s\")") +;; for incomplete +(inf-clojure-update-feature 'clojure 'completion "(incomplete.core/completions \"%s\")") ;; or ;; for compliment (inf-clojure-update-feature 'clojure 'completion "(compliment.core/completions \"%s\")") - ``` If you give a form for the completion form, it is your responsibility to ensure that this namespace is on the classpath and required. If -using lein, this is done for you with clojure-complete. If adding -compliment, the following sample deps.edn can conveniently add the dep -to your program. - -Sample deps.edn: +using Leiningen, this is done for you with `incomplete`. If adding +`compliment`, the following sample `deps.edn` can conveniently add the dep +to your program: ```clojure {:aliases {:compliment {:extra-deps {compliment {:mvn/version "0.3.10"}}}}} ``` - Use the startup command: `clojure -A:compliment`. Then require the ns once so that the completion machinery will work: `(require 'compliment.core)`. Now tab completion should work. For more advanced customization, code completion is particularly open to customization. Not only you can `setq` the customary -`inf-clojure-completion-form`, `inf-clojure-completion-form-lumo`, +`inf-clojure-completion-form`, `inf-clojure-completion-form-planck` and `inf-clojure-completion-form-joker` - the form to send to the REPL - but you can also use `inf-clojure-completions-fn` for specifying a -function that given the REPL response should return elisp data +function that given the REPL response should return Elisp data compatible with [`completion-at-point-functions`](https://www.gnu.org/software/emacs/manual/html_node/elisp/Completion-in-Buffers.html). + For more info run `M-x describe-variable RET inf-clojure-completions-fn`. Another option is to have a look at [how -cider does +CIDER does it](https://github.com/clojure-emacs/cider/blob/3e9ed12e8cfbad04d7618e649322765dc9bff5d6/cider-interaction.el#L595). -#### Lumo Setup - -For an optimal Lumo experience the `-d` needs to be passed to Lumo -when launched from the command line. This disable `readline` support -in order to play nicely with emacs. - ## Troubleshooting ### Things seem broken @@ -440,10 +459,10 @@ in order to play nicely with emacs. Inf-clojure is intentionally quite simple and just sends commands to a REPL on your behalf to provide features. In order to do this inf-clojure largely needs to know the REPL type so it can format the -correct calls. Most end up in `(lumo.repl/doc [symbol])` or +correct calls. Most end up in `(planck.repl/doc [symbol])` or `(cljs.repl/doc ...)` so its important that the REPL type is set correctly. This REPL type exists in the process buffer (REPL) and the -source buffers as a cache. If you have problems, run `m-x +source buffers as a cache. If you have problems, run `M-x inf-clojure-set-repl-type` from the source buffer to set the REPL type in both buffers. To see how simple inf-clojure is, look at `inf-clojure-repl-features` to see largely how things are laid out. @@ -459,7 +478,7 @@ The explanation of this problem and solution can be found [here](https://groups. The solution is to create a file named `.jline.rc` in your `$HOME` directory and add this line to that file: -``` +```ini jline.terminal=unsupported ``` @@ -475,7 +494,7 @@ This creates `.inf-clojure.log` in the project directory so that you can `tail - ## License -Copyright © 2014-2021 Bozhidar Batsov and [contributors][]. +Copyright © 2014-2025 Bozhidar Batsov and [contributors][]. Distributed under the GNU General Public License; type C-h C-c to view it. @@ -494,4 +513,3 @@ Distributed under the GNU General Public License; type C-h C-c to vie [melpa stable]: http://stable.melpa.org [Emacs init file]: https://www.gnu.org/software/emacs/manual/html_node/emacs/Init-File.html [Clojure cli tools]: https://clojure.org/guides/getting_started -[Boot]: http://boot-clj.com diff --git a/inf-clojure.el b/inf-clojure.el index f3f6665..37f91ed 100644 --- a/inf-clojure.el +++ b/inf-clojure.el @@ -1,13 +1,14 @@ -;;; inf-clojure.el --- Run an external Clojure process in an Emacs buffer -*- lexical-binding: t; -*- +;;; inf-clojure.el --- Basic interaction with a Clojure REPL -*- lexical-binding: t; -*- -;; Copyright © 2014-2021 Bozhidar Batsov +;; Copyright © 2014-2025 Bozhidar Batsov -;; Authors: Bozhidar Batsov +;; Authors: Bozhidar Batsov ;; Olin Shivers +;; Maintainer: Bozhidar Batsov ;; URL: http://github.com/clojure-emacs/inf-clojure -;; Keywords: processes, clojure -;; Version: 3.1.0 -;; Package-Requires: ((emacs "25.1") (clojure-mode "5.11")) +;; Keywords: processes, comint, clojure +;; Version: 3.3.0 +;; Package-Requires: ((emacs "28.1") (clojure-mode "5.11")) ;; This file is not part of GNU Emacs. @@ -35,7 +36,7 @@ ;; REPL. ;; ;; `inf-clojure` provides a set of essential features for interactive -;; Clojure(Script) development: +;; Clojure/ClojureScript/ClojureCLR development: ;; ;; * REPL ;; * Interactive code evaluation @@ -46,7 +47,6 @@ ;; * Apropos ;; * Macroexpansion ;; * Support connecting to socket REPLs -;; * Support for Lumo ;; * Support for Planck ;; * Support for Joker ;; @@ -64,19 +64,21 @@ (require 'comint) (require 'clojure-mode) +(require 'clojure-ts-mode nil :no-error) (require 'eldoc) (require 'thingatpt) (require 'ansi-color) (require 'cl-lib) (require 'subr-x) +(require 'project) (defvar inf-clojure-startup-forms '((lein . "lein repl") (boot . "boot repl") (clojure . "clojure") - (cljs . "clojure -m cljs.main -r") + (cljs . "clojure -M -m cljs.main -r") + (lein-clr . "lein clr repl") (planck . "planck -d") (babashka . "bb") - (lumo . "lumo -d") (joker . "joker"))) (defvar inf-clojure-repl-features @@ -88,23 +90,6 @@ (set-ns . "(in-ns '%s)") (macroexpand . "(cljs.core/macroexpand '%s)") (macroexpand-1 . "(cljs.core/macroexpand-1 '%s)"))) - (lumo . ((load . "(clojure.core/load-file \"%s\")") - (doc . "(lumo.repl/doc %s)") - (source . "(lumo.repl/source %s)") - (arglists . - "(let [old-value lumo.repl/*pprint-results*] - (set! lumo.repl/*pprint-results* false) - (js/setTimeout #(set! lumo.repl/*pprint-results* old-value) 0) - (lumo.repl/get-arglists \"%s\"))") - (apropos . "(lumo.repl/apropos \"%s\")") - (ns-vars . "(lumo.repl/dir %s)") - (set-ns . "(in-ns '%s)") - (macroexpand . "(macroexpand-1 '%s)") - (macroexpand-1 . "(macroexpand-1 '%s)") - (completion . - "(let [ret (atom nil)] - (lumo.repl/get-completions \"%s\" (fn [res] (reset! ret (map str res)))) - @ret)"))) (planck . ((load . "(load-file \"%s\")") (doc . "(planck.repl/doc %s)") (source . "(planck.repl/source %s)") @@ -138,6 +123,17 @@ (set-ns . "(clojure.core/in-ns '%s)") (macroexpand . "(clojure.core/macroexpand '%s)") (macroexpand-1 . "(clojure.core/macroexpand-1 '%s)"))) + (node-babashka . ((load . "(clojure.core/load-file \"%s\")") + (doc . "(clojure.repl/doc %s)") + (source . "(clojure.repl/source %s)") + (arglists . + "(try (-> '%s clojure.core/resolve clojure.core/meta :arglists) + (catch Throwable e nil))") + (apropos . "(doseq [var (sort (clojure.repl/apropos \"%s\"))] (println (str var)))") + (ns-vars . "(clojure.repl/dir %s)") + (set-ns . "(clojure.core/in-ns '%s)") + (macroexpand . "(clojure.core/macroexpand '%s)") + (macroexpand-1 . "(clojure.core/macroexpand-1 '%s)"))) (clojure . ((load . "(clojure.core/load-file \"%s\")") (doc . "(clojure.repl/doc %s)") (source . "(clojure.repl/source %s)") @@ -152,7 +148,63 @@ (ns-vars . "(clojure.repl/dir %s)") (set-ns . "(clojure.core/in-ns '%s)") (macroexpand . "(clojure.core/macroexpand '%s)") - (macroexpand-1 . "(clojure.core/macroexpand-1 '%s)"))))) + (macroexpand-1 . "(clojure.core/macroexpand-1 '%s)"))) + (lein-clr . ((load . "(clojure.core/load-file \"%s\")") + (doc . "(clojure.repl/doc %s)") + (source . "(clojure.repl/source %s)") + (arglists . + "(try + (:arglists + (clojure.core/meta + (clojure.core/resolve + (clojure.core/read-string \"%s\")))) + (catch Exception e nil))") + (apropos . "(doseq [var (sort (clojure.repl/apropos \"%s\"))] (println (str var)))") + (ns-vars . "(clojure.repl/dir %s)") + (set-ns . "(clojure.core/in-ns '%s)") + (macroexpand . "(clojure.core/macroexpand '%s)") + (macroexpand-1 . "(clojure.core/macroexpand-1 '%s)"))))) + +(defvar-local inf-clojure-repl-type nil + "Symbol to define your REPL type. +Its root binding is nil and it can be further customized using +either `setq-local` or an entry in `.dir-locals.el`." ) + +(defvar inf-clojure-buffer nil + "The current `inf-clojure' process buffer. + +MULTIPLE PROCESS SUPPORT +=========================================================================== +To run multiple Clojure processes, you start the first up +with \\[inf-clojure]. It will be in a buffer named *inf-clojure*. +Rename this buffer with \\[rename-buffer]. You may now start up a new +process with another \\[inf-clojure]. It will be in a new buffer, +named *inf-clojure*. You can switch between the different process +buffers with \\[switch-to-buffer]. + +Commands that send text from source buffers to Clojure processes -- +like `inf-clojure-eval-defun' or `inf-clojure-show-arglists' -- have to choose a +process to send to, when you have more than one Clojure process around. This +is determined by the global variable `inf-clojure-buffer'. Suppose you +have three inferior Clojures running: + Buffer Process + foo `inf-clojure' + bar inf-clojure<2> + *inf-clojure* inf-clojure<3> +If you do a \\[inf-clojure-eval-defun] command on some Clojure source code, +what process do you send it to? + +- If you're in a process buffer (foo, bar, or *inf-clojure*), + you send it to that process. +- If you're in some other buffer (e.g., a source file), you + send it to the process attached to buffer `inf-clojure-buffer'. +This process selection is performed by function `inf-clojure-proc'. + +Whenever \\[inf-clojure] fires up a new process, it resets +`inf-clojure-buffer' to be the new process's buffer. If you only run +one process, this does the right thing. If you run multiple +processes, you might need to change `inf-clojure-buffer' to +whichever process buffer you want to use.") (defun inf-clojure--get-feature (repl-type feature no-error) "Get FEATURE for REPL-TYPE from repl-features. @@ -171,8 +223,8 @@ If no-error is truthy don't error if feature is not present." (defun inf-clojure--update-feature (repl-type feature form) "Return a copy of the datastructure containing the repl features. -Given a REPL-TYPE ('clojure, 'lumo, ...) and a FEATURE ('doc, -'apropos, ...) and a FORM this will return a new datastructure +Given a REPL-TYPE (`clojure', `planck', ...) and a FEATURE (`doc', +`apropos', ...) and a FORM this will return a new datastructure that can be set as `inf-clojure-repl-features'." (let ((original (alist-get repl-type inf-clojure-repl-features))) (if original @@ -184,8 +236,8 @@ that can be set as `inf-clojure-repl-features'." (defun inf-clojure-update-feature (repl-type feature form) "Mutate the repl features to the new FORM. -Given a REPL-TYPE ('clojure, 'lumo, ...) and a FEATURE ('doc, -'apropos, ...) and a FORM this will set +Given a REPL-TYPE (`clojure', `planck', ...) and a FEATURE (`doc', +`apropos', ...) and a FORM this will set `inf-clojure-repl-features' with these new values." (setq inf-clojure-repl-features (inf-clojure--update-feature repl-type feature form))) @@ -200,7 +252,7 @@ has been found. See also variable `inf-clojure-buffer'." (error "No Clojure subprocess; see variable `inf-clojure-buffer'")))) (defun inf-clojure-repl-p (&optional buf) - "Indicates if BUF is an inf-clojure REPL. + "Indicates if BUF is an `inf-clojure' REPL. If BUF is nil then defaults to the current buffer. Checks the mode and that there is a live process." (let ((buf (or buf (current-buffer)))) @@ -209,33 +261,37 @@ Checks the mode and that there is a live process." (process-live-p (get-buffer-process buf))))) (defun inf-clojure-repls () - "Return a list of all inf-clojure REPL buffers." + "Return a list of all `inf-clojure' REPL buffers." (let (repl-buffers) (dolist (b (buffer-list)) (when (inf-clojure-repl-p b) (push (buffer-name b) repl-buffers))) repl-buffers)) +(defun inf-clojure--prompt-repl-buffer (prompt) + "Prompt the user to select an `inf-clojure' repl buffer. +PROMPT is a string to prompt the user. +Returns nil when no buffer is selected." + (let ((repl-buffers (inf-clojure-repls))) + (if (> (length repl-buffers) 0) + (when-let* ((repl-buffer (completing-read prompt repl-buffers nil t))) + (get-buffer repl-buffer)) + (user-error "No buffers have an inf-clojure process")))) + (defun inf-clojure-set-repl (always-ask) - "Set an inf-clojure buffer as the active (default) REPL. + "Set an `inf-clojure' buffer as the active (default) REPL. If in a REPL buffer already, use that unless a prefix is used (or -ALWAYS-ASK). Otherwise get a list of all active inf-clojure +ALWAYS-ASK). Otherwise get a list of all active `inf-clojure' REPLS and offer a choice. It's recommended to rename REPL buffers after they are created with `rename-buffer'." (interactive "P") - (if (and (not always-ask) - (inf-clojure-repl-p)) - (setq inf-clojure-buffer (current-buffer)) - (let ((repl-buffers (inf-clojure-repls))) - (if (> (length repl-buffers) 0) - (when-let ((repl-buffer (completing-read "Select default REPL: " repl-buffers nil t))) - (setq inf-clojure-buffer (get-buffer repl-buffer))) - (user-error "No buffers have an inf-clojure process"))))) - -(defvar-local inf-clojure-repl-type nil - "Symbol to define your REPL type. -Its root binding is nil and it can be further customized using -either `setq-local` or an entry in `.dir-locals.el`." ) + (when-let* ((new-repl-buffer + (if (or always-ask + (not (inf-clojure-repl-p))) + (inf-clojure--prompt-repl-buffer "Select default REPL: ") + (current-buffer)))) + (setq inf-clojure-buffer new-repl-buffer) + (message "Current inf-clojure REPL set to %s" new-repl-buffer))) (defvar inf-clojure--repl-type-lock nil "Global lock for protecting against proc filter race conditions. @@ -246,7 +302,7 @@ See http://blog.jorgenschaefer.de/2014/05/race-conditions-in-emacs-process-filte (interactive) (let ((types (mapcar #'car inf-clojure-repl-features))) (intern - (completing-read "Set REPL type:" + (completing-read "Set REPL type: " (sort (mapcar #'symbol-name types) #'string-lessp))))) (defgroup inf-clojure nil @@ -257,8 +313,7 @@ See http://blog.jorgenschaefer.de/2014/05/race-conditions-in-emacs-process-filte :link '(emacs-commentary-link :tag "Commentary" "inf-clojure")) (defconst inf-clojure-version - (eval-when-compile - (lm-version (or load-file-name buffer-file-name))) + (package-get-version) "The current version of `inf-clojure'.") (defcustom inf-clojure-prompt-read-only t @@ -275,6 +330,14 @@ mode. Default is whitespace followed by 0 or 1 single-letter colon-keyword \(as in :a, :c, etc.)" :type 'regexp) +(defcustom inf-clojure-source-modes '(clojure-ts-mode clojure-mode) + "Used to determine if a buffer contains Clojure source code. + +Any buffer with one of these major modes, it's considered a Clojure +source file by all `inf-clojure' commands." + :type '(repeat symbol) + :safe #'symbolp) + (defun inf-clojure--modeline-info () "Return modeline info for `inf-clojure-minor-mode'. Either \"no process\" or \"buffer-name(repl-type)\"" @@ -285,7 +348,8 @@ Either \"no process\" or \"buffer-name(repl-type)\"" "no process")) (defvar inf-clojure-mode-map - (let ((map (copy-keymap comint-mode-map))) + (let ((map (make-sparse-keymap))) + (set-keymap-parent map comint-mode-map) (define-key map (kbd "C-x C-e") #'inf-clojure-eval-last-sexp) (define-key map (kbd "C-c C-l") #'inf-clojure-load-file) (define-key map (kbd "C-c C-a") #'inf-clojure-show-arglists) @@ -294,6 +358,7 @@ Either \"no process\" or \"buffer-name(repl-type)\"" (define-key map (kbd "C-c C-S-a") #'inf-clojure-apropos) (define-key map (kbd "C-c M-o") #'inf-clojure-clear-repl-buffer) (define-key map (kbd "C-c C-q") #'inf-clojure-quit) + (define-key map (kbd "C-c C-z") #'inf-clojure-switch-to-recent-buffer) (easy-menu-define inf-clojure-mode-menu map "Inferior Clojure REPL Menu" '("Inf-Clojure REPL" @@ -314,7 +379,7 @@ Either \"no process\" or \"buffer-name(repl-type)\"" map)) (defvar inf-clojure-insert-commands-map - (let ((map (define-prefix-command 'inf-clojure-insert-commands-map))) + (let ((map (make-sparse-keymap))) (define-key map (kbd "d") #'inf-clojure-insert-defun) (define-key map (kbd "C-d") #'inf-clojure-insert-defun) (define-key map (kbd "e") #'inf-clojure-insert-last-sexp) @@ -331,7 +396,7 @@ Either \"no process\" or \"buffer-name(repl-type)\"" (define-key map (kbd "C-c C-r") #'inf-clojure-eval-region) (define-key map (kbd "C-c M-r") #'inf-clojure-reload) (define-key map (kbd "C-c C-n") #'inf-clojure-eval-form-and-next) - (define-key map (kbd "C-c C-j") 'inf-clojure-insert-commands-map) + (define-key map (kbd "C-c C-j") inf-clojure-insert-commands-map) (define-key map (kbd "C-c C-z") #'inf-clojure-switch-to-repl) (define-key map (kbd "C-c C-i") #'inf-clojure-show-ns-vars) (define-key map (kbd "C-c C-S-a") #'inf-clojure-apropos) @@ -377,13 +442,24 @@ The value of this variable is a mode line template as in `mode-line-format'. See Info Node `(elisp)Mode Line Format' for details about mode line templates. -Customize this variable to change how inf-clojure-minor-mode +Customize this variable to change how `inf-clojure-minor-mode' displays its status in the mode line. The default value displays the current REPL. Set this variable to nil to disable the mode line entirely." :type 'sexp :risky t) +(defcustom inf-clojure-enable-eldoc t + "Var that allows disabling `eldoc-mode' in `inf-clojure'. + +Set to nil to disable eldoc. Eldoc can be quite useful by +displaying function signatures in the modeline, but can also +cause multiple prompts to appear in the REPL and mess with *1, +*2, etc." + :type 'boolean + :safe #'booleanp + :package-version '(inf-clojure . "3.2.0")) + ;;;###autoload (define-minor-mode inf-clojure-minor-mode "Minor mode for interacting with the inferior Clojure process buffer. @@ -394,7 +470,8 @@ The following commands are available: :lighter inf-clojure-mode-line :keymap inf-clojure-minor-mode-map (setq-local comint-input-sender 'inf-clojure--send-string) - (inf-clojure-eldoc-setup) + (when inf-clojure-enable-eldoc + (inf-clojure-eldoc-setup)) (make-local-variable 'completion-at-point-functions) (add-to-list 'completion-at-point-functions #'inf-clojure-completion-at-point)) @@ -414,7 +491,8 @@ number (e.g. (\"localhost\" . 5555))." "Form to be used to start `inf-clojure'. Can be a cons pair of (host . port) where host is a string and port is an integer, or a string to startup an interpreter like -\"planck\".") +\"planck\"." + :type '(choice (cons string integer) (const nil))) (defcustom inf-clojure-custom-repl-type nil @@ -423,12 +501,14 @@ Should be a symbol that is a key in `inf-clojure-repl-features'." :package-version '(inf-clojure . "3.0.0") :type '(choice (const :tag "clojure" clojure) (const :tag "cljs" cljs) - (const :tag "lumo" lumo) (const :tag "planck" planck) (const :tag "joker" joker) (const :tag "babashka" babashka) (const :tag "determine at startup" nil))) +(defvar inf-clojure-custom-repl-name nil + "A string to be used as the repl buffer name.") + (defun inf-clojure--whole-comment-line-p (string) "Return non-nil iff STRING is a whole line semicolon comment." (string-match-p "^\s*;" string)) @@ -471,7 +551,7 @@ Clojure to load that file." ;; :reload-all implies :reload and also forces loading of all libs that the ;; identified libs directly or indirectly load via require or use -(defun inf-clojure-reload-form (proc) +(defun inf-clojure-reload-form (_proc) "Return the form to query the Inf-Clojure PROC for reloading a namespace. If you are using REPL types, it will pickup the most appropriate `inf-clojure-reload-form` variant." @@ -489,7 +569,7 @@ Clojure to load that file." :safe #'stringp :package-version '(inf-clojure . "2.2.0")) -(defun inf-clojure-reload-all-form (proc) +(defun inf-clojure-reload-all-form (_proc) "Return the form to query the Inf-Clojure PROC for :reload-all of a namespace. If you are using REPL types, it will pickup the most appropriate `inf-clojure-reload-all-form` variant." @@ -517,22 +597,35 @@ This should usually be a combination of `inf-clojure-prompt' and :package-version '(inf-clojure . "2.0.0")) (defcustom inf-clojure-auto-mode t - "When non-nil, automatically enable inf-clojure-minor-mode for all Clojure buffers." + "Automatically enable `inf-clojure-minor-mode'. +All buffers in `clojure-mode' will automatically be in +`inf-clojure-minor-mode' unless set to nil." :type 'boolean :safe #'booleanp :package-version '(inf-clojure . "3.1.0")) +(defun inf-clojure--get-preferred-major-modes () + "Return list of preferred major modes that are actually available." + (cl-remove-if-not (lambda (mode) (featurep mode)) + inf-clojure-source-modes)) + +(defun inf-clojure--clojure-buffer-p () + "Return TRUE if the current buffer is a Clojure buffer." + (derived-mode-p (inf-clojure--get-preferred-major-modes))) + (defun inf-clojure--clojure-buffers () "Return a list of all existing `clojure-mode' buffers." - (cl-remove-if-not - (lambda (buffer) (with-current-buffer buffer (derived-mode-p 'clojure-mode))) - (buffer-list))) + (cl-remove-if-not (lambda (buffer) + (with-current-buffer buffer + (inf-clojure--clojure-buffer-p))) + (buffer-list))) (defun inf-clojure-enable-on-existing-clojure-buffers () "Enable inf-clojure's minor mode on existing Clojure buffers. See command `inf-clojure-minor-mode'." (interactive) - (add-hook 'clojure-mode-hook #'inf-clojure-minor-mode) + (dolist (mode (inf-clojure--get-preferred-major-modes)) + (add-hook (derived-mode-hook-name mode) #'inf-clojure-minor-mode)) (dolist (buffer (inf-clojure--clojure-buffers)) (with-current-buffer buffer (inf-clojure-minor-mode +1)))) @@ -544,42 +637,6 @@ See command `inf-clojure-minor-mode'." (with-current-buffer buffer (inf-clojure-minor-mode -1)))) -(defvar inf-clojure-buffer nil - "The current `inf-clojure' process buffer. - -MULTIPLE PROCESS SUPPORT -=========================================================================== -To run multiple Clojure processes, you start the first up -with \\[inf-clojure]. It will be in a buffer named `*inf-clojure*'. -Rename this buffer with \\[rename-buffer]. You may now start up a new -process with another \\[inf-clojure]. It will be in a new buffer, -named `*inf-clojure*'. You can switch between the different process -buffers with \\[switch-to-buffer]. - -Commands that send text from source buffers to Clojure processes -- -like `inf-clojure-eval-defun' or `inf-clojure-show-arglists' -- have to choose a -process to send to, when you have more than one Clojure process around. This -is determined by the global variable `inf-clojure-buffer'. Suppose you -have three inferior Clojures running: - Buffer Process - foo inf-clojure - bar inf-clojure<2> - *inf-clojure* inf-clojure<3> -If you do a \\[inf-clojure-eval-defun] command on some Clojure source code, -what process do you send it to? - -- If you're in a process buffer (foo, bar, or *inf-clojure*), - you send it to that process. -- If you're in some other buffer (e.g., a source file), you - send it to the process attached to buffer `inf-clojure-buffer'. -This process selection is performed by function `inf-clojure-proc'. - -Whenever \\[inf-clojure] fires up a new process, it resets -`inf-clojure-buffer' to be the new process's buffer. If you only run -one process, this does the right thing. If you run multiple -processes, you might need to change `inf-clojure-buffer' to -whichever process buffer you want to use.") - (define-derived-mode inf-clojure-mode comint-mode "Inferior Clojure" "Major mode for interacting with an inferior Clojure process. Runs a Clojure interpreter as a subprocess of Emacs, with Clojure @@ -599,39 +656,43 @@ Customization: Entry to this mode runs the hooks on `comint-mode-hook' and You can send text to the inferior Clojure process from other buffers containing Clojure source. - `inf-clojure-switch-to-repl' switches the current buffer to the Clojure process buffer. + `inf-clojure-switch-to-repl' switches the current buffer to the Clojure + process buffer. `inf-clojure-eval-defun' sends the current defun to the Clojure process. `inf-clojure-eval-region' sends the current region to the Clojure process. Prefixing the inf-clojure-eval/defun/region commands with - a \\[universal-argument] causes a switch to the Clojure process buffer after sending - the text. + a \\[universal-argument] causes a switch to the Clojure process buffer after + sending the text. Commands:\\ -\\[comint-send-input] after the end of the process' output sends the text from the - end of process to point. -\\[comint-send-input] before the end of the process' output copies the sexp ending at point - to the end of the process' output, and sends it. -\\[comint-copy-old-input] copies the sexp ending at point to the end of the process' output, - allowing you to edit it before sending it. -If `comint-use-prompt-regexp' is nil (the default), \\[comint-insert-input] on old input - copies the entire old input to the end of the process' output, allowing - you to edit it before sending it. When not used on old input, or if - `comint-use-prompt-regexp' is non-nil, \\[comint-insert-input] behaves according to - its global binding. +\\[comint-send-input] after the end of the process' output sends the text from + the end of process to point. +\\[comint-send-input] before the end of the process' output copies the sexp + ending at point to the end of the process' output, and sends it. +\\[comint-copy-old-input] copies the sexp ending at point to the end of the + process' output,allowing you to edit it before sending it. +If `comint-use-prompt-regexp' is nil (the default), \\[comint-insert-input] on + old input copies the entire old input to the end of the process' output, + allowing you to edit it before sending it. When not used on old input, or if + `comint-use-prompt-regexp' is non-nil, \\[comint-insert-input] behaves + according to its global binding. \\[backward-delete-char-untabify] converts tabs to spaces as it moves back. \\[clojure-indent-line] indents for Clojure; with argument, shifts rest of expression rigidly with the current line. -\\[indent-sexp] does \\[clojure-indent-line] on each line starting within following expression. -Paragraphs are separated only by blank lines. Semicolons start comments. -If you accidentally suspend your process, use \\[comint-continue-subjob] -to continue it." +\\[indent-sexp] does \\[clojure-indent-line] on each line starting within + following expression. Paragraphs are separated only by blank lines. + Semicolons start comments. If you accidentally suspend your process, + use \\[comint-continue-subjob] to continue it." (setq comint-input-sender 'inf-clojure--send-string) (setq comint-prompt-regexp inf-clojure-comint-prompt-regexp) (setq mode-line-process '(":%s")) + ;; NOTE: Using Tree-sitter based syntax highlighting in comint + ;; buffer is currently not possible. (clojure-mode-variables) (clojure-font-lock-setup) - (inf-clojure-eldoc-setup) + (when inf-clojure-enable-eldoc + (inf-clojure-eldoc-setup)) (setq comint-get-old-input #'inf-clojure-get-old-input) (setq comint-input-filter #'inf-clojure-input-filter) (setq-local comint-prompt-read-only inf-clojure-prompt-read-only) @@ -680,22 +741,36 @@ to continue it." (let ((comint-buffer-maximum-size 0)) (comint-truncate-buffer)))) +(defun inf-clojure--swap-to-buffer-window (to-buffer) + "Switch to `TO-BUFFER''s window." + (let ((pop-up-frames + ;; Be willing to use another frame + ;; that already has the window in it. + (or pop-up-frames + (get-buffer-window to-buffer t)))) + (pop-to-buffer to-buffer '(display-buffer-reuse-window . ())))) + (defun inf-clojure-switch-to-repl (eob-p) "Switch to the inferior Clojure process buffer. With prefix argument EOB-P, positions cursor at end of buffer." (interactive "P") (if (get-buffer-process inf-clojure-buffer) - (let ((pop-up-frames - ;; Be willing to use another frame - ;; that already has the window in it. - (or pop-up-frames - (get-buffer-window inf-clojure-buffer t)))) - (pop-to-buffer inf-clojure-buffer)) + (inf-clojure--swap-to-buffer-window inf-clojure-buffer) (call-interactively #'inf-clojure)) (when eob-p (push-mark) (goto-char (point-max)))) +(defun inf-clojure-switch-to-recent-buffer () + "Switch to the most recently used `inf-clojure-minor-mode' buffer." + (interactive) + (let ((recent-inf-clojure-minor-mode-buffer (seq-find (lambda (buf) + (with-current-buffer buf (bound-and-true-p inf-clojure-minor-mode))) + (buffer-list)))) + (if recent-inf-clojure-minor-mode-buffer + (inf-clojure--swap-to-buffer-window recent-inf-clojure-minor-mode-buffer) + (message "inf-clojure: No recent buffer known.")))) + (defun inf-clojure-quit (&optional buffer) "Kill the REPL buffer and its underlying process. @@ -725,21 +800,39 @@ to suppress the usage of the target buffer discovery logic." The name is simply the final segment of the path." (file-name-nondirectory (directory-file-name dir))) +(defun inf-clojure--project-dir () + "Return current Clojure project root." + (let ((project-vc-extra-root-markers '("project.clj" ; Leiningen + "build.gradle" ; Gradle + "build.gradle.kts" ; Gradle + "deps.edn" ; Clojure CLI (a.k.a. tools.deps) + "shadow-cljs.edn" ; shadow-cljs + "bb.edn" ; babashka + "nbb.edn" ; nbb + "basilisp.edn" ; Basilisp (Python) + ))) + (project-root (project-current)))) + +(defvar clojure-ts-mode-syntax-table) + ;;;###autoload -(defun inf-clojure (cmd) - "Run an inferior Clojure process, input and output via buffer `*inf-clojure*'. -If there is a process already running in `*inf-clojure*', just +(defun inf-clojure (cmd &optional suppress-message) + "Run an inferior Clojure process, input and output via buffer *inf-clojure*. +If there is a process already running in *inf-clojure*, just switch to that buffer. CMD is a string which serves as the startup command or a cons of host and port. - Prompts user for repl startup command and repl type if not + Prompts user for REPL startup command and REPL type if not inferrable from startup command. Uses `inf-clojure-custom-repl-type' and `inf-clojure-custom-startup' if those are set. Use a prefix to prevent using these when they are set. +Prints a message that it has connected to the host and port +unless SUPPRESS-MESSAGE is truthy. + Runs the hooks from `inf-clojure-mode-hook' (after the `comint-mode-hook' is run). \(Type \\[describe-mode] in the process buffer for a list of commands.)" @@ -749,10 +842,12 @@ process buffer for a list of commands.)" (mapcar #'cdr inf-clojure-startup-forms) nil 'confirm-after-completion)))) - (let* ((project-dir (clojure-project-dir)) - (process-buffer-name (if project-dir - (format "inf-clojure %s" (inf-clojure--project-name project-dir)) - "inf-clojure")) + (let* ((project-dir (inf-clojure--project-dir)) + (process-buffer-name (or + inf-clojure-custom-repl-name + (if project-dir + (format "inf-clojure %s" (inf-clojure--project-name project-dir)) + "inf-clojure"))) ;; comint adds the asterisks to both sides (repl-buffer-name (format "*%s*" process-buffer-name))) ;; Create a new comint buffer if needed @@ -761,29 +856,136 @@ process buffer for a list of commands.)" (let ((default-directory (or project-dir default-directory)) (cmdlist (if (consp cmd) (list cmd) - (split-string cmd))) + (split-string-and-unquote cmd))) (repl-type (or (unless prefix-arg inf-clojure-custom-repl-type) (car (rassoc cmd inf-clojure-startup-forms)) (inf-clojure--prompt-repl-type)))) - (message "Starting Clojure REPL via `%s'..." cmd) + (unless suppress-message + (message "Starting Clojure REPL via `%s'..." cmd)) (with-current-buffer (apply #'make-comint process-buffer-name (car cmdlist) nil (cdr cmdlist)) (inf-clojure-mode) + (set-syntax-table (pcase (car (inf-clojure--get-preferred-major-modes)) + ('clojure-ts-mode clojure-ts-mode-syntax-table) + (_ clojure-mode-syntax-table))) (setq-local inf-clojure-repl-type repl-type) (hack-dir-local-variables-non-file-buffer)))) ;; update the default comint buffer and switch to it (setq inf-clojure-buffer (get-buffer repl-buffer-name)) (if inf-clojure-repl-use-same-window (pop-to-buffer-same-window repl-buffer-name) - (pop-to-buffer repl-buffer-name)))) + (pop-to-buffer repl-buffer-name)) + repl-buffer-name)) -;;;###autoload -(defun inf-clojure-connect (host port) +;;;###autol +(defun inf-clojure-connect (host port &optional suppress-message) "Connect to a running socket REPL server via `inf-clojure'. -HOST is the host the process is running on, PORT is where it's listening." +HOST is the host the process is running on, PORT is where it's +listening. SUPPRESS-MESSAGE is optional and if truthy will +prevent showing the startup message." (interactive "shost: \nnport: ") - (inf-clojure (cons host port))) + (inf-clojure (cons host port) suppress-message)) + +(defvar-local inf-clojure-socket-callback nil + "Used to transfer state between the socket process buffer & REPL buffer.") + +(defvar-local inf-clojure-socket-buffer nil + "Used to kill the associated socket buffer when it's REPL buffer is killed.") + +(defun inf-clojure-socket-filter (process output) + "A filter that gets triggered each time the socket receives new OUTPUT. +This function prints out the output received but also +watches for a prompt using the `inf-clojure-prompt' regexp, once +this happens a callback is triggered if available. The callback +is intended to be used to trigger a `inf-clojure-connect' once we +can determine that a socket REPL is ready to receive a +connection. + +PROCESS is the process object that is being filtered. + +OUTPUT is the latest data received from the process" + (let ((server-buffer (process-buffer process))) + (when (buffer-live-p server-buffer) + (with-current-buffer server-buffer + (insert output))) + (let ((prompt-displayed (string-match inf-clojure-prompt output))) + (when prompt-displayed + (with-current-buffer server-buffer + (when inf-clojure-socket-callback + (funcall inf-clojure-socket-callback))))))) + +(defun inf-clojure-socket-repl-sentinel (process _event) + "Ensures socket REPL are cleaned up when the REPL buffer is closed. + +PROCESS is the process object that is connected to a socket REPL. + +EVENT is the event that triggered this function to be called." + (when (not (process-live-p process)) + (let ((repl-buffer (process-buffer process))) + (with-current-buffer repl-buffer + (when inf-clojure-socket-buffer + (kill-buffer inf-clojure-socket-buffer)))))) + +(defvar inf-clojure-socket-repl-startup-forms + '((lein . "JVM_OPTS='-Dclojure.server.repl={:port %d :accept clojure.core.server/repl}' lein repl") + (boot . "export BOOT_JVM_OPTIONS='-Dclojure.server.repl=\"{:port %d :accept clojure.core.server/repl}\"' boot repl") + (clojure . "clojure -J-Dclojure.server.repl=\"{:port %d :accept clojure.core.server/repl}\"") + (cljs . "clojure -J-Dclojure.server.repl=\"{:port %d :accept cljs.server.browser/repl}\"") + (lein-clr . "JVM_OPTS='-Dclojure.server.repl={:port %d :accept clojure.core.server/repl}' lein clr repl") + (planck . "planck -n %d") + (babashka . "bb socket-repl %d"))) + +(defcustom inf-clojure-socket-repl-port + nil + "Port to be used when creating a socket REPL via `inf-clojure-socket-repl'. +If left as nil a random port will be selected between 5500-6000." + :type '(choice integer (const nil)) + :package-version '(inf-clojure . "3.3")) + +;;;###autoload +(defun inf-clojure-socket-repl (cmd) + "Start a socket REPL server and connects to it via `inf-clojure-connect'. +CMD is the command line instruction used to start the socket +REPL. It should be a string with \"%d\" in it to take a random +port. Set `inf-clojure-custom-startup' or choose from the +defaults provided in `inf-clojure-socket-repl-startup-forms'." + (interactive (list (or (unless current-prefix-arg + inf-clojure-custom-startup) + (completing-read "Select Clojure socket REPL startup command: " + (mapcar #'cdr inf-clojure-socket-repl-startup-forms) + nil + 'confirm-after-completion)))) + (let* ((host "localhost") + (port (or inf-clojure-socket-repl-port (+ 5500 (random 500)))) + (project-dir (inf-clojure--project-dir)) + (repl-type (or (unless prefix-arg + inf-clojure-custom-repl-type) + (car (rassoc cmd inf-clojure-socket-repl-startup-forms)) + (inf-clojure--prompt-repl-type))) + (project-name (inf-clojure--project-name (or project-dir "standalone"))) + (socket-process-name (format "*%s-%s-socket-server*" project-name repl-type)) + (socket-buffer (get-buffer-create + (format "*%s-%s-socket*" project-name repl-type))) + (socket-cmd (format cmd port)) + (sock (let ((default-directory (or project-dir default-directory))) + (start-file-process-shell-command + socket-process-name socket-buffer + socket-cmd)))) + (with-current-buffer socket-buffer + (setq-local + inf-clojure-socket-callback + (lambda () + (let* ((inf-clojure-custom-repl-type repl-type) + (created-repl-buffer (inf-clojure-connect host port :suppress-message))) + (with-current-buffer (get-buffer created-repl-buffer) + (setq-local inf-clojure-socket-buffer socket-buffer) + (set-process-sentinel + (get-buffer-process (current-buffer)) + #'inf-clojure-socket-repl-sentinel)))))) + (set-process-filter sock #'inf-clojure-socket-filter) + (message "Starting %s socket REPL server at %s:%d with %s" repl-type host port socket-cmd))) + (defun inf-clojure--forms-without-newlines (str) "Remove newlines between toplevel forms. @@ -794,17 +996,24 @@ of forms." (condition-case nil (with-temp-buffer (progn - (clojurec-mode) + ;; Activate preferred major mode. + (funcall (car (inf-clojure--get-preferred-major-modes))) (insert str) (whitespace-cleanup) (goto-char (point-min)) (while (not (eobp)) (while (looking-at "\n") (delete-char 1)) + ;; NOTE: There is no special API for that in + ;; `clojure-ts-mode', so probably for now lets keep this + ;; `clojure-mode' function. (unless (eobp) (clojure-forward-logical-sexp)) (unless (eobp) - (forward-char))) + (forward-char) + ;; Remove an empty line at the end of the buffer. + (when (eobp) + (delete-char -1)))) (buffer-substring-no-properties (point-min) (point-max)))) (scan-error str))) @@ -834,7 +1043,7 @@ current defun else return the string.." (let ((end (point)) (case-fold-search t) (func (if bounds #'cons #'buffer-substring-no-properties))) - (beginning-of-defun) + (beginning-of-defun-raw) (funcall func (point) end)))) (defun inf-clojure-eval-defun (&optional and-go) @@ -872,16 +1081,21 @@ Prefix argument AND-GO means switch to the Clojure buffer afterwards." "Insert FORM into process and evaluate. Indent FORM. FORM is expected to have been trimmed." (let ((clojure-process (inf-clojure-proc))) - (with-current-buffer (process-buffer clojure-process) - (comint-goto-process-mark) - (let ((beginning (point))) - (insert (format "%s" form)) - (let ((end (point))) - (goto-char beginning) - (indent-sexp end) - ;; font-lock the inserted code - (font-lock-ensure beginning end))) - (comint-send-input t t)))) + ;; ensure the repl buffer scrolls. See similar fix in CIDER: + ;; https://github.com/clojure-emacs/cider/pull/2590 + (with-selected-window (or (get-buffer-window inf-clojure-buffer) + (selected-window)) + (with-current-buffer (process-buffer clojure-process) + (comint-goto-process-mark) + (let ((beginning (point))) + (insert form) + (let ((end (point))) + (goto-char beginning) + (indent-sexp end) + ;; font-lock the inserted code + (font-lock-ensure beginning end) + (goto-char end))) + (comint-send-input t t))))) (defun inf-clojure-insert-defun () "Send current defun to process." @@ -895,10 +1109,10 @@ Indent FORM. FORM is expected to have been trimmed." (buffer-substring-no-properties (save-excursion (backward-sexp) (point)) (point)))) -;;; Now that inf-clojure-eval-/defun/region takes an optional prefix arg, -;;; these commands are redundant. But they are kept around for the user -;;; to bind if he wishes, for backwards functionality, and because it's -;;; easier to type C-c e than C-u C-c C-e. +;; Now that inf-clojure-eval-/defun/region takes an optional prefix arg, +;; these commands are redundant. But they are kept around for the user +;; to bind if he wishes, for backwards functionality, and because it's +;; easier to type C-c e than C-u C-c C-e. (defun inf-clojure-eval-region-and-go (start end) "Send the current region to the inferior Clojure, and switch to its buffer. @@ -911,18 +1125,11 @@ START and END are the beginning and end positions in the buffer to send." (interactive) (inf-clojure-eval-defun t)) -(defvar inf-clojure-prev-l/c-dir/file nil +(defvar inf-clojure-prev-loaded-dir-and-file nil "Record last directory and file used in loading or compiling. This holds a cons cell of the form `(DIRECTORY . FILE)' describing the last `inf-clojure-load-file' command.") -(defcustom inf-clojure-source-modes '(clojure-mode) - "Used to determine if a buffer contains Clojure source code. -If it's loaded into a buffer that is in one of these major modes, it's -considered a Clojure source file by `inf-clojure-load-file'. -Used by this command to determine defaults." - :type '(repeat symbol)) - (defun inf-clojure-load-file (&optional switch-to-repl file-name) "Load a Clojure file into the inferior Clojure process. @@ -932,34 +1139,45 @@ is present it will be used instead of the current file." (interactive "P") (let* ((proc (inf-clojure-proc)) (file-name (or file-name - (car (comint-get-source "Load Clojure file: " inf-clojure-prev-l/c-dir/file + (car (comint-get-source "Load Clojure file: " inf-clojure-prev-loaded-dir-and-file ;; nil because doesn't need an exact name - inf-clojure-source-modes nil)))) + (inf-clojure--get-preferred-major-modes) nil)))) (load-form (inf-clojure-get-feature proc 'load))) (comint-check-source file-name) ; Check to see if buffer needs saved. - (setq inf-clojure-prev-l/c-dir/file (cons (file-name-directory file-name) + (setq inf-clojure-prev-loaded-dir-and-file (cons (file-name-directory file-name) (file-name-nondirectory file-name))) (inf-clojure--send-string proc (format load-form file-name)) (when switch-to-repl (inf-clojure-switch-to-repl t)))) +(declare-function clojure-ts-find-ns "clojure-ts-mode") + +(defun inf-clojure--find-ns () + "Return the namespace of the current Clojure buffer. + +This function delegates its job to an appropritate function, considering +`inf-clojure-source-modes'." + (pcase (car (inf-clojure--get-preferred-major-modes)) + ('clojure-ts-mode (clojure-ts-find-ns)) + (_ (clojure-find-ns)))) + (defun inf-clojure-reload (arg) "Send a query to the inferior Clojure for reloading the namespace. -See variable `inf-clojure-reload-form' and +See variable `inf-clojure-reload-form' and variable `inf-clojure-reload-all-form'. The prefix argument ARG can change the behavior of the command: - - C-u M-x `inf-clojure-reload': prompts for a namespace name. - - M-- M-x `inf-clojure-reload': executes (require ... :reload-all). - - M-- C-u M-x `inf-clojure-reload': reloads all AND prompts." + - \\`C-u' \\[inf-clojure-reload]: prompts for a namespace name. + - \\`M--' \\[inf-clojure-reload]: executes (require ... :reload-all). + - \\`M--' \\`C-u' \\[inf-clojure-reload]: reloads all AND prompts." (interactive "P") (let* ((proc (inf-clojure-proc)) (reload-all-p (or (equal arg '-) (equal arg '(-4)))) (prompt-p (or (equal arg '(4)) (equal arg '(-4)))) (ns (if prompt-p - (car (inf-clojure-symprompt "Namespace" (clojure-find-ns))) - (clojure-find-ns))) + (car (inf-clojure-symprompt "Namespace" (inf-clojure--find-ns))) + (inf-clojure--find-ns))) (form (if (not reload-all-p) (inf-clojure-reload-form proc) (inf-clojure-reload-all-form proc)))) @@ -986,7 +1204,7 @@ display." (if (zerop (length ans)) default ans)))) -;;; Adapted from function-called-at-point in help.el. +;; Adapted from function-called-at-point in help.el. (defun inf-clojure-fn-called-at-pt () "Return the name of the function called in the current call. The value is nil if it can't find one." @@ -1060,7 +1278,7 @@ STRING if present." (prin1-to-string (substring-no-properties string)))) nil (expand-file-name inf-clojure--log-file-name - (clojure-project-dir)) + (inf-clojure--project-dir)) 'append 'no-annoying-write-file-in-minibuffer))) @@ -1168,11 +1386,11 @@ for evaluation, therefore FORM should not include it." (defun inf-clojure-arglists (fn) "Send a query to the inferior Clojure for the arglists for function FN. See variable `inf-clojure-arglists-form'." - (when-let ((proc (inf-clojure-proc 'no-error))) - (when-let ((arglists-form (inf-clojure-get-feature proc 'arglists))) - (thread-first (format arglists-form fn) - (inf-clojure--process-response proc "(" ")") - (inf-clojure--some))))) + (when-let* ((proc (inf-clojure-proc 'no-error)) + (arglists-form (inf-clojure-get-feature proc 'arglists))) + (thread-first (format arglists-form fn) + (inf-clojure--process-response proc "(" ")") + (inf-clojure--some)))) (defun inf-clojure-show-arglists (prompt-for-symbol) "Show the arglists for function FN in the mini-buffer. @@ -1194,8 +1412,8 @@ prefix argument PROMPT-FOR-NS, it prompts for a namespace name." (interactive "P") (let* ((proc (inf-clojure-proc)) (ns (if prompt-for-ns - (car (inf-clojure-symprompt "Ns vars" (clojure-find-ns))) - (clojure-find-ns))) + (car (inf-clojure-symprompt "Ns vars" (inf-clojure--find-ns))) + (inf-clojure--find-ns))) (ns-vars-form (inf-clojure-get-feature proc 'ns-vars))) (inf-clojure--send-string proc (format ns-vars-form ns)))) @@ -1207,8 +1425,8 @@ PROMPT-FOR-NS, it prompts for a namespace name." (interactive "P") (let* ((proc (inf-clojure-proc)) (ns (if prompt-for-ns - (car (inf-clojure-symprompt "Set ns to" (clojure-find-ns))) - (clojure-find-ns))) + (car (inf-clojure-symprompt "Set ns to" (inf-clojure--find-ns))) + (inf-clojure--find-ns))) (set-ns-form (inf-clojure-get-feature proc 'set-ns))) (when (or (not ns) (equal ns "")) (user-error "No namespace selected")) @@ -1252,19 +1470,6 @@ every other EXPR will be discarded and nil will be returned." (inf-clojure--read-or-nil) (inf-clojure--list-or-nil))) -(defun inf-clojure-completions (expr) - "Return completions for the Clojure expression starting with EXPR. - -Under the hood it calls the function -\\[inf-clojure-completions-fn] passing in the result of -evaluating \\[inf-clojure-completion-form] at the REPL." - (let* ((proc (inf-clojure-proc 'no-error)) - (completion-form (inf-clojure-get-feature proc 'completion t))) - (when (and proc completion-form (not (string-blank-p expr))) - (let ((completion-expr (format completion-form (substring-no-properties expr)))) - (funcall inf-clojure-completions-fn - (inf-clojure--process-response completion-expr proc "(" ")")))))) - (defcustom inf-clojure-completions-fn 'inf-clojure-list-completions "The function that parses completion results. @@ -1289,22 +1494,26 @@ you might want to use in your customization." :type 'function :package-version '(inf-clojure . "2.1.0")) -(defconst inf-clojure-clojure-expr-break-chars "^[] \"'`><,;|&{()[@\\^]" - "Regexp are hard. +(defun inf-clojure-completions (expr) + "Return completions for the Clojure expression starting with EXPR. -This regex has been built in order to match the first of the -listed chars. There are a couple of quirks to consider: +Under the hood it calls the function +\\[inf-clojure-completions-fn] passing in the result of +evaluating \\[inf-clojure-completion-form] at the REPL." + (let* ((proc (inf-clojure-proc 'no-error)) + (completion-form (inf-clojure-get-feature proc 'completion t))) + (when (and proc completion-form (not (string-blank-p expr))) + (let ((completion-expr (format completion-form (substring-no-properties expr)))) + (funcall inf-clojure-completions-fn + (inf-clojure--process-response completion-expr proc "(" ")")))))) -- the ] is always a special in elisp regex so you have to put it - directly AFTER [ if you want to match it as literal. -- The ^ needs to be escaped with \\^. +(defconst inf-clojure-clojure-expr-break-chars "^][ \"'`><,;|&{()@\\^" + "A list of characters that serve as expression boundaries. -Tests and `re-builder' are your friends.") +See `inf-clojure-completion-bounds-of-expr-at-point'.") (defun inf-clojure--kw-to-symbol (kw) - "Convert the keyword KW to a symbol. - -This guy was taken from CIDER, thanks folks." + "Convert the keyword KW to a symbol." (when kw (replace-regexp-in-string "\\`:+" "" kw))) @@ -1406,6 +1615,7 @@ Return the number of nested sexp the point was over or after." "Backend function for eldoc to show argument list in the echo area." ;; todo: this never gets unset once connected and is a lie (when (and (inf-clojure-connected-p) + inf-clojure-enable-eldoc ;; don't clobber an error message in the minibuffer (not (member last-command '(next-error previous-error)))) (let* ((info (inf-clojure-eldoc-info-in-current-sexp)) @@ -1449,7 +1659,7 @@ Useful for commands that can invoked outside of an ‘inf-clojure’ buffer "Send FORM and apply MATCH-P on the result of sending it to PROC. Note that this function will add a \n to the end of the string for evaluation, therefore FORM should not include it." -p (funcall match-p (inf-clojure--process-response form proc nil))) + (funcall match-p (inf-clojure--process-response form proc nil))) (provide 'inf-clojure) diff --git a/test/inf-clojure-tests.el b/test/inf-clojure-tests.el index 635ac2c..af6dea9 100644 --- a/test/inf-clojure-tests.el +++ b/test/inf-clojure-tests.el @@ -2,7 +2,7 @@ ;; ;; Copyright © 2014-2021 Bozhidar Batsov -;; Authors: Bozhidar Batsov +;; Authors: Bozhidar Batsov ;; Andrea Richiardi ;; This file is not part of GNU Emacs. @@ -88,9 +88,8 @@ (ict-with-assess-buffers ((a (insert "@"))) (with-current-buffer a - (expect - (ict-bounds-string (inf-clojure-completion-bounds-of-expr-at-point)) - :not :to-be nil)))) + (expect (inf-clojure-completion-bounds-of-expr-at-point) + :to-be nil)))) (it "computes bounds for [symbol" (ict-with-assess-buffers pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy