diff --git a/.circleci/config.yml b/.circleci/config.yml index 5e843a02f..6c041601d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -54,32 +54,35 @@ commands: command: eldev -dtT compile --warnings-as-errors jobs: - test-ubuntu-emacs-26: - docker: - - image: silex/emacs:26-ci - entrypoint: bash - steps: - - setup - - test + # NB: commented until https://github.com/magit/transient/issues/368 or + # whatever is causing it is fixed. + # test-ubuntu-emacs-27: + # docker: + # - image: silex/emacs:27-ci + # entrypoint: bash + # steps: + # - setup + # - test - test-ubuntu-emacs-27: + test-ubuntu-emacs-28: docker: - - image: silex/emacs:27-ci + - image: silex/emacs:28-ci entrypoint: bash steps: - setup - test - test-ubuntu-emacs-28: + test-ubuntu-emacs-29: docker: - - image: silex/emacs:28-ci + - image: silex/emacs:29-ci entrypoint: bash steps: - setup - test - test-ubuntu-emacs-29: + + test-ubuntu-emacs-30: docker: - - image: silex/emacs:29-ci + - image: silex/emacs:30-ci entrypoint: bash steps: - setup @@ -131,14 +134,10 @@ workflows: jobs: - test-shellcheck - test-lint - - test-ubuntu-emacs-26: - requires: - - test-lint - - test-shellcheck - - test-ubuntu-emacs-27: - requires: - - test-lint - - test-shellcheck + # - test-ubuntu-emacs-27: + # requires: + # - test-lint + # - test-shellcheck - test-ubuntu-emacs-28: requires: - test-lint @@ -147,14 +146,20 @@ workflows: requires: - test-lint - test-shellcheck - - test-ubuntu-emacs-master: + - test-ubuntu-emacs-30: requires: - test-lint - test-shellcheck + # TODO: reenable once this issue is fixed https://app.circleci.com/pipelines/github/clojure-emacs/cider/2931/workflows/b7284759-c6d1-44dd-bbfc-e71709e3f62f/jobs/17699 + # - test-ubuntu-emacs-master: + # requires: + # - test-lint + # - test-shellcheck - test-windows-emacs-latest: requires: - test-lint - test-shellcheck - test-macos-emacs-latest: requires: - - test-ubuntu-emacs-28 + - test-lint + - test-shellcheck diff --git a/.github/stale.yml b/.github/stale.yml deleted file mode 100644 index 8bbe234c0..000000000 --- a/.github/stale.yml +++ /dev/null @@ -1,62 +0,0 @@ -# Configuration for probot-stale - https://github.com/probot/stale - -# Number of days of inactivity before an Issue or Pull Request becomes stale -daysUntilStale: 90 - -# Number of days of inactivity before an Issue or Pull Request with the stale label is closed. -# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. -daysUntilClose: 30 - -# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) -onlyLabels: [] - -# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable -exemptLabels: - - "high priority" - - "good first issue" - - "pinned" - -# Set to true to ignore issues in a project (defaults to false) -exemptProjects: false - -# Set to true to ignore issues in a milestone (defaults to false) -exemptMilestones: true - -# Set to true to ignore issues with an assignee (defaults to false) -exemptAssignees: true - -# Label to use when marking as stale -staleLabel: stale - -# Comment to post when marking as stale. Set to `false` to disable -markComment: > - This issue has been automatically marked as stale because it has not had - recent activity. It will be closed if no further activity occurs. Thank you - for your contribution and understanding! - -# Comment to post when removing the stale label. -# unmarkComment: > -# Your comment here. - -# Comment to post when closing a stale Issue or Pull Request. -closeComment: > - This issues been automatically closed due to lack of activity. Feel free to re-open it - if you ever come back to it. - -# Limit the number of actions per hour, from 1-30. Default is 30 -limitPerRun: 30 - -# Limit to only `issues` or `pulls` -# only: issues - -# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': -# pulls: -# daysUntilStale: 30 -# markComment: > -# This pull request has been automatically marked as stale because it has not had -# recent activity. It will be closed if no further activity occurs. Thank you -# for your contributions. - -# issues: -# exemptLabels: -# - confirmed diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 000000000..a7774f413 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,37 @@ +name: Close inactive issues and pull requests +on: + schedule: + - cron: "30 1 * * *" + +jobs: + close-issues: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - uses: actions/stale@v9 + with: + days-before-issue-stale: 30 + days-before-issue-close: 30 + stale-issue-label: "stale" + exempt-all-milestones: true + exempt-issue-labels: "bug, high priority, good first issue, pinned" + stale-issue-message: >- + This issue has been automatically marked as stale because it has not + had any recent activity. It will be closed soon if no further + activity occurs. Thank you for your contribution and understanding! + close-issue-message: >- + This issue been automatically closed due to lack of activity. Feel free to re-open it + if you ever come back to it. + days-before-pr-stale: 30 + days-before-pr-close: 30 + exempt-pr-labels: "high priority, good first issue, pinned" + stale-pr-message: >- + This pull request has been automatically marked as stale because it has not + had any recent activity. It will be closed soon if no further + activity occurs. Thank you for your contribution and understanding! + close-pr-message: >- + This issue been automatically closed due to lack of activity. Feel free to re-open it + if you ever come back to it. + repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e48e98fa9..7b012716b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,34 +21,27 @@ jobs: strategy: matrix: - os: [macos-13, ubuntu-latest, windows-latest] - emacs_version: ['26.3', '27.2', '28.2', '29.3'] - java_version: ['11', '17'] + # Test all Emacs versions on Ubuntu. + os: [ubuntu-latest] + # TODO: reenable snapshot once this is fixed https://app.circleci.com/pipelines/github/clojure-emacs/cider/2931/workflows/b7284759-c6d1-44dd-bbfc-e71709e3f62f/jobs/17699 + # emacs_version: ['27.2', '28.2', '29.3', '30.1', 'snapshot'] + emacs_version: ['27.2', '28.2', '29.3', '30.1'] + java_version: ['21'] include: - # aarch64 (macos-13 is Intel) - - os: macos-latest - emacs_version: '29.3' - java_version: '11' - - os: macos-latest - emacs_version: '28.2' - java_version: '11' - - os: macos-latest - emacs_version: '29.3' - java_version: '17' - - os: macos-latest - emacs_version: '28.2' - java_version: '17' + # For other OSes, test only the latest stable Emacs version. + - os: macos-latest # aarch64 + emacs_version: '30.1' + java_version: '21' + - os: macos-13 # x64 + emacs_version: '30.1' + java_version: '21' + - os: windows-latest + emacs_version: '30.1' + java_version: '21' steps: - name: Set up Emacs - if: "!startsWith (matrix.os, 'windows')" - uses: purcell/setup-emacs@master - with: - version: ${{matrix.emacs_version}} - - - name: Set up Emacs on Windows - if: startsWith (matrix.os, 'windows') - uses: jcs090218/setup-emacs-windows@master + uses: jcs090218/setup-emacs@master with: version: ${{matrix.emacs_version}} @@ -112,7 +105,7 @@ jobs: # be GH connectivity runner issues. We attempt to address this # problem by rerunning the tests more than once. eldev -p -dtTC test --test-type integration || eldev -p -dtTC test --test-type integration - + - name: Run tests that need enrich-classpath if: "!startsWith(matrix.os, 'windows')" run: | @@ -121,6 +114,6 @@ jobs: eldev -p -dtTC test --test-type enrich || eldev -p -dtTC test --test-type enrich - name: Test clojure-ts-mode - if: startsWith (matrix.emacs_version, '29') + if: startsWith (matrix.emacs_version, '30') run: | eldev -p -dtTC test --test-type clojure-ts-mode || eldev -p -dtTC test --test-type clojure-ts-mode diff --git a/.markdownlint-cli2.yaml b/.markdownlint-cli2.yaml new file mode 100644 index 000000000..37a97df1f --- /dev/null +++ b/.markdownlint-cli2.yaml @@ -0,0 +1,14 @@ +config: + default: true + + line-length: + line_length: 100 + code_blocks: false + tables: false + + no-duplicate-heading: + allow_different_nesting: true + + no-inline-html: false + + first-line-heading: false diff --git a/CHANGELOG.md b/CHANGELOG.md index 809dc9d09..97aeda91e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,17 +2,125 @@ ## master (unreleased) +## 1.18.0 (2025-04-30) + +### New features + +- [#3802](https://github.com/clojure-emacs/cider/issues/3802): Inspector analytics. +- [#3802](https://github.com/clojure-emacs/cider/issues/3802): Inspector table view-mode. +- [#3813](https://github.com/clojure-emacs/cider/issues/3813): Inspector pretty-printing mode. +- [#3810](https://github.com/clojure-emacs/cider/pull/3810): Inspector: `C-c C-p` to pretty-print the currently inspected value. +- [orchard#320](https://github.com/clojure-emacs/orchard/pull/320): Info: recognize printed Java classes/methods and munged Clojure functions in stacktrace outputs. + +### Changes + +- [#3782](https://github.com/clojure-emacs/cider/issues/3782): Drop official support for Emacs 26. +- [#3812](https://github.com/clojure-emacs/cider/issues/3812): **(Breaking)** Remove support for Boot. +- [#3793](https://github.com/clojure-emacs/cider/issues/3793): **(Breaking)** Remove features that relied on printed exception parsing: + - `cider-stacktrace-analyze-string` and `cider-stacktrace-analyze-at-point` functions. + - Automatic stacktrace parsing in log viewer. +- Bump the injected `cider-nrepl` to [0.55.7](https://github.com/clojure-emacs/cider-nrepl/blob/master/CHANGELOG.md#0557-2025-04-29). + - [compliment#122](https://github.com/alexander-yakushev/compliment/pull/122): Completion: sort candidates by priority. + - Inspector: add dedicated view for Exceptions. + - Stop vendoring Haystack dependency. + - Stop vendoring Puget dependency. You can still use `puget` pretty-printer in CIDER, but you need to depend on Puget explicitly. If Puget is not found on the classpath, CIDER will revert to `clojure.pprint/pprint` for pretty-printing. +- [#3777](https://github.com/clojure-emacs/cider/issues/3777): Inspector no longer displays parsed Javadoc for Java classes and members. +- [#3790](https://github.com/clojure-emacs/cider/issues/3790): Stacktrace: show messages and data for all exception causes by default. +- [#3807](https://github.com/clojure-emacs/cider/issues/3807): Stacktrace: make exception data individually inspectable. +- [#3789](https://github.com/clojure-emacs/cider/issues/3789): Refactor and simplify exception handling. +- [#3789](https://github.com/clojure-emacs/cider/issues/3796): Completion: disable client-side sorting (defer to backend-provided candidate order). +- [#3797](https://github.com/clojure-emacs/cider/issues/3797): Completion: enable `cider-completion-style` by default (this enables richer completion suggestions where candidates don't have to strictly match the prefix). +- [#3803](https://github.com/clojure-emacs/cider/pull/3803): Enable dynamic indentation for `clojure-ts-mode`. +- [#3805](https://github.com/clojure-emacs/cider/pull/3805): Profiler: update to latest profiling middleware. +- [#3806](https://github.com/clojure-emacs/cider/pull/3806): Add client info to `clone` op request. + +### Bugs fixed + +- [#3784](https://github.com/clojure-emacs/cider/issues/3784): Inspector: make point less erratic when navigating between inspector screens. +- [#3786](https://github.com/clojure-emacs/cider/issues/3786): Sort dictionaries by key in nrepl-bencode +- [#3779](https://github.com/clojure-emacs/cider/pull/3779): `cider-find-keyword` doesn't work with `clojure-ts-mode`. +- [#3791](https://github.com/clojure-emacs/cider/issues/3791): Missing font-lock when `cider-font-lock-dynamically` is enabled for `clojure-ts-mode`. + +## 1.17.1 (2025-02-25) + +### Changes + +- Bump the injected `cider-nrepl` to [0.52.1](https://github.com/clojure-emacs/cider-nrepl/blob/master/CHANGELOG.md#0521-2025-02-24). + +### Bugs fixed + +- [#3775](https://github.com/clojure-emacs/cider/issues/3775): Code completion throws `MalformedURLException` on Windows. + +## 1.17.0 (2025-02-17) + +### New features + +- Automatic downloading of third-party Java sources for better Java documentation and jump-to-definition functionality. See [Obtaining source JARs](https://docs.cider.mx/cider/usage/working_with_documentation.html#obtaining-source-jars). +- CIDER [History](https://docs.cider.mx/cider/repl/history.html): Add a command to delete history item at point. + +### Changes + +- Bump the injected nREPL version to [1.3.1](https://github.com/nrepl/nrepl/blob/master/CHANGELOG.md#131-2025-01-01). +- Bump the injected `cider-nrepl` to [0.52.0](https://github.com/clojure-emacs/cider-nrepl/blob/master/CHANGELOG.md#0520-2025-01-10). +- [#3574](https://github.com/clojure-emacs/cider/issues/3574): New value `per-project` for `cider-repl-history-file` to save the history on a per-project basis. + +### Bugs fixed + +- [#3763](https://github.com/clojure-emacs/cider/issues/3763): Fix `cider-docview-render` completion popup error when symbol being completed does not have a docstring. +- [#3774](https://github.com/clojure-emacs/cider/issues/3774): Fix overlay hangup when evaluating huge values. + +## 1.16.1 (2024-12-03) + +### Changes + +- Bump the injected `cider-nrepl` to [0.50.3](https://github.com/clojure-emacs/cider-nrepl/blob/master/CHANGELOG.md#0503-2024-12-02). +- [#3753](https://github.com/clojure-emacs/cider/pull/3753): Add `cider-log-show-frameworks` command to show available log frameworks in a buffer. +- [#3746](https://github.com/clojure-emacs/cider/issues/3746): Bring back `cider` completion style for activating backend-driven completion. + +### Bugs fixed + +- [#3742](https://github.com/clojure-emacs/cider/issues/3742): Restore syntax highlighting of result in the minibuffer. +- [#3747](https://github.com/clojure-emacs/cider/issues/3747): Fix errors when docstring is `nil`. +- [#3757](https://github.com/clojure-emacs/cider/issues/3757): Fix inspector's `def-current-value` selecting wrong REPL when multiple are connected. +- [#3754](https://github.com/clojure-emacs/cider/issues/3754): Fix regex in `cider-ns-from-p`. + +## 1.16.0 (2024-09-24) + +### Changes + +- Bump the injected nREPL version to [1.3](https://github.com/nrepl/nrepl/releases/tag/v1.3.0). +- [#3733](https://github.com/clojure-emacs/cider/issues/3733): Remove support for sideloading. (this experimental feature was removed from nREPL 1.3) +- Bump the injected `cider-nrepl` to [0.50.2](https://github.com/clojure-emacs/cider-nrepl/blob/master/CHANGELOG.md#0502-2024-09-03). + * Introduce new backend for CIDER tracing functionality (replacing `tools.trace`). + * Remove special handling of Boot classpath. + +### Bugs fixed + +- [#3722](https://github.com/clojure-emacs/cider/pull/3722): Call `cider-docstring--format` after checking argument is not `nil`. +- [#3739](https://github.com/clojure-emacs/cider/pull/3739): Leiningen jack-in fails when `cider-enable-nrepl-jvmti-agent` is enabled. + +## 1.15.1 (2024-07-01) + +### Changes + +- [#3714](https://github.com/clojure-emacs/cider/pull/3714): Show progress when evaluating files using `cider-load-all-files`. +- [#3713](https://github.com/clojure-emacs/cider/pull/3713): Optimize `nrepl-dict-get` and deprecate its 3-argument arity. +- [#3719](https://github.com/clojure-emacs/cider/pull/3719): Remove duplicated keybinding. +- Bump the injected `cider-nrepl` to [0.49.1](https://github.com/clojure-emacs/cider-nrepl/blob/master/CHANGELOG.md#0491-2024-06-30). + - Reduces the minimal supported Clojure version to 1.10.0 (from 1.10.3). + ## 1.15.0 (2024-06-10) ### New features -- [#3692](https://github.com/clojure-emacs/cider/pull/3692): Add ability to switch view modes in the ispector (bound to `v`). +- [#3692](https://github.com/clojure-emacs/cider/pull/3692): Add ability to switch view modes in the inspector (bound to `v`). - [#3693](https://github.com/clojure-emacs/cider/pull/3693): Add `cider-enable-nrepl-jvmti-agent` defcustom to enable loading native nREPL JVMTI agent which restores thread stop ability on Java 21+. ### Changes - [#3691](https://github.com/clojure-emacs/cider/pull/3691): Deprecate `cider-sync-request:inspect-set-*` functions in favor of generic `inspect-refresh` op. - Bump the injected `cider-nrepl` to [0.49.0](https://github.com/clojure-emacs/cider-nrepl/blob/master/CHANGELOG.md#0490-2024-06-02). +- Bump the injected nREPL version to 1.2.0. ### Bugs fixed diff --git a/README.md b/README.md index b5156e501..41be31d5b 100644 --- a/README.md +++ b/README.md @@ -61,30 +61,25 @@ install CIDER with the following command: ### Launch an nREPL server and client from Emacs -Simply open in Emacs a file belonging to your `lein`, `tools.deps` or `boot` project (like -`foo.clj`) and type M-x `cider-jack-in`. This will start an nREPL +Simply open in Emacs a file belonging to your `lein` or `tools.deps` project (like +`foo.clj`) and type M-x `cider-jack-in`. This will start an [nREPL][] server with all the project dependencies loaded in and CIDER will automatically connect to it. Alternatively you can use C-u M-x `cider-jack-in` to specify the path to a Clojure project, without having to visit any file in it. -**Tip:** In Clojure(Script) buffers the command `cider-jack-in` is bound to -C-c C-x (C-)j. +> [!TIP] +> +> In Clojure(Script) buffers the command `cider-jack-in` is bound to C-c C-x (C-)j. ### Connect to a running nREPL server You can go to your project's directory in a terminal and type there (assuming you're using Leiningen that is): -``` -$ lein repl -``` - -Or with Boot: - -``` -$ boot repl -s wait +```shell +lein repl ``` Alternatively you can start nREPL either manually or by the facilities provided @@ -93,9 +88,11 @@ by your project's build tool (`tools.deps`, Gradle, Maven, etc). After you get your nREPL server running go back to Emacs. Typing there M-x `cider-connect` will allow you to connect to the running nREPL server. -**Tip:** In Clojure(Script) buffers the command `cider-connect` is bound to -C-c C-x (C-)c (C-)j and the command `cider-connect-cljs` is bound to -C-c C-x (C-)c (C-)s. +> [!TIP] +> +> In Clojure(Script) buffers the command `cider-connect` is bound to +> C-c C-x (C-)c (C-)j and the command `cider-connect-cljs` is bound to +> C-c C-x (C-)c (C-)s. ## Diving Deeper @@ -131,22 +128,23 @@ The direction of the project is being stewarded by the CIDER core team. This group of long-term contributors manage releases, evaluate pull-requests, and does a lot of the groundwork on major new features. -* [Bozhidar Batsov](https://github.com/bbatsov) (author & head maintainer) -* [Vitalie Spinu](https://github.com/vspinu) -* [Michael Griffiths](https://github.com/cichli) -* [Lars Andersen](https://github.com/expez) +- [Bozhidar Batsov](https://github.com/bbatsov) (author & head maintainer) +- [Oleksandr Yakushev](https://github.com/alexander-yakushev) ### CIDER Alumni In addition, we'd like to extend a special thanks the following retired CIDER core team members. Lovingly known as The Alumni: -* [Tim King](https://github.com/kingtim) (original author) -* [Phil Hagelberg](https://github.com/technomancy) -* [Hugo Duncan](https://github.com/hugoduncan) -* [Steve Purcell](https://github.com/purcell) -* [Artur Malabarba](https://github.com/malabarba) -* [Jeff Valk](https://github.com/jeffvalk) +- [Tim King](https://github.com/kingtim) (original author) +- [Phil Hagelberg](https://github.com/technomancy) +- [Hugo Duncan](https://github.com/hugoduncan) +- [Steve Purcell](https://github.com/purcell) +- [Artur Malabarba](https://github.com/malabarba) +- [Jeff Valk](https://github.com/jeffvalk) +- [Vitalie Spinu](https://github.com/vspinu) +- [Michael Griffiths](https://github.com/cichli) +- [Lars Andersen](https://github.com/expez) ## Release policy @@ -184,11 +182,12 @@ for your preferences (although currently [Open Collective](https://opencollectiv If you're working in a company that's making significant use of CIDER we'd appreciate it if you suggest to your company to become a CIDER sponsor. -You can support the development of CIDER, [clojure-mode][] and [inf-clojure][] via -[Open Collective](https://opencollective.com/cider), -[GitHub Sponsors](https://github.com/sponsors/bbatsov), -[Patreon](https://www.patreon.com/bbatsov) and -[PayPal](https://www.paypal.me/bbatsov). +You can support the development of CIDER, [clojure-mode][] and [inf-clojure][] via: + +- [Open Collective](https://opencollective.com/cider) +- [GitHub Sponsors](https://github.com/sponsors/bbatsov) +- [Patreon](https://www.patreon.com/bbatsov) +- [PayPal](https://www.paypal.me/bbatsov) ### Open Collective Backers @@ -263,13 +262,11 @@ site. [[Become a sponsor](https://opencollective.com/cider#sponsor)] CIDER is distributed under the GNU General Public License, version 3. -Copyright © 2012-2024 Bozhidar Batsov, Artur Malabarba, Tim King, Phil Hagelberg and +Copyright © 2012-2025 Bozhidar Batsov, Artur Malabarba, Tim King, Phil Hagelberg and [contributors](https://github.com/clojure-emacs/cider/contributors). [badge-license]: https://img.shields.io/badge/license-GPL_3-green.svg [nREPL]:https://github.com/nrepl/nrepl -[Sly]: https://github.com/joaotavora/sly -[Geiser]: https://github.com/jaor/geiser [clojure-mode]: https://github.com/clojure-emacs/clojure-mode [clojure-ts-mode]: https://github.com/clojure-emacs/clojure-ts-mode [inf-clojure]: https://github.com/clojure-emacs/inf-clojure diff --git a/ROADMAP.md b/ROADMAP.md index 3f6bab656..bcdff55dc 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -49,7 +49,6 @@ There's a bit of info on the subject [here](https://github.com/clojure-emacs/cid ## Implement new nREPL features -* sideloading (there's some experimental support for this) * dynamic middleware loading * ~~completion~~ * ~~lookup~~ diff --git a/Vagrantfile b/Vagrantfile deleted file mode 100644 index 483b37dec..000000000 --- a/Vagrantfile +++ /dev/null @@ -1,8 +0,0 @@ -# -*- mode: ruby -*- -# vi: set ft=ruby : - -Vagrant::Config.run do |config| - config.vm.box = "ubuntu/trusty64" - - config.vm.provision :shell, :path => "vagrant/provision.sh" -end diff --git a/cider-apropos.el b/cider-apropos.el index b94e27ed8..1cf50647c 100644 --- a/cider-apropos.el +++ b/cider-apropos.el @@ -1,6 +1,6 @@ ;;; cider-apropos.el --- Apropos functionality for Clojure -*- lexical-binding: t -*- -;; Copyright © 2014-2024 Jeff Valk, Bozhidar Batsov and CIDER contributors +;; Copyright © 2014-2025 Jeff Valk, Bozhidar Batsov and CIDER contributors ;; ;; Author: Jeff Valk diff --git a/cider-browse-ns.el b/cider-browse-ns.el index 4e9e6be79..ce35b5a62 100644 --- a/cider-browse-ns.el +++ b/cider-browse-ns.el @@ -1,6 +1,6 @@ ;;; cider-browse-ns.el --- CIDER namespace browser -*- lexical-binding: t; -*- -;; Copyright © 2014-2024 John Andrews, Bozhidar Batsov and CIDER contributors +;; Copyright © 2014-2025 John Andrews, Bozhidar Batsov and CIDER contributors ;; Author: John Andrews diff --git a/cider-browse-spec.el b/cider-browse-spec.el index 7163a25f1..eb9d28166 100644 --- a/cider-browse-spec.el +++ b/cider-browse-spec.el @@ -1,6 +1,6 @@ ;;; cider-browse-spec.el --- CIDER spec browser -*- lexical-binding: t; -*- -;; Copyright © 2017-2024 Juan Monetta, Bozhidar Batsov and CIDER contributors +;; Copyright © 2017-2025 Juan Monetta, Bozhidar Batsov and CIDER contributors ;; Author: Juan Monetta diff --git a/cider-cheatsheet.el b/cider-cheatsheet.el index fbc07dad6..6babaebcb 100644 --- a/cider-cheatsheet.el +++ b/cider-cheatsheet.el @@ -1,6 +1,6 @@ ;;; cider-cheatsheet.el --- Quick reference for Clojure -*- lexical-binding: t -*- -;; Copyright © 2019-2024 Kris Jenkins, Bozhidar Batsov and CIDER contributors +;; Copyright © 2019-2025 Kris Jenkins, Bozhidar Batsov and CIDER contributors ;; ;; Author: Kris Jenkins ;; Kato Muso diff --git a/cider-classpath.el b/cider-classpath.el index 3ebe86502..55ed6ac6b 100644 --- a/cider-classpath.el +++ b/cider-classpath.el @@ -1,6 +1,6 @@ ;;; cider-classpath.el --- Basic Java classpath browser -*- lexical-binding: t; -*- -;; Copyright © 2014-2024 Bozhidar Batsov and CIDER contributors +;; Copyright © 2014-2025 Bozhidar Batsov and CIDER contributors ;; This program is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by diff --git a/cider-client.el b/cider-client.el index 0ae4025bd..32cc63b3e 100644 --- a/cider-client.el +++ b/cider-client.el @@ -1,6 +1,6 @@ ;;; cider-client.el --- A layer of abstraction above low-level nREPL client code. -*- lexical-binding: t -*- -;; Copyright © 2013-2024 Bozhidar Batsov +;; Copyright © 2013-2025 Bozhidar Batsov ;; ;; Author: Bozhidar Batsov @@ -108,7 +108,7 @@ EVAL-BUFFER is the buffer where the spinner was started." ;;; Evaluation helpers (defun cider-ns-form-p (form) "Check if FORM is an ns form." - (string-match-p "^[[:space:]]*\(ns\\([[:space:]]*$\\|[[:space:]]+\\)" form)) + (string-match-p "\\`[[:space:]]*\(ns\\([[:space:]]*$\\|[[:space:]]+\\)" form)) (defun cider-ns-from-form (ns-form) "Get ns substring from NS-FORM." @@ -189,16 +189,20 @@ the current connection. Return the id of the sent message. If TOOLING is truthy then the tooling session is used." (nrepl-send-request request callback (or connection (cider-current-repl 'any 'ensure)) tooling)) -(defun cider-nrepl-send-sync-request (request &optional connection abort-on-input) +(defun cider-nrepl-send-sync-request (request &optional connection + abort-on-input callback) "Send REQUEST to the nREPL server synchronously using CONNECTION. Hold till final \"done\" message has arrived and join all response messages of the same \"op\" that came along and return the accumulated response. If ABORT-ON-INPUT is non-nil, the function will return nil at the first sign of user input, so as not to hang the -interface." +interface. +if CALLBACK is non-nil, it will additionally be called on all received messages." (nrepl-send-sync-request request (or connection (cider-current-repl 'any 'ensure)) - abort-on-input)) + abort-on-input + nil + callback)) (defun cider-nrepl-send-unhandled-request (request &optional connection) "Send REQUEST to the nREPL CONNECTION and ignore any responses. @@ -245,15 +249,15 @@ Assuming this is the Clojure map you want to use as `cljfmt' options: you need to encode it as the following plist: - '((\"indents\" ((\"org.me/foo\" ((\"inner\" 0))))) (\"alias-map\" ((\"me\" \"org.me\"))))" - :type 'list + \\='((\"indents\" ((\"org.me/foo\" ((\"inner\" 0))))) (\"alias-map\" ((\"me\" \"org.me\"))))" + :type '(repeat sexp) :group 'cider :package-version '(cider . "1.1.0")) -(defun cider--nrepl-format-code-request-map (&optional format-options) +(defun cider--nrepl-format-code-request-options (&optional format-options) "Map to merge into requests that require code formatting. If non-nil, FORMAT-OPTIONS specifies the options cljfmt will use to format -the code. See `cider-format-code-options` for details." +the code. See `cider-format-code-options' for details." (when format-options (let* ((indents-dict (when (assoc "indents" format-options) (thread-last @@ -267,15 +271,11 @@ the code. See `cider-format-code-options` for details." (map-pairs) (seq-mapcat #'identity) (apply #'nrepl-dict))))) - (thread-last - (map-merge 'list - (when indents-dict - `(("indents" ,indents-dict))) - (when alias-map-dict - `(("alias-map" ,alias-map-dict)))) - (map-pairs) - (seq-mapcat #'identity) - (apply #'nrepl-dict))))) + (nrepl-dict + `(,@(when indents-dict + `("indents" ,indents-dict)) + ,@(when alias-map-dict + `("alias-map" ,alias-map-dict))))))) (defcustom cider-print-fn 'pprint "Sets the function to use for printing. @@ -316,14 +316,11 @@ nil." "A map of options that will be passed to `cider-print-fn'. Here's an example for `pprint': - '((\"length\" 50) (\"right-margin\" 70))" - :type 'list + \\='((\"length\" 50) (\"right-margin\" 70))" + :type '(repeat sexp) :group 'cider :package-version '(cider . "0.21.0")) -(make-obsolete-variable 'cider-pprint-fn 'cider-print-fn "0.21") -(make-obsolete-variable 'cider-pprint-options 'cider-print-options "0.21") - (defcustom cider-print-quota (* 1024 1024) "A hard limit on the number of bytes to return from any printing operation. Set to nil for no limit." @@ -342,6 +339,17 @@ The default value in nREPL is 1024." :group 'cider :package-version '(cider . "0.25.0")) +(defcustom cider-download-java-sources nil + "Whether to automatically download source artifacts for 3rd-party Java classes. + +When enabled, CIDER will attempt to download source JARs from Maven for +Java classes if the source file is not found locally. This downloading only +happens once per artifact, and only when the user jumps to definition or +requests `cider-doc' on a Java class or a member of the class." + :type 'boolean + :group 'cider + :package-version '(cider . "1.17.1")) + (defun cider--print-fn () "Return the value to send in the nrepl.middleware.print/print slot." (pcase cider-print-fn @@ -373,7 +381,7 @@ The result will be a string." (result (cdr (assoc printer (cadr (assoc name cider--print-options-mapping)))))) (symbol-name (or result name)))) -(defun cider--nrepl-print-request-map (&optional right-margin) +(defun cider--nrepl-print-request-plist (&optional right-margin) "Map to merge into requests that require pretty-printing. RIGHT-MARGIN specifies the maximum column-width of the printed result, and is included in the request if non-nil." @@ -385,35 +393,29 @@ is included in the request if non-nil." (map-pairs) (seq-mapcat #'identity) (apply #'nrepl-dict)))) - (map-merge 'list - `(("nrepl.middleware.print/stream?" "1")) - (when cider-print-fn - `(("nrepl.middleware.print/print" ,(cider--print-fn)))) - (when cider-print-quota - `(("nrepl.middleware.print/quota" ,cider-print-quota))) - (when cider-print-buffer-size - `(("nrepl.middleware.print/buffer-size" ,cider-print-buffer-size))) - (unless (nrepl-dict-empty-p print-options) - `(("nrepl.middleware.print/options" ,print-options)))))) - -(defun cider--nrepl-pr-request-map () + `("nrepl.middleware.print/stream?" "1" + ,@(when cider-print-fn + `("nrepl.middleware.print/print" ,(cider--print-fn))) + ,@(when cider-print-quota + `("nrepl.middleware.print/quota" ,cider-print-quota)) + ,@(when cider-print-buffer-size + `("nrepl.middleware.print/buffer-size" ,cider-print-buffer-size)) + ,@(unless (nrepl-dict-empty-p print-options) + `("nrepl.middleware.print/options" ,print-options))))) + +(defun cider--nrepl-pr-request-plist () "Map to merge into requests that do not require pretty printing." (let ((print-options (thread-last cider-print-options (map-pairs) (seq-mapcat #'identity) (apply #'nrepl-dict)))) - (map-merge 'list - `(("nrepl.middleware.print/print" "cider.nrepl.pprint/pr") - ("nrepl.middleware.print/stream?" nil)) - (unless (nrepl-dict-empty-p print-options) - `(("nrepl.middleware.print/options" ,print-options))) - (when cider-print-quota - `(("nrepl.middleware.print/quota" ,cider-print-quota)))))) - -(defun cider--nrepl-content-type-map () - "Map to be merged into an eval request to make it use content-types." - '(("content-type" "true"))) + `("nrepl.middleware.print/print" "cider.nrepl.pprint/pr" + "nrepl.middleware.print/stream?" nil + ,@(unless (nrepl-dict-empty-p print-options) + `("nrepl.middleware.print/options" ,print-options)) + ,@(when cider-print-quota + `("nrepl.middleware.print/quota" ,cider-print-quota))))) (defun cider-tooling-eval (input callback &optional ns connection) "Send the request INPUT to CONNECTION and register the CALLBACK. @@ -667,6 +669,7 @@ CONTEXT represents a completion context for compliment." "ns" ,(cider-current-ns) "prefix" ,prefix "context" ,context + "sort-order" "by-name" ,@(when cider-enhanced-cljs-completion-p '("enhanced-cljs-completion?" "t"))) (cider-nrepl-send-sync-request (cider-current-repl) 'abort-on-input)))) @@ -681,13 +684,25 @@ CONTEXT represents a completion context for compliment." (defun cider-sync-request:info (symbol &optional class member context) "Send \"info\" op with parameters SYMBOL or CLASS and MEMBER, honor CONTEXT." - (let ((var-info (thread-first `("op" "info" - "ns" ,(cider-current-ns) - ,@(when symbol `("sym" ,symbol)) - ,@(when class `("class" ,class)) - ,@(when member `("member" ,member)) - ,@(when context `("context" ,context))) - (cider-nrepl-send-sync-request (cider-current-repl))))) + (let* ((req + `("op" "info" + "ns" ,(cider-current-ns) + ,@(when symbol `("sym" ,symbol)) + ,@(when class `("class" ,class)) + ,@(when member `("member" ,member)) + ,@(when context `("context" ,context)) + ,@(when cider-download-java-sources `("download-sources-jar" "1")))) + (callback + (lambda (resp) + (let ((status (nrepl-dict-get resp "status")) + (coords (nrepl-dict-get resp "coords"))) + (when (member "download-sources-jar" status) + (message "Local source not found, downloading Java sources for artifact %s/%s %s..." + (nrepl-dict-get coords "group") + (nrepl-dict-get coords "artifact") + (nrepl-dict-get coords "version")))))) + (var-info + (cider-nrepl-send-sync-request req (cider-current-repl) nil callback))) (if (member "no-info" (nrepl-dict-get var-info "status")) nil var-info))) @@ -850,7 +865,7 @@ The result entries are relative to the classpath." "Perform nREPL \"format-code\" op with CODE. FORMAT-OPTIONS is an optional configuration map for cljfmt." (let* ((request `("op" "format-code" - "options" ,(cider--nrepl-format-code-request-map format-options) + "options" ,(cider--nrepl-format-code-request-options format-options) "code" ,code)) (response (cider-nrepl-send-sync-request request)) (err (nrepl-dict-get response "err"))) @@ -862,12 +877,9 @@ FORMAT-OPTIONS is an optional configuration map for cljfmt." (defun cider-sync-request:format-edn (edn right-margin) "Perform \"format-edn\" op with EDN and RIGHT-MARGIN." - (let* ((request (thread-last - (map-merge 'list - `(("op" "format-edn") - ("edn" ,edn)) - (cider--nrepl-print-request-map right-margin)) - (seq-mapcat #'identity))) + (let* ((request `("op" "format-edn" + "edn" ,edn + ,@(cider--nrepl-print-request-plist right-margin))) (response (cider-nrepl-send-sync-request request)) (err (nrepl-dict-get response "err"))) (when err diff --git a/cider-clojuredocs.el b/cider-clojuredocs.el index 85ebca8de..0cbb99706 100644 --- a/cider-clojuredocs.el +++ b/cider-clojuredocs.el @@ -1,6 +1,6 @@ ;;; cider-clojuredocs.el --- ClojureDocs integration -*- lexical-binding: t -*- -;; Copyright © 2014-2024 Bozhidar Batsov and CIDER contributors +;; Copyright © 2014-2025 Bozhidar Batsov and CIDER contributors ;; ;; Author: Bozhidar Batsov @@ -25,16 +25,16 @@ ;;; Code: +(require 'subr-x) +(require 'url-vars) + (require 'cider-client) (require 'cider-common) -(require 'subr-x) +(require 'cider-docstring) (require 'cider-popup) (require 'cider-util) - (require 'nrepl-dict) -(require 'url-vars) - (defconst cider-clojuredocs-url "https://clojuredocs.org/") (defconst cider-clojuredocs-buffer "*cider-clojuredocs*") @@ -120,10 +120,7 @@ opposite of what that option dictates." (insert (format " [%s]\n" arglist))) (newline)) (when-let* ((doc (nrepl-dict-get dict "doc")) - ;; As this is a literal docstring from the source code and - ;; there are two spaces at the beginning of lines in docstrings, - ;; we remove them to make it align nicely in ClojureDocs buffer. - (doc (replace-regexp-in-string "\n " "\n" doc))) + (doc (cider-docstring--format doc))) (insert doc "\n") (newline))) diff --git a/cider-common.el b/cider-common.el index f3e6582f9..9ab1aed56 100644 --- a/cider-common.el +++ b/cider-common.el @@ -1,6 +1,6 @@ ;;; cider-common.el --- Common use functions -*- lexical-binding: t; -*- -;; Copyright © 2015-2024 Artur Malabarba +;; Copyright © 2015-2025 Artur Malabarba ;; Author: Artur Malabarba @@ -123,8 +123,9 @@ Use CALLBACK as the completing read var callback." (defun cider-try-symbol-at-point (prompt callback) "Call CALLBACK with symbol at point. On failure, read a symbol name using PROMPT and call CALLBACK with that." - (condition-case nil (funcall callback (cider--kw-to-symbol (cider-symbol-at-point 'look-back))) - ('error (funcall callback (cider-read-from-minibuffer prompt))))) + (condition-case nil + (funcall callback (cider--kw-to-symbol (cider-symbol-at-point 'look-back))) + (error (funcall callback (cider-read-from-minibuffer prompt))))) (declare-function cider-mode "cider-mode") diff --git a/cider-completion-context.el b/cider-completion-context.el index 3ebbf203f..7eca77cf8 100644 --- a/cider-completion-context.el +++ b/cider-completion-context.el @@ -1,6 +1,6 @@ ;;; cider-completion-context.el --- Context parsing -*- lexical-binding: t -*- -;; Copyright © 2013-2024 Bozhidar Batsov, Artur Malabarba and CIDER contributors +;; Copyright © 2013-2025 Bozhidar Batsov, Artur Malabarba and CIDER contributors ;; ;; Author: Bozhidar Batsov ;; Artur Malabarba diff --git a/cider-completion.el b/cider-completion.el index 8e9271454..23954ffd4 100644 --- a/cider-completion.el +++ b/cider-completion.el @@ -1,6 +1,6 @@ ;;; cider-completion.el --- Smart REPL-powered code completion -*- lexical-binding: t -*- -;; Copyright © 2013-2024 Bozhidar Batsov, Artur Malabarba and CIDER contributors +;; Copyright © 2013-2025 Bozhidar Batsov, Artur Malabarba and CIDER contributors ;; ;; Author: Bozhidar Batsov ;; Artur Malabarba @@ -211,7 +211,10 @@ performed by `cider-annotate-completion-function'." ;; ;; This api is better described in the section ;; '21.6.7 Programmed Completion' of the elisp manual. - (cond ((eq action 'metadata) `(metadata (category . cider))) ;; defines a completion category named 'cider, used later in our `completion-category-overrides` logic. + (cond ((eq action 'metadata) + `(metadata + (category . cider) ;; defines a completion category named 'cider, used later in our `completion-category-overrides` logic. + (display-sort-function . identity))) ;; don't override sorting done by backend ((eq (car-safe action) 'boundaries) nil) (t (with-current-buffer (current-buffer) (complete-with-action action (funcall complete) prefix pred))))) @@ -246,9 +249,9 @@ in the buffer." (defun cider-company-docsig (thing) "Return signature for THING." (when-let ((eldoc-info (cider-eldoc-info thing))) - (let* ((ns (lax-plist-get eldoc-info "ns")) - (symbol (lax-plist-get eldoc-info "symbol")) - (arglists (lax-plist-get eldoc-info "arglists"))) + (let* ((ns (cider-plist-get eldoc-info "ns")) + (symbol (cider-plist-get eldoc-info "symbol")) + (arglists (cider-plist-get eldoc-info "arglists"))) (format "%s: %s" (cider-eldoc-format-thing ns symbol thing (cider-eldoc-thing-type eldoc-info)) @@ -268,25 +271,51 @@ in the buffer." ;; which introduced `cider-company-enable-fuzzy-completion') (add-to-list 'completion-styles-alist '(cider - cider-company-unfiltered-candidates + ;; Use `ignore' in place of "try-competion function". + ignore cider-company-unfiltered-candidates "CIDER backend-driven completion style.")) (defun cider-company-enable-fuzzy-completion () - "Enable backend-driven fuzzy completion in the current buffer. + "Enables `cider' completion style for CIDER in all buffers. + +DEPRECATED: please use `cider-enable-cider-completion-style' instead." + (interactive) + (cider-enable-cider-completion-style)) -DEPRECATED: please use `cider-enable-flex-completion' instead." - (setq-local completion-styles '(cider))) +(defun cider-enable-cider-completion-style (&optional arg) + "Enables or disables `cider' completion style for CIDER in all buffers. -(make-obsolete 'cider-company-enable-fuzzy-completion 'cider-enable-flex-completion "1.8.0") +This style supports non-prefix completion candidates returned by the +completion backend. Only affects the `cider' completion category. If ARG +is `1' or nil, enables the custom completion style; if `-1', disables it." + (interactive) + (if (eq arg -1) + (setq completion-category-overrides + (assq-delete-all 'cider completion-category-overrides)) + (let* ((cider (assq 'cider completion-category-overrides)) + (found-styles (assq 'styles cider)) + (new-styles (if found-styles + (cons 'styles (cons 'cider (cdr found-styles))) + '(styles cider basic))) + (new-cider (if cider + (cons 'cider + (cons new-styles + (seq-remove (lambda (x) (equal 'styles (car x))) + (cdr cider)))) + (list 'cider new-styles))) + (new-overrides (cons new-cider + (seq-remove (lambda (x) (equal 'cider (car x))) + completion-category-overrides)))) + (setq completion-category-overrides new-overrides)))) + +(make-obsolete 'cider-company-enable-fuzzy-completion 'cider-enable-cider-completion-style "1.17.0") (defun cider-enable-flex-completion () "Enables `flex' (fuzzy) completion for CIDER in all buffers. Only affects the `cider' completion category.`" (interactive) - (when (< emacs-major-version 27) - (user-error "`cider-enable-flex-completion' requires Emacs 27 or later")) (let ((found-styles (when-let ((cider (assq 'cider completion-category-overrides))) (assq 'styles cider))) (found-cycle (when-let ((cider (assq 'cider completion-category-overrides))) diff --git a/cider-connection.el b/cider-connection.el index a72e014e5..1983c75f8 100644 --- a/cider-connection.el +++ b/cider-connection.el @@ -1,6 +1,6 @@ ;;; cider-connection.el --- Connection and session life-cycle management for CIDER -*- lexical-binding: t -*- ;; -;; Copyright © 2019-2024 Artur Malabarba, Bozhidar Batsov, Vitalie Spinu and CIDER contributors +;; Copyright © 2019-2025 Artur Malabarba, Bozhidar Batsov, Vitalie Spinu and CIDER contributors ;; ;; Author: Artur Malabarba ;; Bozhidar Batsov @@ -264,7 +264,7 @@ We look only at the major and minor components. When the major version is 0, only check that the minor versions match. When the major version is > 0, first check that the major version matches, then that the minor version is >= the required minor version. -VER the 'installed' version, +VER the `installed' version, REQUIRED-VER the version required by cider." (let ((ver* (cider--strip-version-patch ver)) (required-ver* (cider--strip-version-patch required-ver))) @@ -631,7 +631,6 @@ REPL defaults to the current REPL." (define-key map (kbd "j d") #'cider-describe-connection) (define-key map (kbd "j i") #'cider-describe-connection) (define-key map (kbd "C-c C-q") #'cider-quit) - (define-key map (kbd "C-c C-q") #'cider-quit) (define-key map (kbd "C-c C-r") #'cider-restart) (define-key map (kbd "C-c M-r") #'cider-restart) (define-key map (kbd "C-c C-d") #'cider-describe-connection) @@ -786,7 +785,7 @@ Session name can be customized with `cider-session-name-template'." ;;; REPL Buffer Init (defvar-local cider-cljs-repl-type nil - "The type of the ClojureScript runtime ('browser, 'node, 'figwheel, etc.).") + "The type of the ClojureScript runtime (\\='browser, \\='node, \\='figwheel, etc.).") (defvar-local cider-repl-type nil "The type of this REPL buffer, usually either clj or cljs.") @@ -939,6 +938,12 @@ function with the repl buffer set as current." (user-error "No %s REPLs in current session \"%s\"" type (car (sesman-current-session 'CIDER))))) +(defvar-local cider--ancillary-buffer-repl nil + "Special buffer-local variable that contains reference to the REPL connection. +This should be set in ancillary CIDER buffers that originate from some +event (e.g. *cider-inspector*, *cider-error*) and which never change the +REPL (connection) which produced them.") + (defun cider-current-repl (&optional type ensure) "Get the most recent REPL of TYPE from the current session. TYPE is either clj, cljs, multi or any. @@ -952,17 +957,18 @@ no linked session or there is no REPL of TYPE within the current session." (eq cider-repl-type type))) ;; shortcut when in REPL buffer (current-buffer) - (let* ((type (or type (cider-repl-type-for-buffer))) - (repls (cider-repls type ensure)) - (repl (if (<= (length repls) 1) - (car repls) - ;; pick the most recent one - (seq-find (lambda (b) - (member b repls)) - (buffer-list))))) - (if (and ensure (null repl)) - (cider--no-repls-user-error type) - repl))))) + (or cider--ancillary-buffer-repl + (let* ((type (or type (cider-repl-type-for-buffer))) + (repls (cider-repls type ensure)) + (repl (if (<= (length repls) 1) + (car repls) + ;; pick the most recent one + (seq-find (lambda (b) + (member b repls)) + (buffer-list))))) + (if (and ensure (null repl)) + (cider--no-repls-user-error type) + repl)))))) (defun cider--match-repl-type (type buffer) "Return non-nil if TYPE matches BUFFER's REPL type." diff --git a/cider-debug.el b/cider-debug.el index 5b9a69b38..ce24cb31b 100644 --- a/cider-debug.el +++ b/cider-debug.el @@ -1,6 +1,6 @@ ;;; cider-debug.el --- CIDER interaction with the cider.debug nREPL middleware -*- lexical-binding: t; -*- -;; Copyright © 2015-2024 Bozhidar Batsov, Artur Malabarba and CIDER contributors +;; Copyright © 2015-2025 Bozhidar Batsov, Artur Malabarba and CIDER contributors ;; Author: Artur Malabarba @@ -97,10 +97,6 @@ configure `cider-debug-prompt' instead." (const :tag "Both" both)) :package-version '(cider . "0.9.1")) -(make-obsolete 'cider-debug-print-length 'cider-debug-print-options "0.20") -(make-obsolete 'cider-debug-print-level 'cider-debug-print-options "0.20") -(make-obsolete-variable 'cider-debug-print-options 'cider-print-options "0.21") - ;;; Implementation (declare-function cider-browse-ns--combined-vars-with-meta "cider-browse-ns") @@ -143,11 +139,8 @@ configure `cider-debug-prompt' instead." (defun cider--debug-init-connection () "Initialize a connection with the cider.debug middleware." (cider-nrepl-send-request - (thread-last - (map-merge 'list - '(("op" "init-debugger")) - (cider--nrepl-print-request-map fill-column)) - (seq-mapcat #'identity)) + `("op" "init-debugger" + ,@(cider--nrepl-print-request-plist fill-column)) #'cider--debug-response-handler)) @@ -683,8 +676,7 @@ needed. It is expected to contain at least \"key\", \"input-type\", and (setq cider--debug-mode-response response) (cider--debug-mode 1))) (when inspect - (setq cider-inspector--current-repl (cider-current-repl)) - (cider-inspector--render-value inspect))) + (cider-inspector--render-value inspect :next-inspectable))) ;; If something goes wrong, we send a "quit" or the session hangs. (error (cider-debug-mode-send-reply ":quit" key) (message "Error encountered while handling the debug message: %S" e))))) diff --git a/cider-doc.el b/cider-doc.el index addfdeb82..d9d4d6466 100644 --- a/cider-doc.el +++ b/cider-doc.el @@ -1,6 +1,6 @@ ;;; cider-doc.el --- CIDER documentation functionality -*- lexical-binding: t -*- -;; Copyright © 2014-2024 Bozhidar Batsov, Jeff Valk and CIDER contributors +;; Copyright © 2014-2025 Bozhidar Batsov, Jeff Valk and CIDER contributors ;; Author: Jeff Valk @@ -428,10 +428,11 @@ in a COMPACT format is specified, FOR-TOOLTIP if specified." "doc-first-sentence-fragments" (nrepl-dict-get info "doc-first-sentence-fragments")))) (fetched-doc (nrepl-dict-get info "doc")) (doc (or rendered-fragments - (if compact - (cider-docstring--trim - (cider-docstring--format fetched-doc)) - fetched-doc) + (when fetched-doc + (if compact + (cider-docstring--trim + (cider-docstring--format fetched-doc)) + fetched-doc)) (unless compact "Not documented."))) (url (nrepl-dict-get info "url")) diff --git a/cider-docstring.el b/cider-docstring.el index 007bb0a8b..4b4430c32 100644 --- a/cider-docstring.el +++ b/cider-docstring.el @@ -1,6 +1,6 @@ ;;; cider-docstring.el --- Docstring rendering -*- lexical-binding: t -*- -;; Copyright © 2013-2024 Bozhidar Batsov and CIDER contributors +;; Copyright © 2013-2025 Bozhidar Batsov and CIDER contributors ;; ;; Author: Bozhidar Batsov @@ -101,15 +101,15 @@ Note that `cider-docstring' will trim thing smartly, for Java doc comments: (defun cider--render-docstring-first-sentence (eldoc-info) "Render the first sentence of the docstring extracted from ELDOC-INFO." - (when-let ((first-sentence-fragments (lax-plist-get eldoc-info "doc-first-sentence-fragments"))) + (when-let ((first-sentence-fragments (cider-plist-get eldoc-info "doc-first-sentence-fragments"))) (cider--fragments-to-s first-sentence-fragments))) (defun cider--render-docstring (eldoc-info) "Renders the docstring from ELDOC-INFO based on its length and content. Prioritize rendering as much as possible while staying within `cider-docstring-max-lines'." - (let* ((first-sentence-fragments (lax-plist-get eldoc-info "doc-first-sentence-fragments")) - (body-fragments (lax-plist-get eldoc-info "doc-fragments")) - (block-tags-fragments (lax-plist-get eldoc-info "doc-block-tags-fragments")) + (let* ((first-sentence-fragments (cider-plist-get eldoc-info "doc-first-sentence-fragments")) + (body-fragments (cider-plist-get eldoc-info "doc-fragments")) + (block-tags-fragments (cider-plist-get eldoc-info "doc-block-tags-fragments")) (block-tags-fragments-rendered (cider--fragments-to-s block-tags-fragments)) (first-sentence-fragments-rendered) ;; mutable, for performance (first-attempt (when body-fragments @@ -143,19 +143,22 @@ Prioritize rendering as much as possible while staying within `cider-docstring-m (cl-defun cider-docstring--trim (string &optional (max-lines cider-docstring-max-lines)) "Return MAX-LINES of STRING, adding \"...\" if trimming was necessary." - (let* ((lines (split-string string "\n")) - (string (string-join (seq-take lines max-lines) "\n"))) - (concat string (when (> (length lines) max-lines) "...")))) + (when string + (let* ((lines (split-string string "\n")) + (string (string-join (seq-take lines max-lines) "\n"))) + (concat string (when (> (length lines) max-lines) "..."))))) (defun cider-docstring--format (string) - "Return a nicely formatted STRING to be displayed to the user." - (let* ((string (replace-regexp-in-string "\\. " ".\n\n" string)) ;; improve the formatting of e.g. clojure.core/reduce - (string (mapconcat (lambda (line) - ;; Remove spaces at the beginning of each line, as it is common in many clojure.core defns: - (replace-regexp-in-string "\\`[ ]+" "" line)) - (split-string string "\n") - "\n"))) - string)) + "Return a nicely formatted STRING to be displayed to the user. + +We need to format the docstring before displaying it to the user +because it is obtained from the source code. For example, this means +that it usually has two spaces before each line used for indentation +\(see https://guide.clojure.style/#docstring-indentation). While displaying +the docstring to the user, we usually want to control indentation and +other aspects of the presentation, so we format it before displaying." + (when string + (replace-regexp-in-string "\n " "\n" string))) (provide 'cider-docstring) ;;; cider-docstring.el ends here diff --git a/cider-eldoc.el b/cider-eldoc.el index 4c094b06c..1cc4674f2 100644 --- a/cider-eldoc.el +++ b/cider-eldoc.el @@ -1,7 +1,7 @@ ;;; cider-eldoc.el --- eldoc support for Clojure -*- lexical-binding: t -*- -;; Copyright © 2012-2024 Tim King, Phil Hagelberg, Bozhidar Batsov -;; Copyright © 2013-2024 Bozhidar Batsov, Artur Malabarba and CIDER contributors +;; Copyright © 2012-2025 Tim King, Phil Hagelberg, Bozhidar Batsov +;; Copyright © 2013-2025 Bozhidar Batsov, Artur Malabarba and CIDER contributors ;; ;; Author: Tim King ;; Phil Hagelberg @@ -214,15 +214,15 @@ Otherwise, only the docstring is returned." THING is the variable name. ELDOC-INFO is a p-list containing the eldoc information." - (let* ((ns (lax-plist-get eldoc-info "ns")) - (symbol (lax-plist-get eldoc-info "symbol")) + (let* ((ns (cider-plist-get eldoc-info "ns")) + (symbol (cider-plist-get eldoc-info "symbol")) (docstring (or (cider--render-docstring-first-sentence eldoc-info) (cider--render-docstring eldoc-info) - (cider-docstring--trim - (cider-docstring--format - (lax-plist-get eldoc-info "docstring"))))) + (when-let (docstring (cider-plist-get eldoc-info "docstring")) + (cider-docstring--trim + (cider-docstring--format docstring))))) ;; if it's a single class (and not multiple class candidates), that's it - (maybe-class (car (lax-plist-get eldoc-info "class"))) + (maybe-class (car (cider-plist-get eldoc-info "class"))) (formatted-var (or (when maybe-class (cider-propertize maybe-class 'var)) (cider-eldoc-format-thing ns symbol thing 'var)))) @@ -233,9 +233,9 @@ information." "Return the formatted eldoc string for a function. THING is the function name. POS is the argument-index of the functions arglists. ELDOC-INFO is a p-list containing the eldoc information." - (let ((ns (lax-plist-get eldoc-info "ns")) - (symbol (lax-plist-get eldoc-info "symbol")) - (arglists (lax-plist-get eldoc-info "arglists"))) + (let ((ns (cider-plist-get eldoc-info "ns")) + (symbol (cider-plist-get eldoc-info "symbol")) + (arglists (cider-plist-get eldoc-info "arglists"))) (format "%s: %s" (cider-eldoc-format-thing ns symbol thing 'fn) (cider-eldoc-format-arglist arglists pos)))) @@ -245,13 +245,13 @@ arglists. ELDOC-INFO is a p-list containing the eldoc information." THING is the special form's name. POS is the argument index of the special-form's arglists. ELDOC-INFO is a p-list containing the eldoc information." - (let* ((ns (lax-plist-get eldoc-info "ns")) - (special-form-symbol (lax-plist-get eldoc-info "symbol")) + (let* ((ns (cider-plist-get eldoc-info "ns")) + (special-form-symbol (cider-plist-get eldoc-info "symbol")) (arglists (mapcar (lambda (arglist) (if (equal (car arglist) special-form-symbol) (cdr arglist) arglist)) - (lax-plist-get eldoc-info "arglists")))) + (cider-plist-get eldoc-info "arglists")))) (format "%s: %s" (cider-eldoc-format-thing ns special-form-symbol thing 'fn) (cider-eldoc-format-arglist arglists pos)))) @@ -333,7 +333,7 @@ if the maximum number of sexps to skip is exceeded." (defun cider-eldoc-thing-type (eldoc-info) "Return the type of the ELDOC-INFO being displayed by eldoc. It can be a function or var now." - (or (pcase (lax-plist-get eldoc-info "type") + (or (pcase (cider-plist-get eldoc-info "type") ("function" 'fn) ("special-form" 'special-form) ("macro" 'macro) @@ -453,9 +453,9 @@ This includes the arglist and ns and symbol name (if available)." ;; add inputs of datomic query ((and (equal ns-or-class "datomic.api") (equal name-or-member "q")) - (let ((arglists (lax-plist-get eldoc-plist "arglists"))) - (lax-plist-put eldoc-plist "arglists" - (cider--eldoc-add-datomic-query-inputs-to-arglists arglists)))) + (let ((arglists (cider-plist-get eldoc-plist "arglists"))) + (cider-plist-put eldoc-plist "arglists" + (cider--eldoc-add-datomic-query-inputs-to-arglists arglists)))) ;; if none of the clauses is successful, do cache the eldoc (t (setq cider-eldoc-last-symbol (list thing eldoc-plist)))) ;; middleware eldoc lookups are expensive, so we @@ -499,9 +499,9 @@ Only useful for interop forms. Clojure forms would be returned unchanged." ;; don't try to provide eldoc in EDN buffers (not (cider--eldoc-edn-file-p buffer-file-name))) (let* ((sexp-eldoc-info (cider-eldoc-info-in-current-sexp)) - (eldoc-info (lax-plist-get sexp-eldoc-info "eldoc-info")) - (pos (lax-plist-get sexp-eldoc-info "pos")) - (thing (lax-plist-get sexp-eldoc-info "thing"))) + (eldoc-info (cider-plist-get sexp-eldoc-info "eldoc-info")) + (pos (cider-plist-get sexp-eldoc-info "pos")) + (thing (cider-plist-get sexp-eldoc-info "thing"))) (when eldoc-info (cond ((eq (cider-eldoc-thing-type eldoc-info) 'var) diff --git a/cider-eval.el b/cider-eval.el index 7325abde7..21e1b39ef 100644 --- a/cider-eval.el +++ b/cider-eval.el @@ -1,7 +1,7 @@ ;;; cider-eval.el --- Interactive evaluation (compilation) functionality -*- lexical-binding: t -*- -;; Copyright © 2012-2024 Tim King, Phil Hagelberg, Bozhidar Batsov -;; Copyright © 2013-2024 Bozhidar Batsov, Artur Malabarba and CIDER contributors +;; Copyright © 2012-2025 Tim King, Phil Hagelberg, Bozhidar Batsov +;; Copyright © 2013-2025 Bozhidar Batsov, Artur Malabarba and CIDER contributors ;; ;; Author: Tim King ;; Phil Hagelberg @@ -122,9 +122,9 @@ Only applies when the *cider-inspect* buffer is currently visible." If nil, files are not saved. If 'prompt, the user is prompted to save the file if it's been modified. If t, save the file without confirmation." - :type '(choice (const prompt :tag "Prompt to save the file if it's been modified") - (const nil :tag "Don't save the file") - (const t :tag "Save the file without confirmation")) + :type '(choice (const :tag "Prompt to save the file if it's been modified" prompt) + (const :tag "Don't save the file" nil) + (const :tag "Save the file without confirmation" t)) :group 'cider :package-version '(cider . "0.6.0")) @@ -204,190 +204,6 @@ When invoked with a prefix ARG the command doesn't prompt for confirmation." (save-excursion (quit-window nil error-win)))) - -;;; Sideloader -;; -;; nREPL includes sideloader middleware which provides a Java classloader that -;; is able to dynamically load classes and resources at runtime by interacting -;; with the nREPL client (as opposed to using the classpath of the JVM hosting -;; nREPL server). -;; -;; This performs a similar functionality as the load-file -;; operation, where we can load Clojure namespaces (as source files) or Java -;; classes (as bytecode) by simply requiring or importing them. -;; -;; See https://nrepl.org/nrepl/design/middleware.html#sideloading - -(defcustom cider-sideloader-path nil - "List of directories and jar files to scan for sideloader resources. -When not set the cider-nrepl jar will be added automatically when upgrading -an nREPL connection." - :type 'list - :group 'cider - :package-version '(cider . "1.2.0")) - -(defcustom cider-dynload-cider-nrepl-version nil - "Version of the cider-nrepl jar used for dynamically upgrading a connection. -Defaults to `cider-required-middleware-version'." - :type 'string - :group 'cider - :package-version '(cider . "1.2.0")) - -(defun cider-read-bytes (path) - "Read binary data from PATH. -Return the binary data as unibyte string." - ;; based on f-read-bytes - (with-temp-buffer - (set-buffer-multibyte nil) - (setq buffer-file-coding-system 'binary) - (insert-file-contents-literally path nil) - (buffer-substring-no-properties (point-min) (point-max)))) - -(defun cider-retrieve-resource (dirs name) - "Find a resource NAME in a list DIRS of directories or jar files. -Similar to a classpath lookup. Returns the file contents as a string." - (seq-some - (lambda (path) - (cond - ((file-directory-p path) - (let ((expanded (expand-file-name name path))) - (when (file-exists-p expanded) - (cider-read-bytes expanded)))) - ((and (file-exists-p path) (string-suffix-p ".jar" path)) - (cider-jar-retrieve-resource path name)))) - dirs)) - -(defun cider-provide-file (file) - "Provide FILE in a format suitable for sideloading." - (let ((contents (cider-retrieve-resource cider-sideloader-path file))) - (if contents - (base64-encode-string contents 'no-line-breaks) - ;; if we can't find the file we should return an empty string - (base64-encode-string "")))) - -(defun cider-sideloader-lookup-handler () - "Make a sideloader-lookup handler." - (lambda (response) - (nrepl-dbind-response response (id status type name) - (if status - (when (member "sideloader-lookup" status) - (cider-request:sideloader-provide id type name)))))) - -(defun cider-add-middleware-handler (continue) - "Make a add-middleware handler. -CONTINUE is an optional continuation function." - (lambda (response) - (nrepl-dbind-response response (status unresolved-middleware) ;; id middleware - (when unresolved-middleware - (seq-do - (lambda (mw) - (cider-repl-emit-interactive-stderr - (concat "WARNING: middleware " mw " was not found or failed to load.\n"))) - unresolved-middleware)) - (when (and status (member "done" status) continue) - (funcall continue))))) - -(defun cider-request:sideloader-start (&optional connection tooling) - "Perform the nREPL \"sideloader-start\" op. -If CONNECTION is nil, use `cider-current-repl'. -If TOOLING is truthy then the operation is performed over the tooling -session, rather than the regular session." - (cider-ensure-op-supported "sideloader-start") - (cider-nrepl-send-request `("op" "sideloader-start") - (cider-sideloader-lookup-handler) - connection - tooling)) - -(defun cider-request:sideloader-provide (id type file &optional connection) - "Perform the nREPL \"sideloader-provide\" op for ID, TYPE and FILE. -If CONNECTION is nil, use `cider-current-repl'." - (cider-nrepl-send-request `("id" ,id - "op" "sideloader-provide" - "type" ,type - "name" ,file - "content" ,(cider-provide-file file)) - (cider-sideloader-lookup-handler) - connection)) - -(defun cider-sideloader-start (&optional connection) - "Start nREPL's sideloader. -If CONNECTION is nil, use `cider-current-repl'." - (interactive) - (message "Starting nREPL's sideloader") - (cider-request:sideloader-start connection) - (cider-request:sideloader-start connection 'tooling)) - -(defvar cider-nrepl-middlewares - '("cider.nrepl/wrap-apropos" - "cider.nrepl/wrap-classpath" - "cider.nrepl/wrap-clojuredocs" - "cider.nrepl/wrap-complete" - "cider.nrepl/wrap-content-type" - "cider.nrepl/wrap-debug" - "cider.nrepl/wrap-enlighten" - "cider.nrepl/wrap-format" - "cider.nrepl/wrap-info" - "cider.nrepl/wrap-inspect" - "cider.nrepl/wrap-log" - "cider.nrepl/wrap-macroexpand" - "cider.nrepl/wrap-ns" - "cider.nrepl/wrap-out" - "cider.nrepl/wrap-slurp" - "cider.nrepl/wrap-profile" - "cider.nrepl/wrap-refresh" - "cider.nrepl/wrap-resource" - "cider.nrepl/wrap-spec" - "cider.nrepl/wrap-stacktrace" - "cider.nrepl/wrap-test" - "cider.nrepl/wrap-trace" - "cider.nrepl/wrap-tracker" - "cider.nrepl/wrap-undef" - "cider.nrepl/wrap-version" - "cider.nrepl/wrap-xref")) - -(defun cider-request:add-middleware (middlewares - &optional connection tooling continue) - "Use the nREPL dynamic loader to add MIDDLEWARES to the nREPL session. - -- If CONNECTION is nil, use `cider-current-repl'. -- If TOOLING it truthy, use the tooling session instead of the main session. -- CONTINUE is an optional continuation function, which will be called when the -add-middleware op has finished successfully." - (cider-nrepl-send-request `("op" "add-middleware" - "middleware" ,middlewares) - (cider-add-middleware-handler continue) - connection - tooling)) - -(defun cider-add-cider-nrepl-middlewares (&optional connection) - "Use dynamic loading to add the cider-nrepl middlewares to nREPL. -If CONNECTION is nil, use `cider-current-repl'." - (cider-request:add-middleware - cider-nrepl-middlewares connection nil - (lambda () - ;; When the main session is done adding middleware, then do the tooling - ;; session. At this point all the namespaces have been sideloaded so this - ;; is faster, we don't want these to race to sideload resources. - (cider-request:add-middleware - cider-nrepl-middlewares connection 'tooling - (lambda () - ;; Ask nREPL again what its capabilities are, so we know which new - ;; operations are supported. - (nrepl--init-capabilities (or connection (cider-current-repl)))))))) - -(defvar cider-required-middleware-version) -(defun cider-upgrade-nrepl-connection (&optional connection) - "Sideload cider-nrepl middleware. -If CONNECTION is nil, use `cider-current-repl'." - (interactive) - (when (not cider-sideloader-path) - (setq cider-sideloader-path (list (cider-jar-find-or-fetch - "cider" "cider-nrepl" - (or cider-dynload-cider-nrepl-version - cider-required-middleware-version))))) - (cider-sideloader-start connection) - (cider-add-cider-nrepl-middlewares connection)) - ;;; Dealing with compilation (evaluation) errors and warnings (defun cider-find-property (property &optional backward) @@ -429,17 +245,18 @@ currently selected buffer." '(t always only-in-repl) '(t always except-in-repl))))) -(defun cider-new-error-buffer (&optional mode error-types) +(defun cider-new-error-buffer (&optional mode error-types dont-show) "Return an empty error buffer using MODE. When deciding whether to display the buffer, takes into account not only the value of `cider-show-error-buffer' and the currently selected buffer but also the ERROR-TYPES of the error, which is checked against the -`cider-stacktrace-suppressed-errors' set. +`cider-stacktrace-suppressed-errors' set, and the value of DONT-SHOW. When deciding whether to select the buffer, takes into account the value of `cider-auto-select-error-buffer'." (if (and (cider--show-error-buffer-p) + (not dont-show) (not (cider-stacktrace-some-suppressed-errors-p error-types))) (cider-popup-buffer cider-error-buffer cider-auto-select-error-buffer mode 'ancillary) (cider-make-popup-buffer cider-error-buffer mode 'ancillary))) @@ -478,13 +295,22 @@ When clojure.stracktrace is not present." (cider-nrepl-sync-request:eval "(println (ex-data *e))"))) -(defun cider--render-stacktrace-causes (causes &optional error-types) +(defun cider--render-stacktrace-causes (causes &optional error-types + is-compilation repl) "If CAUSES is non-nil, render its contents into a new error buffer. Optional argument ERROR-TYPES contains a list which should determine the -op/situation that originated this error." +op/situation that originated this error. +If IS-COMPILATION is true, render the stacktrace into the error buffer but +don't bring it forward. +REPL connection can be provided to set it as the connection for the created +*cider-error* buffer." (when causes - (let ((error-buffer (cider-new-error-buffer #'cider-stacktrace-mode error-types))) - (cider-stacktrace-render error-buffer (reverse causes) error-types)))) + (let* ((repl (or repl (cider-current-repl))) + (error-buffer (cider-new-error-buffer #'cider-stacktrace-mode + error-types is-compilation))) + (with-current-buffer error-buffer + (setq cider--ancillary-buffer-repl repl)) + (cider-stacktrace-render error-buffer causes error-types)))) (defconst cider-clojure-compilation-error-phases-default-value '("read-source" @@ -520,43 +346,80 @@ https://clojure.org/reference/repl_and_main#_at_repl" cider-clojure-compilation-error-phases-default-value cider-clojure-compilation-error-phases)) -(defun cider--handle-stacktrace-response (response causes ex-phase) - "Handle stacktrace RESPONSE, aggregate the result into CAUSES, honor EX-PHASE. -If RESPONSE contains a cause, cons it onto CAUSES and return that. If -RESPONSE is the final message (i.e. it contains a status), render CAUSES -into a new error buffer." - (nrepl-dbind-response response (class msg status type) - (cond ((and (member "notification" status) causes) - (nrepl-notify msg type)) - (class (cons response causes)) - (status - (unless (member ex-phase (cider-clojure-compilation-error-phases)) - (cider--render-stacktrace-causes causes)))))) - -(defun cider-default-err-op-handler () - "Display the last exception, with middleware support." +(defun cider--display-error-unobtrusively (buffer err) + "Display ERR as a minibuffer message and/or as a temporary overlay in BUFFER." + (let ((cider-result-use-clojure-font-lock nil) + (trimmed-err (funcall cider-inline-error-message-function err))) + (with-current-buffer buffer + (cider--display-interactive-eval-result trimmed-err + 'error + (save-excursion (end-of-line) (point)) + 'cider-error-overlay-face)))) + +(defun cider--handle-stacktrace-response (causes ex-phase source-buffer) + "Handle stacktrace response provided as aggregated CAUSES. +For EX-PHASE that represents compilation errors, don't show *cider-error* +buffer but render an error overlay instead in the SOURCE-BUFFER. +For others, pop up *cider-error* buffer." + ;; Handle special "notification" server messages. + (dolist (cause causes) + (nrepl-dbind-response cause (msg status type) + (when (member "notification" status) + (nrepl-notify msg type)))) + ;; Render stacktrace in *cider-error* buffer if it is a runtime error. + (cider--render-stacktrace-causes + causes nil (member ex-phase (cider-clojure-compilation-error-phases)) + (with-current-buffer source-buffer (cider-current-repl))) + ;; If the error is a compilation error (which we normally don't show + ;; *cider-error* buffer for), or the error buffer is disabled, compensate for + ;; the lack of info with a overlay error. Verify that the provided buffer is + ;; not a REPL buffer but either visits a Clojure source file or is + ;; e.g. cider-scratch. + (when (and source-buffer + (with-current-buffer source-buffer + (or (cider-clojure-major-mode-p) + (cider-clojurec-major-mode-p) + (cider-clojurescript-major-mode-p))) + (or (member ex-phase (cider-clojure-compilation-error-phases)) + (not (cider--show-error-buffer-p)) + (not (cider-connection-has-capability-p 'jvm-compilation-errors)))) + ;; Search if any of the received causes contains a "triage" field. Append it + ;; to the inline error message if found. + (let* ((triage (seq-some (lambda (cause) (nrepl-dict-get cause "triage")) causes)) + (err-message (mapconcat (lambda (cause) (nrepl-dict-get cause "message")) + causes "\n")) + (err-message (if triage + (concat err-message "\n" triage) + err-message))) + (cider--display-error-unobtrusively source-buffer err-message)))) + +(defun cider--analyze-last-stacktrace (callback) + "Send `analyze-last-stacktrace' to server and invoke CALLBACK on the result. +Accumulates a list of causes and then calls CALLBACK on causes and phase." ;; Causes are returned as a series of messages, which we aggregate in `causes' (let (causes ex-phase) (cider-nrepl-send-request - (thread-last - (map-merge 'list - '(("op" "analyze-last-stacktrace")) - (cider--nrepl-print-request-map fill-column)) - (seq-mapcat #'identity)) + `("op" "analyze-last-stacktrace") (lambda (response) - (nrepl-dbind-response response (phase) - (when phase - (setq ex-phase phase))) - ;; While the return value of `cider--handle-stacktrace-response' is not - ;; meaningful for the last message, we do not need the value of `causes' - ;; after it has been handled, so it's fine to set it unconditionally here - (setq causes (cider--handle-stacktrace-response response causes ex-phase)))))) - -(defun cider-default-err-handler () + (nrepl-dbind-response response (status phase) + (if (member "done" status) + (funcall callback causes ex-phase) + (when phase + (setq ex-phase phase)) + (setq causes (append causes (list response))))))))) + +(defun cider-default-err-op-handler (buffer) + "Display the last exception, with middleware support. +Show error overlay in BUFFER if needed." + (cider--analyze-last-stacktrace + (lambda (causes phase) (cider--handle-stacktrace-response causes phase buffer)))) + +(defun cider-default-err-handler (&optional buffer) "This function determines how the error buffer is shown. -It delegates the actual error content to the eval or op handler." +It delegates the actual error content to the eval or op handler. +Show error overlay in BUFFER if needed." (cond ((cider-nrepl-op-supported-p "analyze-last-stacktrace") - (cider-default-err-op-handler)) + (cider-default-err-op-handler buffer)) ((cider-library-present-p "clojure.stacktrace") (cider-default-err-eval-handler)) (t (cider-default-err-eval-print-handler)))) @@ -597,8 +460,6 @@ It delegates the actual error content to the eval or op handler." " - ") "Regexp matching various non-error messages, e.g. reflection warnings.") -;; Please keep this in sync with `cider-clojure-compilation-error-regexp', -;; which is a subset of these regexes. (defconst cider-clojure-compilation-regexp (rx-to-string `(seq bol (or ,cider--clojure-warning @@ -609,17 +470,6 @@ It delegates the actual error content to the eval or op handler." \"Syntax error compiling at (src/workspace_service.clj:227:3).\" \"Unexpected error (ClassCastException) macroexpanding defmulti at (src/haystack/parser.cljc:21:1).\"") -(defconst cider-clojure-compilation-error-regexp - (rx-to-string - `(seq bol ,cider--clojure-1.10-error) - 'nogroup) - "Like `cider-clojure-compilation-regexp', -but excluding warnings such as reflection warnings. - -A few example values that will match: -\"Syntax error compiling at (src/workspace_service.clj:227:3).\" -\"Unexpected error (ClassCastException) macroexpanding defmulti at (src/haystack/parser.cljc:21:1).\"") - (defconst cider--clojure-execution-error `(sequence (or "Error reading eval result " ; phase = :read-eval-result @@ -635,18 +485,6 @@ A few example values that will match: " " ,cider--clojure-1.10-location)) -(defconst cider-clojure-runtime-error-regexp - (rx-to-string - `(seq bol (or ,cider--clojure-execution-error - ,cider--clojure-spec-execution-error)) - 'nogroup) - "Matches runtime errors, as oppsed to compile-time/macroexpansion-time errors. - -A few example values that will match: - -\"Execution error (ArithmeticException) at foo/foo (src/haystack/parser.cljc:4).\" -\"Execution error - invalid arguments to foo/bar at (src/haystack/parser.cljc:4).\"") - (defconst cider-module-info-regexp (rx " (" (minimal-match (one-or-more anything)) @@ -713,45 +551,47 @@ until we find a delimiters that's not inside a string." (nth 3 (syntax-ppss))) (backward-char)))) -(defun cider--find-last-error-location (message) - "Return the location (begin end buffer) from the Clojure error MESSAGE. +(defun cider--find-last-error-location (error-info) + "Return the location (begin end buffer) from the parsed ERROR-INFO. If location could not be found, return nil." (save-excursion - (let ((info (cider-extract-error-info cider-compilation-regexp message))) - (when info - (let ((file (nth 0 info)) - (line (nth 1 info)) - (col (nth 2 info))) - (unless (or (not (stringp file)) - (cider--tooling-file-p file)) - (when-let* ((buffer (cider-find-file file))) - (with-current-buffer buffer - (save-excursion - (save-restriction - (widen) - (goto-char (point-min)) - (forward-line (1- line)) - (move-to-column (or col 0)) - ;; if this condition is false, it means that `col` was a spuriously large value, - ;; therefore the whole calculation should be discarded: - (when (or (not col) ;; if there's no col info, we cannot judge if it's spurious/not - ;; (current-column) never goes past the last column in the actual line, - ;; so if it's <, then the message had spurious info: - (>= (1+ (current-column)) - col)) - (let ((begin (progn (if col (cider--goto-expression-start) (back-to-indentation)) - (point))) - (end (progn (if col (forward-list) (move-end-of-line nil)) - (point)))) - (list begin end buffer))))))))))))) + (when error-info + (let ((file (nth 0 error-info)) + (line (nth 1 error-info)) + (col (nth 2 error-info))) + (unless (or (not (stringp file)) + (cider--tooling-file-p file)) + (when-let* ((buffer (cider-find-file file))) + (with-current-buffer buffer + (save-excursion + (save-restriction + (widen) + (goto-char (point-min)) + (forward-line (1- line)) + (move-to-column (or col 0)) + ;; if this condition is false, it means that `col` was a spuriously large value, + ;; therefore the whole calculation should be discarded: + (when (or (not col) ;; if there's no col info, we cannot judge if it's spurious/not + ;; (current-column) never goes past the last column in the actual line, + ;; so if it's <, then the message had spurious info: + (>= (1+ (current-column)) + col)) + (let ((begin (progn (if col (cider--goto-expression-start) (back-to-indentation)) + (point))) + (end (progn (if col (forward-list) (move-end-of-line nil)) + (point)))) + (list begin end buffer)))))))))))) (defun cider-handle-compilation-errors (message eval-buffer &optional no-jump) - "Highlight and jump to compilation error extracted from MESSAGE, honor NO-JUMP. -EVAL-BUFFER is the buffer that was current during user's interactive -evaluation command. Honor `cider-auto-jump-to-error'." - (when-let* ((loc (cider--find-last-error-location message)) - (overlay (make-overlay (nth 0 loc) (nth 1 loc) (nth 2 loc))) - (info (cider-extract-error-info cider-compilation-regexp message))) + "Parse a possible compiler error MESSAGE and highlight it in EVAL-BUFFER. +If MESSAGE is an error or warning from the compiler, parse the location +data from the message and put an overlay on the given location in the code +buffer. +If `cider-auto-jump-to-error' is enabled and not NO-JUMP, jump to the +parsed location." + (when-let* ((info (cider-extract-error-info cider-compilation-regexp message)) + (loc (cider--find-last-error-location info)) + (overlay (make-overlay (nth 0 loc) (nth 1 loc) (nth 2 loc)))) (let* ((face (nth 3 info)) (note (nth 4 info)) (auto-jump (unless no-jump @@ -780,17 +620,16 @@ evaluation command. Honor `cider-auto-jump-to-error'." ;;; Interactive evaluation handlers -(defun cider-insert-eval-handler (&optional buffer bounds source-buffer on-success-callback) +(defun cider-insert-eval-handler (&optional buffer _bounds source-buffer on-success-callback) "Make an nREPL evaluation handler for the BUFFER, -BOUNDS representing the buffer bounds of the evaled input, +_BOUNDS representing the buffer bounds of the evaled input, SOURCE-BUFFER the original buffer, and ON-SUCCESS-CALLBACK an optional callback. The handler simply inserts the result value in BUFFER." (let ((eval-buffer (current-buffer)) (res "") - (failed nil) - (error-phase-requested nil)) ;; avoid requesting the phase more than once - can happen if there are errors during the phase nrepl sync request. + (failed nil)) (nrepl-make-response-handler (or buffer eval-buffer) ;; value handler: (lambda (_buffer value) @@ -803,27 +642,20 @@ The handler simply inserts the result value in BUFFER." (cider-repl-emit-interactive-stdout out)) ;; stderr handler: (lambda (_buffer err) - (setq failed t) - (when (and source-buffer - (listp bounds)) ;; if it's a list, it represents bounds, otherwise it's a string (code) and we can't display the overlay - (with-current-buffer source-buffer - (let* ((phase (if error-phase-requested - nil - (setq error-phase-requested t) - (cider--error-phase-of-last-exception buffer))) - (end (or (car-safe (cdr-safe bounds)) bounds)) - (end (when end - (copy-marker end)))) - (cider--maybe-display-error-as-overlay phase err end)))) - - (cider-handle-compilation-errors err eval-buffer)) + (cider-repl-emit-interactive-stderr err) + ;; Don't jump + (cider-handle-compilation-errors err eval-buffer t)) ;; done handler: (lambda (_buffer) (when cider-eval-register (set-register cider-eval-register res)) (when (and (not failed) on-success-callback) - (funcall on-success-callback)))))) + (funcall on-success-callback))) + ;; eval-error handler + (lambda (_buffer) + (setq failed t) + (funcall nrepl-err-handler source-buffer))))) (defun cider--emit-interactive-eval-output (output repl-emit-function) "Emit output resulting from interactive code evaluation. @@ -868,22 +700,6 @@ REPL buffer. This is controlled via (cider--make-fringe-overlay (point))) (scan-error nil))))) -(defun cider--error-phase-of-last-exception (buffer) - "Returns the :phase of the latest exception associated to BUFFER, if any." - (when (cider-clojure-compilation-error-phases) - (when-let ((conn (with-current-buffer buffer - (cider-current-repl)))) - (when (cider-nrepl-op-supported-p "analyze-last-stacktrace" conn) - (let ((nrepl-sync-request-timeout 4)) ;; ensure that this feature cannot possibly create an overly laggy UX - (when-let* ((result (nrepl-send-sync-request (thread-last (map-merge 'list - '(("op" "analyze-last-stacktrace")) - (cider--nrepl-print-request-map fill-column)) - (seq-mapcat #'identity)) - conn - 'abort-on-input ;; favor responsiveness over this feature, in case something went wrong. - ))) - (nrepl-dict-get result "phase"))))))) - (defcustom cider-inline-error-message-function #'cider--shorten-error-message "A function that will shorten a given error message, as shown in overlays / the minibuffer (per `cider-use-overlays'). @@ -904,30 +720,6 @@ and the suffix matched by `cider-module-info-regexp'." "") (string-trim))) -(defun cider--maybe-display-error-as-overlay (phase err end) - "Possibly display ERR as an overlay honoring END, -depending on the PHASE." - (when (and (or - ;; if we won't show *cider-error*, because of configuration, the overlay is adequate because it compensates for the lack of info in a compact manner: - (not cider-show-error-buffer) - (not (cider-connection-has-capability-p 'jvm-compilation-errors)) - ;; if we won't show *cider-error*, because of an ignored phase, the overlay is adequate: - (and cider-show-error-buffer - (member phase (cider-clojure-compilation-error-phases)))) - ;; Only show overlays for things that do look like an exception (#3587): - ;; Note: only applicable to JVM Clojure error messages (#3687) - (if (cider-runtime-clojure-p) - (or (string-match-p cider-clojure-runtime-error-regexp err) - (string-match-p cider-clojure-compilation-error-regexp err)) - t)) - ;; Display errors as temporary overlays - (let ((cider-result-use-clojure-font-lock nil) - (trimmed-err (funcall cider-inline-error-message-function err))) - (cider--display-interactive-eval-result trimmed-err - 'error - end - 'cider-error-overlay-face)))) - (declare-function cider-inspect-last-result "cider-inspector") (defun cider-interactive-eval-handler (&optional buffer place) "Make an interactive eval handler for BUFFER. @@ -943,58 +735,48 @@ when `cider-auto-inspect-after-eval' is non-nil." (beg (when beg (copy-marker beg))) (end (when end (copy-marker end))) (fringed nil) - (res "") - (error-phase-requested nil)) ;; avoid requesting the phase more than once - can happen if there are errors during the phase nrepl sync request. - (nrepl-make-response-handler (or buffer eval-buffer) - ;; value handler: - (lambda (_buffer value) - (setq res (concat res value)) - (cider--display-interactive-eval-result res 'value end)) - ;; stdout handler: - (lambda (_buffer out) - (cider-emit-interactive-eval-output out)) - ;; stderr handler: - (lambda (buffer err) - (cider-emit-interactive-eval-err-output err) - - (let ((phase (if error-phase-requested - nil - (setq error-phase-requested t) - (cider--error-phase-of-last-exception buffer)))) - - (cider--maybe-display-error-as-overlay phase err end) - - (cider-handle-compilation-errors err - eval-buffer - ;; we prevent jumping behavior on compilation errors, - ;; because lines tend to be spurious (e.g. 0:0) - ;; and because on compilation errors, normally - ;; the error is 'right there' in the current line - ;; and needs no jumping: - phase))) - ;; done handler: - (lambda (buffer) - (if beg - (unless fringed - (cider--make-fringe-overlays-for-region beg end) - (setq fringed t)) - (cider--make-fringe-overlay end)) - (when (and cider-auto-inspect-after-eval - (boundp 'cider-inspector-buffer) - (windowp (get-buffer-window cider-inspector-buffer 'visible))) - (cider-inspect-last-result) - (select-window (get-buffer-window buffer))) - (when cider-eval-register - (set-register cider-eval-register res)))))) + (res "")) + (nrepl-make-response-handler + (or buffer eval-buffer) + ;; value handler: + (lambda (_buffer value) + (setq res (concat res value)) + (cider--display-interactive-eval-result res 'value end)) + ;; stdout handler: + (lambda (_buffer out) + (cider-emit-interactive-eval-output out)) + ;; stderr handler: + (lambda (_buffer err) + (cider-emit-interactive-eval-err-output err) + (cider-handle-compilation-errors + err eval-buffer + ;; Disable jumping behavior when compiling a single form because + ;; lines tend to be spurious (e.g. 0:0) and the jump brings us to + ;; the beginning of the same form anyway. + t)) + ;; done handler: + (lambda (buffer) + (if beg + (unless fringed + (cider--make-fringe-overlays-for-region beg end) + (setq fringed t)) + (cider--make-fringe-overlay end)) + (when (and cider-auto-inspect-after-eval + (boundp 'cider-inspector-buffer) + (windowp (get-buffer-window cider-inspector-buffer 'visible))) + (cider-inspect-last-result) + (select-window (get-buffer-window buffer))) + (when cider-eval-register + (set-register cider-eval-register res)))))) (defun cider-load-file-handler (&optional buffer done-handler) "Make a load file handler for BUFFER. Optional argument DONE-HANDLER lambda will be run once load is complete." (let ((eval-buffer (current-buffer)) - (res "") - (error-phase-requested nil)) ;; avoid requesting the phase more than once - can happen if there are errors during the phase nrepl sync request. + (res "")) (nrepl-make-response-handler (or buffer eval-buffer) + ;; value (lambda (buffer value) (cider--display-interactive-eval-result value 'value) (when cider-eval-register @@ -1003,31 +785,19 @@ Optional argument DONE-HANDLER lambda will be run once load is complete." (with-current-buffer buffer (cider--make-fringe-overlays-for-region (point-min) (point-max)) (run-hooks 'cider-file-loaded-hook)))) + ;; stdout (lambda (_buffer value) (cider-emit-interactive-eval-output value)) + ;; stderr (lambda (_buffer err) (cider-emit-interactive-eval-err-output err) - ;; 1.- Jump to the error line: - (cider-handle-compilation-errors err eval-buffer) - (with-current-buffer eval-buffer - (let* ((phase (if error-phase-requested - nil - (setq error-phase-requested t) - (cider--error-phase-of-last-exception buffer))) - ;; 2.- Calculate the overlay position, which is the point (per the previous jump), - ;; and then end-of-line (for ensuring the overlay will be rendered properly): - (end (save-excursion - (when (equal cider-result-overlay-position 'at-eol) - (end-of-line)) - (point)))) - (cider--maybe-display-error-as-overlay phase err end)))) + (cider-handle-compilation-errors err eval-buffer)) + ;; done (lambda (buffer) (when cider-eval-register (set-register cider-eval-register res)) (when done-handler - (funcall done-handler buffer))) - (lambda () - (funcall nrepl-err-handler))))) + (funcall done-handler buffer)))))) (defun cider-eval-print-handler (&optional buffer) "Make a handler for evaluating and printing result in BUFFER." @@ -1114,16 +884,15 @@ COMMENT-POSTFIX is the text to output after the last line." (lambda (_buffer warning) (setq res (concat res warning)))))) -(defun cider-popup-eval-handler (&optional buffer bounds source-buffer) +(defun cider-popup-eval-handler (&optional buffer _bounds _source-buffer) "Make a handler for printing evaluation results in popup BUFFER, -BOUNDS representing the buffer bounds of the evaled input, -and SOURCE-BUFFER the original buffer +_BOUNDS representing the buffer bounds of the evaled input, +and _SOURCE-BUFFER the original buffer This is used by pretty-printing commands." ;; NOTE: cider-eval-register behavior is not implemented here for performance reasons. ;; See https://github.com/clojure-emacs/cider/pull/3162 - (let ((chosen-buffer (or buffer (current-buffer))) - (error-phase-requested nil)) ;; avoid requesting the phase more than once - can happen if there are errors during the phase nrepl sync request. + (let ((chosen-buffer (or buffer (current-buffer)))) (nrepl-make-response-handler chosen-buffer ;; value handler: @@ -1133,23 +902,12 @@ This is used by pretty-printing commands." (lambda (_buffer out) (cider-emit-interactive-eval-output out)) ;; stderr handler: - (lambda (buffer err) - (cider-emit-interactive-eval-err-output err) - (when (and source-buffer - (listp bounds)) ;; if it's a list, it represents bounds, otherwise it's a string (code) and we can't display the overlay - (with-current-buffer source-buffer - (let* ((phase (if error-phase-requested - nil - (setq error-phase-requested t) - (cider--error-phase-of-last-exception buffer))) - (end (or (car-safe (cdr-safe bounds)) bounds)) - (end (when end - (copy-marker end)))) - (cider--maybe-display-error-as-overlay phase err end))))) + (lambda (_buffer err) + (cider-emit-interactive-eval-err-output err)) ;; done handler: nil ;; eval-error handler: - (lambda () + (lambda (buffer) (when (and (buffer-live-p chosen-buffer) (member (buffer-name chosen-buffer) cider-ancillary-buffers)) @@ -1157,7 +915,7 @@ This is used by pretty-printing commands." (cider-popup-buffer-quit-function t))) ;; also call the default nrepl-err-handler, so that our custom behavior doesn't void the base behavior: (when nrepl-err-handler - (funcall nrepl-err-handler))) + (funcall nrepl-err-handler buffer))) ;; content type handler: nil ;; truncated handler: @@ -1210,11 +968,12 @@ API. Most other interactive eval functions should rely on this function. If CALLBACK is nil use `cider-interactive-eval-handler'. BOUNDS, if non-nil, is a list of two numbers marking the start and end positions of FORM in its buffer. -ADDITIONAL-PARAMS is a map to be merged into the request message. +ADDITIONAL-PARAMS is a plist to be merged into the request message. If `cider-interactive-eval-override' is a function, call it with the same arguments and only proceed with evaluation if it returns nil." (let ((form (or form (apply #'buffer-substring-no-properties bounds))) + (additional-params (nrepl--alist-to-plist additional-params)) (start (car-safe bounds)) (end (car-safe (cdr-safe bounds)))) (when (and start end) @@ -1241,7 +1000,7 @@ arguments and only proceed with evaluation if it returns nil." (if (cider-ns-form-p form) "user" (cider-current-ns)) (when start (line-number-at-pos start)) (when start (cider-column-number-at-pos start)) - (seq-mapcat #'identity additional-params) + additional-params connection)))))) (defun cider-eval-region (start end) @@ -1250,7 +1009,7 @@ arguments and only proceed with evaluation if it returns nil." (cider-interactive-eval nil nil (list start end) - (cider--nrepl-pr-request-map))) + (cider--nrepl-pr-request-plist))) (defun cider-eval-last-sexp (&optional output-to-current-buffer) "Evaluate the expression preceding point. @@ -1260,7 +1019,7 @@ buffer." (cider-interactive-eval nil (when output-to-current-buffer (cider-eval-print-handler)) (cider-last-sexp 'bounds) - (cider--nrepl-pr-request-map))) + (cider--nrepl-pr-request-plist))) (defun cider-eval-last-sexp-and-replace () "Evaluate the expression preceding point and replace it with its result." @@ -1275,7 +1034,7 @@ buffer." (cider-interactive-eval last-sexp (cider-eval-print-handler) nil - (cider--nrepl-pr-request-map)))) + (cider--nrepl-pr-request-plist)))) (defun cider-eval-list-at-point (&optional output-to-current-buffer) "Evaluate the list (eg. a function call, surrounded by parens) around point. @@ -1302,7 +1061,7 @@ buffer." (cider-interactive-eval tapped-form (when output-to-current-buffer (cider-eval-print-handler)) nil - (cider--nrepl-pr-request-map)))) + (cider--nrepl-pr-request-plist)))) (defun cider-tap-sexp-at-point (&optional output-to-current-buffer) "Evaluate and tap the expression around point. @@ -1351,7 +1110,7 @@ When GUESS is non-nil, attempt to extract the context from parent let-bindings." (cider-interactive-eval code nil bounds - (cider--nrepl-pr-request-map)))) + (cider--nrepl-pr-request-plist)))) (defun cider-eval-last-sexp-in-context (guess) "Evaluate the preceding sexp in user-supplied context. @@ -1390,7 +1149,7 @@ With the prefix arg INSERT-BEFORE, insert before the form, otherwise afterwards. (set-marker (make-marker) insertion-point) cider-comment-prefix) bounds - (cider--nrepl-pr-request-map)))) + (cider--nrepl-pr-request-plist)))) (defun cider-pprint-form-to-comment (form-fn insert-before) "Evaluate the form selected by FORM-FN and insert result as comment. @@ -1420,7 +1179,7 @@ If INSERT-BEFORE is non-nil, insert before the form, otherwise afterwards." cider-comment-continued-prefix comment-postfix) bounds - (cider--nrepl-print-request-map fill-column)))) + (cider--nrepl-print-request-plist fill-column)))) (defun cider-pprint-eval-last-sexp-to-comment (&optional insert-before) "Evaluate the last sexp and insert result as comment. @@ -1474,13 +1233,13 @@ honoring SWITCH-TO-REPL, REQUEST-MAP." "Evaluate the expression preceding point and insert its result in the REPL. If invoked with a PREFIX argument, switch to the REPL buffer." (interactive "P") - (cider--eval-last-sexp-to-repl prefix (cider--nrepl-pr-request-map))) + (cider--eval-last-sexp-to-repl prefix (cider--nrepl-pr-request-plist))) (defun cider-pprint-eval-last-sexp-to-repl (&optional prefix) "Evaluate expr before point and insert its pretty-printed result in the REPL. If invoked with a PREFIX argument, switch to the REPL buffer." (interactive "P") - (cider--eval-last-sexp-to-repl prefix (cider--nrepl-print-request-map fill-column))) + (cider--eval-last-sexp-to-repl prefix (cider--nrepl-print-request-plist fill-column))) (defun cider-eval-print-last-sexp (&optional pretty-print) "Evaluate the expression preceding point. @@ -1491,8 +1250,8 @@ With an optional PRETTY-PRINT prefix it pretty-prints the result." (cider-eval-print-handler) (cider-last-sexp 'bounds) (if pretty-print - (cider--nrepl-print-request-map fill-column) - (cider--nrepl-pr-request-map)))) + (cider--nrepl-print-request-plist fill-column) + (cider--nrepl-pr-request-plist)))) (defun cider--pprint-eval-form (form) "Pretty print FORM in popup buffer." @@ -1503,7 +1262,7 @@ With an optional PRETTY-PRINT prefix it pretty-prints the result." (cider-interactive-eval (when (stringp form) form) handler (when (consp form) form) - (cider--nrepl-print-request-map fill-column))))) + (cider--nrepl-print-request-plist fill-column))))) (defun cider-pprint-eval-last-sexp (&optional output-to-current-buffer) "Evaluate the sexp preceding point and pprint its value. @@ -1567,7 +1326,7 @@ command `cider-debug-defun-at-point'." (concat "#dbg\n" (cider-defun-at-point))) nil (cider-defun-at-point 'bounds) - (cider--nrepl-pr-request-map)))) + (cider--nrepl-pr-request-plist)))) (defun cider--insert-closing-delimiters (code) "Closes all open parenthesized or bracketed expressions of CODE." @@ -1599,7 +1358,7 @@ buffer. It constructs an expression to eval in the following manner: (when output-to-current-buffer (cider-eval-print-handler)) (list beg-of-defun (point)) - (cider--nrepl-pr-request-map)))) + (cider--nrepl-pr-request-plist)))) (defun cider--matching-delimiter (delimiter) "Get the matching (opening/closing) delimiter for DELIMITER." @@ -1630,7 +1389,7 @@ buffer. It constructs an expression to eval in the following manner: (when output-to-current-buffer (cider-eval-print-handler)) (list beg-of-sexp (point)) - (cider--nrepl-pr-request-map)))) + (cider--nrepl-pr-request-plist)))) (defun cider-pprint-eval-defun-at-point (&optional output-to-current-buffer) "Evaluate the \"top-level\" form at point and pprint its value. @@ -1669,7 +1428,7 @@ If VALUE is non-nil, it is inserted into the minibuffer as initial input." (cider-interactive-eval form nil nil - (cider--nrepl-pr-request-map)))))) + (cider--nrepl-pr-request-plist)))))) (defun cider-read-and-eval-defun-at-point () "Insert the toplevel form at point in the minibuffer and output its result. @@ -1839,9 +1598,15 @@ all ns aliases and var mappings from the namespace before reloading it." Useful when the running nREPL on remote host. When UNDEF-ALL is non-nil or called with \\[universal-argument], removes all ns aliases and var mappings from the namespaces being reloaded" - (interactive "DLoad files beneath directory: \nP") - (mapcar (lambda (file) (cider-load-file file undef-all)) - (directory-files-recursively directory "\\.clj[cs]?$"))) + (interactive "DRecursively load files in directory: \nP") + (let* ((files (directory-files-recursively directory "\\.clj[cs]?$")) + (reporter (make-progress-reporter "Loading files" 0 (length files)))) + (seq-do-indexed (lambda (file idx) + (let ((inhibit-message t)) + (cider-load-file file undef-all)) + (progress-reporter-update reporter (1+ idx) file)) + files) + (progress-reporter-done reporter))) (defalias 'cider-eval-file #'cider-load-file "A convenience alias as some people are confused by the load-* names.") diff --git a/cider-find.el b/cider-find.el index 0af706e24..7e6550e09 100644 --- a/cider-find.el +++ b/cider-find.el @@ -1,6 +1,6 @@ ;;; cider-find.el --- Functionality for finding things -*- lexical-binding: t -*- -;; Copyright © 2013-2024 Bozhidar Batsov, Artur Malabarba and CIDER contributors +;; Copyright © 2013-2025 Bozhidar Batsov, Artur Malabarba and CIDER contributors ;; ;; Author: Bozhidar Batsov ;; Artur Malabarba @@ -206,7 +206,7 @@ are disregarded." (current-point (point))) (while continue (setq found (and (search-forward-regexp kw nil 'noerror) - (member 'clojure-keyword-face (text-properties-at (1- (point)))))) + (cider-keyword-at-point-p (1- (point))))) (setq continue (and (not found) ;; if we haven't moved, there's nothing left to search: (not (equal current-point (point))))) diff --git a/cider-format.el b/cider-format.el index 2673430f5..a2b0bcb45 100644 --- a/cider-format.el +++ b/cider-format.el @@ -1,6 +1,6 @@ ;;; cider-format.el --- Code and EDN formatting functionality -*- lexical-binding: t -*- -;; Copyright © 2013-2024 Bozhidar Batsov, Artur Malabarba and CIDER contributors +;; Copyright © 2013-2025 Bozhidar Batsov, Artur Malabarba and CIDER contributors ;; ;; Author: Bozhidar Batsov ;; Artur Malabarba diff --git a/cider-inspector.el b/cider-inspector.el index 79a21efcd..e8de3bf81 100644 --- a/cider-inspector.el +++ b/cider-inspector.el @@ -1,7 +1,7 @@ ;;; cider-inspector.el --- Object inspector -*- lexical-binding: t -*- -;; Copyright © 2013-2024 Vital Reactor, LLC -;; Copyright © 2014-2024 Bozhidar Batsov and CIDER contributors +;; Copyright © 2013-2025 Vital Reactor, LLC +;; Copyright © 2014-2025 Bozhidar Batsov and CIDER contributors ;; Author: Ian Eslick ;; Bozhidar Batsov @@ -30,6 +30,7 @@ (require 'cl-lib) (require 'easymenu) (require 'seq) +(require 'cider-client) (require 'cider-eval) ;; =================================== @@ -78,6 +79,11 @@ The max depth can be also changed interactively within the inspector." :type 'boolean :package-version '(cider . "0.15.0")) +(defcustom cider-inspector-pretty-print nil + "When true, pretty print values in the inspector." + :type 'boolean + :package-version '(cider . "1.18.0")) + (defcustom cider-inspector-skip-uninteresting t "Controls whether to skip over uninteresting values in the inspector. Only applies to navigation with `cider-inspector-prev-inspectable-object' @@ -91,6 +97,12 @@ by clicking or navigating to them by other means." :type 'boolean :package-version '(cider . "0.27.0")) +(defcustom cider-inspector-display-analytics-hint t + "When true, display hint about analytics feature for eligible objects. +Can be turned to nil once the user sees and acknowledges the feature." + :type 'boolean + :package-version '(cider . "1.18.0")) + (defvar cider-inspector-uninteresting-regexp (concat "nil" ; nils are not interesting "\\|:" clojure--sym-regexp ; nor keywords @@ -125,6 +137,7 @@ by clicking or navigating to them by other means." (define-key map "c" #'cider-inspector-set-max-coll-size) (define-key map "C" #'cider-inspector-set-max-nested-depth) (define-key map "v" #'cider-inspector-toggle-view-mode) + (define-key map "y" #'cider-inspector-display-analytics) (define-key map "d" #'cider-inspector-def-current-val) (define-key map "t" #'cider-inspector-tap-current-val) (define-key map "1" #'cider-inspector-tap-at-point) @@ -133,6 +146,8 @@ by clicking or navigating to them by other means." (define-key map "n" #'cider-inspector-next-inspectable-object) (define-key map [(shift tab)] #'cider-inspector-previous-inspectable-object) (define-key map "p" #'cider-inspector-previous-inspectable-object) + (define-key map "P" #'cider-inspector-toggle-pretty-print) + (define-key map (kbd "C-c C-p") #'cider-inspector-print-current-value) (define-key map ":" #'cider-inspect-expr-from-inspector) (define-key map "f" #'forward-char) (define-key map "b" #'backward-char) @@ -205,22 +220,6 @@ With a second prefix argument it prompts for an expression to eval and inspect." These locations are used to emulate `save-excursion' between `cider-inspector-push' and `cider-inspector-pop' operations.") -(defvar cider-inspector-page-location-stack nil - "A stack used to save point locations in inspector buffers. -These locations are used to emulate `save-excursion' between -`cider-inspector-next-page' and `cider-inspector-prev-page' operations.") - -(defvar cider-inspector-last-command nil - "Contains the value of the most recently used `cider-inspector-*' command. -This is used as an alternative to the built-in `last-command'. Whenever we -invoke any command through \\[execute-extended-command] and its variants, -the value of `last-command' is not set to the command it invokes.") - -(defvar cider-inspector--current-repl nil - "Contains the reference to the REPL where inspector was last invoked from. -This is needed for internal inspector buffer operations (push, -pop) to execute against the correct REPL session.") - ;; Operations ;;;###autoload (defun cider-inspect-expr (expr ns) @@ -229,10 +228,10 @@ Interactively, EXPR is read from the minibuffer, and NS the current buffer's namespace." (interactive (list (cider-read-from-minibuffer "Inspect expression: " (cider-sexp-at-point)) (cider-current-ns))) - (setq cider-inspector--current-repl (cider-current-repl)) - (let ((result (cider-sync-request:inspect-expr expr ns 'v2))) + (let ((result (cider-sync-request:inspect-expr expr ns))) (when (nrepl-dict-get result "value") - (cider-inspector--render-value result 'v2)))) + (setq cider-inspector-location-stack nil) + (cider-inspector--render-value result :next-inspectable)))) (defun cider-inspect-expr-from-inspector () "Performs `cider-inspect-expr' in a way that is suitable from the Inspector itself. @@ -248,63 +247,60 @@ In particular, it does not read `cider-sexp-at-point'." "Pop the last value off the inspector stack and render it. See `cider-sync-request:inspect-pop' and `cider-inspector--render-value'." (interactive) - (setq cider-inspector-last-command 'cider-inspector-pop) - (let ((result (cider-sync-request:inspect-pop 'v2))) + (let ((result (cider-sync-request:inspect-pop))) (when (nrepl-dict-get result "value") - (cider-inspector--render-value result 'v2)))) + (cider-inspector--render-value result :pop)))) (defun cider-inspector-push (idx) "Inspect the value at IDX in the inspector stack and render it. See `cider-sync-request:inspect-push' and `cider-inspector--render-value'" (interactive) - (let ((result (cider-sync-request:inspect-push idx 'v2))) + (let ((result (cider-sync-request:inspect-push idx))) (when (nrepl-dict-get result "value") (push (point) cider-inspector-location-stack) - (cider-inspector--render-value result 'v2) - (cider-inspector-next-inspectable-object 1)))) + (cider-inspector--render-value result :next-inspectable)))) -(defun cider-inspector-inspect-last-exception (index) - "Inspects the exception in the cause stack identified by INDEX." +(defun cider-inspector-inspect-last-exception (index &optional ex-data) + "Inspects the exception in the cause stack identified by INDEX. +If EX-DATA is true, inspect ex-data of the exception instead." (interactive) (cl-assert (numberp index)) - (setq cider-inspector--current-repl (cider-current-repl)) - (let ((result (cider-sync-request:inspect-last-exception index 'v2))) + (let ((result (cider-nrepl-send-sync-request + `("op" "inspect-last-exception" + "index" ,index + ,@(when ex-data + `("ex-data" "true"))) + (cider-current-repl)))) (when (nrepl-dict-get result "value") - (push (point) cider-inspector-location-stack) - (cider-inspector--render-value result 'v2) - (cider-inspector-next-inspectable-object 1)))) + (setq cider-inspector-location-stack nil) + (cider-inspector--render-value result :next-inspectable)))) (defun cider-inspector-previous-sibling () "Inspect the previous sibling value within a sequential parent. See `cider-sync-request:inspect-previous-sibling' and `cider-inspector--render-value'" (interactive) - (let ((result (cider-sync-request:inspect-previous-sibling 'v2))) + (let ((result (cider-sync-request:inspect-previous-sibling))) (when (nrepl-dict-get result "value") - (push (point) cider-inspector-location-stack) - (cider-inspector--render-value result 'v2) - (cider-inspector-next-inspectable-object 1)))) + (cider-inspector--render-value result)))) (defun cider-inspector-next-sibling () "Inspect the next sibling value within a sequential parent. See `cider-sync-request:inspect-next-sibling' and `cider-inspector--render-value'" (interactive) - (let ((result (cider-sync-request:inspect-next-sibling 'v2))) + (let ((result (cider-sync-request:inspect-next-sibling))) (when (nrepl-dict-get result "value") - (push (point) cider-inspector-location-stack) - (cider-inspector--render-value result 'v2) - (cider-inspector-next-inspectable-object 1)))) + (cider-inspector--render-value result)))) (defun cider-inspector--refresh-with-opts (&rest opts) "Invokes `inspect-refresh' op with supplied extra OPTS. Re-renders the currently inspected value." (let ((result (cider-nrepl-send-sync-request `("op" "inspect-refresh" ,@opts) - cider-inspector--current-repl))) + (cider-current-repl)))) (when (nrepl-dict-get result "value") - (cider-inspector--render-value result 'v2)))) + (cider-inspector--render-value result)))) (defun cider-inspector-refresh () - "Re-render the currently inspected value. -See `cider-sync-request:inspect-refresh' and `cider-inspector--render-value'" + "Re-render the currently inspected value." (interactive) (cider-inspector--refresh-with-opts)) @@ -313,20 +309,18 @@ See `cider-sync-request:inspect-refresh' and `cider-inspector--render-value'" Does nothing if already on the last page." (interactive) - (push (point) cider-inspector-page-location-stack) - (let ((result (cider-sync-request:inspect-next-page 'v2))) + (let ((result (cider-sync-request:inspect-next-page))) (when (nrepl-dict-get result "value") - (cider-inspector--render-value result 'v2)))) + (cider-inspector--render-value result)))) (defun cider-inspector-prev-page () "Jump to the previous page when expecting a paginated sequence/map. Does nothing if already on the first page." (interactive) - (setq cider-inspector-last-command 'cider-inspector-prev-page) - (let ((result (cider-sync-request:inspect-prev-page 'v2))) + (let ((result (cider-sync-request:inspect-prev-page))) (when (nrepl-dict-get result "value") - (cider-inspector--render-value result 'v2)))) + (cider-inspector--render-value result)))) (defun cider-inspector-set-page-size (page-size) "Set the page size in pagination mode to the specified PAGE-SIZE. @@ -352,13 +346,33 @@ MAX-NESTED-DEPTH is the new value." (interactive (list (read-number "Max nested depth: " cider-inspector-max-nested-depth))) (cider-inspector--refresh-with-opts "max-nested-depth" max-nested-depth)) +(defun cider-inspector-display-analytics () + "Toggle the display of analytics for the inspected object." + (interactive) + ;; Disable hint about analytics feature so that it is never displayed again. + (when cider-inspector-display-analytics-hint + (customize-set-variable 'cider-inspector-display-analytics-hint nil)) + (let ((result (cider-nrepl-send-sync-request `("op" "inspect-display-analytics") + (cider-current-repl)))) + (when (nrepl-dict-get result "value") + (cider-inspector--render-value result :next-inspectable)))) + +(defun cider-inspector-toggle-pretty-print () + "Toggle the pretty printing of values in the inspector." + (interactive) + (let ((result (cider-nrepl-send-sync-request + `("op" "inspect-toggle-pretty-print") + (cider-current-repl)))) + (when (nrepl-dict-get result "value") + (cider-inspector--render-value result)))) + (defun cider-inspector-toggle-view-mode () "Toggle the view mode of the inspector between normal and object view mode." (interactive) (let ((result (cider-nrepl-send-sync-request `("op" "inspect-toggle-view-mode") - cider-inspector--current-repl))) + (cider-current-repl)))) (when (nrepl-dict-get result "value") - (cider-inspector--render-value result 'v2)))) + (cider-inspector--render-value result :next-inspectable)))) (defcustom cider-inspector-preferred-var-names nil "The preferred var names to be suggested by `cider-inspector-def-current-val'. @@ -388,235 +402,159 @@ current-namespace." (interactive (let ((ns (cider-current-ns))) (list (cider-inspector--read-var-name-from-user ns) ns))) - (setq cider-inspector--current-repl (cider-current-repl)) - (when-let* ((result (cider-sync-request:inspect-def-current-val ns var-name 'v2))) - (cider-inspector--render-value result 'v2) + (when-let* ((result (cider-sync-request:inspect-def-current-val ns var-name))) + (cider-inspector--render-value result) (message "Defined current inspector value as #'%s/%s" ns var-name))) +(defun cider-inspector-print-current-value () + "Print the current value of the inspector." + (interactive) + (cider-ensure-connected) + (cider-ensure-op-supported "inspect-print-current-value") + (let ((buffer (cider-popup-buffer cider-result-buffer nil 'clojure-mode 'ancillary))) + (cider-nrepl-send-request + `("op" "inspect-print-current-value" + ,@(cider--nrepl-print-request-plist fill-column)) + (cider-popup-eval-handler buffer) + (cider-current-repl)))) + (defun cider-inspector-tap-current-val () "Sends the current Inspector current value to `tap>'." (interactive) - ;; NOTE: we don't set `cider-inspector--current-repl', because we mean to tap the current value of an existing Inspector, - ;; so whatever repl was used for it, should be used here. - (if cider-inspector--current-repl - (let ((response (cider-sync-request:inspect-tap-current-val))) - (nrepl-dbind-response response (value err) - (if value - (message "Successfully tapped the current Inspector value") - (error "Could not tap the current Inspector value: %s" err)))) - (user-error "No CIDER session found"))) + (let ((response (cider-sync-request:inspect-tap-current-val))) + (nrepl-dbind-response response (value err) + (if value + (message "Successfully tapped the current Inspector value") + (error "Could not tap the current Inspector value: %s" err))))) (defun cider-inspector-tap-at-point () "Sends the current Inspector current sub-value (per POINT) to `tap>'." (interactive) - ;; NOTE: we don't set `cider-inspector--current-repl', because we mean to tap the current value of an existing Inspector, - ;; so whatever repl was used for it, should be used here. - (if cider-inspector--current-repl - (seq-let (property value) (cider-inspector-property-at-point) - (pcase property - (`cider-value-idx - (let* ((idx value) - (response (cider-sync-request:inspect-tap-indexed idx))) - (nrepl-dbind-response response (value err) - (if value - (message "Successfully tapped the Inspector item at point") - (error "Could not tap the Inspector item at point: %s" err))))) - (_ (error "No object at point")))) - (user-error "No CIDER session found"))) + (seq-let (property value) (cider-inspector-property-at-point) + (pcase property + (`cider-value-idx + (let* ((idx value) + (response (cider-sync-request:inspect-tap-indexed idx))) + (nrepl-dbind-response response (value err) + (if value + (message "Successfully tapped the Inspector item at point") + (error "Could not tap the Inspector item at point: %s" err))))) + (_ (error "No object at point"))))) ;; nREPL interactions -(defun cider-sync-request:inspect-pop (&optional v2) - "Move one level up in the inspector stack, -V2 indicates if the entire response should be returned -instead of just its \"value\" entry." - (let ((result (thread-first '("op" "inspect-pop") - (cider-nrepl-send-sync-request cider-inspector--current-repl)))) - (if v2 - result - (nrepl-dict-get result "value")))) - -(defun cider-sync-request:inspect-push (idx &optional v2) - "Inspect the inside value specified by IDX, -V2 indicates if the entire response should be returned -instead of just its \"value\" entry." - (let ((result (thread-first `("op" "inspect-push" - "idx" ,idx) - (cider-nrepl-send-sync-request cider-inspector--current-repl)))) - (if v2 - result - (nrepl-dict-get result "value")))) - -(defun cider-sync-request:inspect-previous-sibling (&optional v2) - "Inspect the previous sibling value within a sequential parent, -V2 indicates if the entire response should be returned -instead of just its \"value\" entry." - (let ((result (thread-first `("op" "inspect-previous-sibling") - (cider-nrepl-send-sync-request cider-inspector--current-repl)))) - (if v2 - result - (nrepl-dict-get result "value")))) -;;;###autoload -(defun cider-sync-request:inspect-last-exception (index &optional v2) - "Inspects the exception in the cause stack identified by INDEX, -V2 indicates if the entire response should be returned -instead of just its \"value\" entry." - (cl-assert (numberp index)) - (let ((result (thread-first `("op" "inspect-last-exception" - "index" ,index) - (cider-nrepl-send-sync-request cider-inspector--current-repl)))) - (if v2 - result - (nrepl-dict-get result "value")))) - -(defun cider-sync-request:inspect-next-sibling (&optional v2) - "Inspect the next sibling value within a sequential parent, -V2 indicates if the entire response should be returned -instead of just its \"value\" entry." - (let ((result (thread-first `("op" "inspect-next-sibling") - (cider-nrepl-send-sync-request cider-inspector--current-repl)))) - (if v2 - result - (nrepl-dict-get result "value")))) - -(defun cider-sync-request:inspect-refresh (&optional v2) - "Re-render the currently inspected value, -V2 indicates if the entire response should be returned -instead of just its \"value\" entry." - (let ((result (thread-first '("op" "inspect-refresh") - (cider-nrepl-send-sync-request cider-inspector--current-repl)))) - (if v2 - result - (nrepl-dict-get result "value")))) - -(defun cider-sync-request:inspect-next-page (&optional v2) - "Jump to the next page in paginated collection view, -V2 indicates if the entire response should be returned -instead of just its \"value\" entry." - (let ((result (thread-first '("op" "inspect-next-page") - (cider-nrepl-send-sync-request cider-inspector--current-repl)))) - (if v2 - result - (nrepl-dict-get result "value")))) - -(defun cider-sync-request:inspect-prev-page (&optional v2) - "Jump to the previous page in paginated collection view, -V2 indicates if the entire response should be returned -instead of just its \"value\" entry." - (let ((result (thread-first '("op" "inspect-prev-page") - (cider-nrepl-send-sync-request cider-inspector--current-repl)))) - (if v2 - result - (nrepl-dict-get result "value")))) - -(defun cider-sync-request:inspect-set-page-size (page-size &optional v2) - "Set the page size in paginated view to PAGE-SIZE, -V2 indicates if the entire response should be returned -instead of just its \"value\" entry." +(defun cider-sync-request:inspect-pop () + "Move one level up in the inspector stack." + (cider-nrepl-send-sync-request `("op" "inspect-pop") + (cider-current-repl))) + +(defun cider-sync-request:inspect-push (idx) + "Inspect the inside value specified by IDX." + (cider-nrepl-send-sync-request `("op" "inspect-push" + "idx" ,idx) + (cider-current-repl))) + +(defun cider-sync-request:inspect-previous-sibling () + "Inspect the previous sibling value within a sequential parent." + (cider-nrepl-send-sync-request `("op" "inspect-previous-sibling") + (cider-current-repl))) + +(defun cider-sync-request:inspect-next-sibling () + "Inspect the next sibling value within a sequential parent." + (cider-nrepl-send-sync-request `("op" "inspect-next-sibling") + (cider-current-repl))) + +(defun cider-sync-request:inspect-next-page () + "Jump to the next page in paginated collection view." + (cider-nrepl-send-sync-request '("op" "inspect-next-page") + (cider-current-repl))) + +(defun cider-sync-request:inspect-prev-page () + "Jump to the previous page in paginated collection view." + (cider-nrepl-send-sync-request '("op" "inspect-prev-page") + (cider-current-repl))) + +(defun cider-sync-request:inspect-set-page-size (page-size) + "Set the page size in paginated view to PAGE-SIZE." (declare (obsolete "use `inspect-refresh' op instead." "1.15.0")) - (let ((result (thread-first `("op" "inspect-set-page-size" - "page-size" ,page-size) - (cider-nrepl-send-sync-request cider-inspector--current-repl)))) - (if v2 - result - (nrepl-dict-get result "value")))) - -(defun cider-sync-request:inspect-set-max-atom-length (max-length &optional v2) - "Set the max length of nested atoms to MAX-LENGTH, -V2 indicates if the entire response should be returned -instead of just its \"value\" entry." + (cider-nrepl-send-sync-request `("op" "inspect-set-page-size" + "page-size" ,page-size) + (cider-current-repl))) + +(defun cider-sync-request:inspect-set-max-atom-length (max-length) + "Set the max length of nested atoms to MAX-LENGTH." (declare (obsolete "use `inspect-refresh' op instead." "1.15.0")) - (let ((result (thread-first `("op" "inspect-set-max-atom-length" - "max-atom-length" ,max-length) - (cider-nrepl-send-sync-request cider-inspector--current-repl)))) - (if v2 - result - (nrepl-dict-get result "value")))) - -(defun cider-sync-request:inspect-set-max-coll-size (max-size &optional v2) - "Set the number of nested collection members to display before truncating. -MAX-SIZE is the new value, V2 indicates if the entire response should be returned -instead of just its \"value\" entry." + (cider-nrepl-send-sync-request `("op" "inspect-set-max-atom-length" + "max-atom-length" ,max-length) + (cider-current-repl))) + +(defun cider-sync-request:inspect-set-max-coll-size (max-size) + "Set the maximum number of nested collection members to display to MAX-SIZE." (declare (obsolete "use `inspect-refresh' op instead." "1.15.0")) - (let ((result (thread-first `("op" "inspect-set-max-coll-size" - "max-coll-size" ,max-size) - (cider-nrepl-send-sync-request cider-inspector--current-repl)))) - (if v2 - result - (nrepl-dict-get result "value")))) - -(defun cider-sync-request:inspect-set-max-nested-depth (max-nested-depth &optional v2) - "Set the level of nesting for collections to display before truncating. -MAX-NESTED-DEPTH is the new value, V2 indicates if the entire response should be returned -instead of just its \"value\" entry." + (cider-nrepl-send-sync-request `("op" "inspect-set-max-coll-size" + "max-coll-size" ,max-size) + (cider-current-repl))) + +(defun cider-sync-request:inspect-set-max-nested-depth (max-nested-depth) + "Set the level of nesting to display before truncating to MAX-NESTED-DEPTH." (declare (obsolete "use `inspect-refresh' op instead." "1.15.0")) - (let ((result (thread-first `("op" "inspect-set-max-nested-depth" - "max-nested-depth" ,max-nested-depth) - (cider-nrepl-send-sync-request cider-inspector--current-repl)))) - (if v2 - result - (nrepl-dict-get result "value")))) - -(defun cider-sync-request:inspect-def-current-val (ns var-name &optional v2) - "Defines a var with VAR-NAME in NS with the current inspector value, -V2 indicates if the entire response should be returned -instead of just its \"value\" entry." - (let ((result (thread-first `("op" "inspect-def-current-value" - "ns" ,ns - "var-name" ,var-name) - (cider-nrepl-send-sync-request cider-inspector--current-repl)))) - (if v2 - result - (nrepl-dict-get result "value")))) + (cider-nrepl-send-sync-request `("op" "inspect-set-max-nested-depth" + "max-nested-depth" ,max-nested-depth) + (cider-current-repl))) + +(defun cider-sync-request:inspect-def-current-val (ns var-name) + "Defines a var with VAR-NAME in NS with the current inspector value." + (cider-nrepl-send-sync-request `("op" "inspect-def-current-value" + "ns" ,ns + "var-name" ,var-name) + (cider-current-repl))) (defun cider-sync-request:inspect-tap-current-val () "Sends current inspector value to tap>." - (cider-nrepl-send-sync-request '("op" "inspect-tap-current-value") cider-inspector--current-repl)) + (cider-nrepl-send-sync-request '("op" "inspect-tap-current-value") + (cider-current-repl))) (defun cider-sync-request:inspect-tap-indexed (idx) "Sends current inspector sub-value to tap>, per IDX." (cl-assert idx) (cider-nrepl-send-sync-request `("op" "inspect-tap-indexed" "idx" ,idx) - cider-inspector--current-repl)) + (cider-current-repl))) -(defun cider-sync-request:inspect-expr (expr ns &optional v2) +(defun cider-sync-request:inspect-expr (expr ns) "Evaluate EXPR in context of NS and inspect its result. Set the page size in paginated view to PAGE-SIZE, maximum length of atomic collection members to MAX-ATOM-LENGTH, and maximum size of nested collections to -MAX-COLL-SIZE if non nil, -V2 indicates if the entire response should be returned -instead of just its \"value\" entry." - (let ((result (thread-first - (append (nrepl--eval-request expr ns) - `("inspect" "true" - ,@(when cider-inspector-page-size - `("page-size" ,cider-inspector-page-size)) - ,@(when cider-inspector-max-atom-length - `("max-atom-length" ,cider-inspector-max-atom-length)) - ,@(when cider-inspector-max-coll-size - `("max-coll-size" ,cider-inspector-max-coll-size)) - ,@(when cider-inspector-max-nested-depth - `("max-nested-depth" ,cider-inspector-max-nested-depth)) - "spacious" ,(if cider-inspector-spacious-collections - "true" "false"))) - (cider-nrepl-send-sync-request cider-inspector--current-repl)))) - (if v2 - result - (nrepl-dict-get result "value")))) +MAX-COLL-SIZE if non nil." + (thread-first + (append (nrepl--eval-request expr ns) + `("inspect" "true" + ,@(when cider-inspector-page-size + `("page-size" ,cider-inspector-page-size)) + ,@(when cider-inspector-max-atom-length + `("max-atom-length" ,cider-inspector-max-atom-length)) + ,@(when cider-inspector-max-coll-size + `("max-coll-size" ,cider-inspector-max-coll-size)) + ,@(when cider-inspector-max-nested-depth + `("max-nested-depth" ,cider-inspector-max-nested-depth)) + ,@(when cider-inspector-display-analytics-hint + `("display-analytics-hint" "true")) + ,@(when cider-inspector-pretty-print + `("pretty-print" "true")))) + (cider-nrepl-send-sync-request (cider-current-repl)))) (declare-function cider-set-buffer-ns "cider-mode") ;; Render Inspector from Structured Values -(defun cider-inspector--render-value (dict-or-value &optional v2) - "Render DICT-OR-VALUE, depending on V2." - (let* ((value (if v2 +(defun cider-inspector--render-value (dict-or-value &optional point-action) + "Render DICT-OR-VALUE. +It can either be a value directly or a inspector response that contains +`value' field. +POINT-ACTION can either be nil (leave point where it is now), `:pop' (pop point +from stack), `:next-inspectable' (move point to next inspectable object)." + (let* ((value (if (nrepl-dict-p dict-or-value) (nrepl-dict-get dict-or-value "value") dict-or-value)) - (fragments (when v2 - (nrepl-dict-get dict-or-value "doc-fragments"))) - (block-tags (when v2 - (nrepl-dict-get dict-or-value "doc-block-tags-fragments"))) (ns (cider-current-ns)) (font-size (when-let* ((b (get-buffer cider-inspector-buffer)) (variable 'text-scale-mode-amount) @@ -629,34 +567,27 @@ instead of just its \"value\" entry." (local-variable-p 'truncate-lines b))) (truncate-lines-p (when-let* ((b (get-buffer cider-inspector-buffer)) (continue truncate-lines-defined)) - (buffer-local-value 'truncate-lines b)))) + (buffer-local-value 'truncate-lines b))) + (repl (cider-current-repl)) + (current-point (point))) (cider-make-popup-buffer cider-inspector-buffer 'cider-inspector-mode 'ancillary) (cider-inspector-render cider-inspector-buffer value :font-size font-size :truncate-lines-defined truncate-lines-defined - :truncate-lines-p truncate-lines-p - :fragments fragments - :block-tags block-tags) + :truncate-lines-p truncate-lines-p) (cider-popup-buffer-display cider-inspector-buffer cider-inspector-auto-select-buffer) (when cider-inspector-fill-frame (delete-other-windows)) - (ignore-errors (cider-inspector-next-inspectable-object 1)) (with-current-buffer cider-inspector-buffer + (setq cider--ancillary-buffer-repl repl) (cider-set-buffer-ns ns) - (when (eq cider-inspector-last-command 'cider-inspector-pop) - (setq cider-inspector-last-command nil) - ;; Prevents error message being displayed when we try to pop - ;; from the top-level of a data structure - (when cider-inspector-location-stack - (goto-char (pop cider-inspector-location-stack)))) - - (when (eq cider-inspector-last-command 'cider-inspector-prev-page) - (setq cider-inspector-last-command nil) - ;; Prevents error message being displayed when we try to - ;; go to a prev-page from the first page - (when cider-inspector-page-location-stack - (goto-char (pop cider-inspector-page-location-stack))))))) - -(cl-defun cider-inspector-render (buffer str &key font-size truncate-lines-defined truncate-lines-p fragments block-tags) + (cond ((eq point-action nil) (goto-char current-point)) + ((eq point-action :next-inspectable) (ignore-errors (cider-inspector-next-inspectable-object 1))) + ((eq point-action :pop) + (goto-char (or (when cider-inspector-location-stack + (pop cider-inspector-location-stack)) + current-point))))))) + +(cl-defun cider-inspector-render (buffer str &key font-size truncate-lines-defined truncate-lines-p) "Render STR in BUFFER." (with-current-buffer buffer (cider-inspector-mode) @@ -666,23 +597,17 @@ instead of just its \"value\" entry." (setq-local truncate-lines truncate-lines-p)) (let ((inhibit-read-only t)) (condition-case nil - (cider-inspector-render* (car (read-from-string str)) - fragments - block-tags) + (cider-inspector-render* (car (read-from-string str))) (error (insert "\nInspector error for: " str)))) (goto-char (point-min)))) (defvar cider-inspector-looking-at-java-p nil) -(defun cider-inspector-render* (elements &optional fragments block-tags) - "Render ELEMENTS, and FRAGMENTS, BLOCK-TAGS if present." +(defun cider-inspector-render* (elements) + "Render ELEMENTS." (setq cider-inspector-looking-at-java-p nil) (dolist (el elements) - (cider-inspector-render-el* el)) - (when fragments - (insert "\n\n") - (insert (cider--render-docstring (list "doc-fragments" fragments - "doc-block-tags-fragments" block-tags))))) + (cider-inspector-render-el* el))) (defconst cider--inspector-java-headers ;; NOTE "--- Static fields:" "--- Instance fields:" are for objects, diff --git a/cider-jar.el b/cider-jar.el index 006ad7ec8..49eb6b4ad 100644 --- a/cider-jar.el +++ b/cider-jar.el @@ -1,6 +1,6 @@ ;;; cider-jar.el --- Jar functionality for Clojure -*- lexical-binding: t -*- -;; Copyright © 2022-2024 Arne Brasseur +;; Copyright © 2022-2025 Arne Brasseur ;; ;; Author: Arne Brasseur diff --git a/cider-log.el b/cider-log.el index 188e88930..af57952cb 100644 --- a/cider-log.el +++ b/cider-log.el @@ -1,6 +1,6 @@ ;;; cider-log.el --- Log inspection functionality for Clojure -*- lexical-binding: t -*- -;; Copyright © 2023-2024 Bozhidar Batsov and CIDER contributors +;; Copyright © 2023-2025 Bozhidar Batsov and CIDER contributors ;; Author: r0man @@ -48,6 +48,20 @@ Example values: \"Logback\", \"Timbre\"." :safe #'stringp :type 'string) +(defcustom cider-log-frameworks-buffer "*cider-log-frameworks*" + "The name of the log frameworks popup buffer." + :group 'cider + :package-version '(cider . "1.17") + :safe #'stringp + :type 'string) + +(defcustom cider-log-auto-select-frameworks-buffer t + "Whether to auto-select the log frameworks popup buffer." + :group 'cider + :package-version '(cider . "1.17") + :safe #'booleanp + :type 'boolean) + (defcustom cider-log-appender-id "cider-log" "The name of the default log appender." :group 'cider @@ -201,15 +215,6 @@ It will not be used if the package hasn't been installed." "filters" ,(cider-log-consumer-filters consumer)) (cider-nrepl-send-request callback))) -(defun cider-request:log-analyze-stacktrace (framework appender event &optional callback) - "Analyze the EVENT stacktrace of the APPENDER of FRAMEWORK and call CALLBACK." - (cider-ensure-op-supported "cider/log-analyze-stacktrace") - (thread-first `("op" "cider/log-analyze-stacktrace" - "framework" ,(cider-log-framework-id framework) - "appender" ,(cider-log-appender-id appender) - "event" ,(cider-log-event-id event)) - (cider-nrepl-send-request callback))) - (defun cider-sync-request:log-update-consumer (framework appender consumer) "Add CONSUMER to the APPENDER of FRAMEWORK and call CALLBACK on log events." (cider-ensure-op-supported "cider/log-update-consumer") @@ -268,13 +273,11 @@ It will not be used if the package hasn't been installed." "Format the log EVENT from the APPENDER of the log FRAMEWORK." (cider-ensure-op-supported "cider/log-format-event") (thread-first - (seq-mapcat #'identity - (map-merge 'list - (cider--nrepl-print-request-map fill-column) - `(("op" "cider/log-format-event") - ("framework" ,(cider-log-framework-id framework)) - ("appender" ,(cider-log-appender-id appender)) - ("event" ,(cider-log-event-id event))))) + `("op" "cider/log-format-event" + "framework" ,(cider-log-framework-id framework) + "appender" ,(cider-log-appender-id appender) + "event" ,(cider-log-event-id event) + ,@(cider--nrepl-print-request-plist fill-column)) (cider-nrepl-send-sync-request) (nrepl-dict-get "cider/log-format-event"))) @@ -678,24 +681,6 @@ The KEYS are used to lookup the values and are joined by SEPARATOR." (seq-doseq (window windows) (set-window-point window (point-max)))))) -(defun cider-log-event--show-stacktrace (framework appender event) - "Show the stacktrace of the log EVENT of FRAMEWORK and APPENDER." - (when (and framework appender event (cider-log-event-exception event)) - (let ((auto-select-buffer cider-auto-select-error-buffer) - (causes nil)) - (cider-request:log-analyze-stacktrace - framework appender event - (lambda (response) - (nrepl-dbind-response response (class status) - (cond (class (setq causes (cons response causes))) - (status (when causes - (cider-stacktrace-render - (cider-popup-buffer cider-error-buffer - auto-select-buffer - #'cider-stacktrace-mode - 'ancillary) - (reverse causes))))))))))) - (defun cider-log-event-next-line (&optional n) "Move N lines forward." (interactive "p") @@ -707,8 +692,6 @@ The KEYS are used to lookup the values and are joined by SEPARATOR." (event (cider-log-event-at-point))) (let ((cider-auto-select-error-buffer nil)) (save-window-excursion - (when (get-buffer-window cider-error-buffer) - (cider-log-event--show-stacktrace framework appender event)) (when (get-buffer-window cider-inspector-buffer) (cider-log-event--inspect framework appender event)) (when (get-buffer-window cider-log-event-buffer) @@ -844,7 +827,6 @@ The KEYS are used to lookup the values and are joined by SEPARATOR." (define-key map (kbd "C-c M-l f") #'cider-log-framework) (define-key map (kbd "C-c M-l i") #'cider-log-info) (define-key map (kbd "C-c M-l l") #'cider-log) - (define-key map (kbd "E") 'cider-log-show-stacktrace) (define-key map (kbd "F") 'cider-log-print-event) (define-key map (kbd "I") 'cider-log-inspect-event) (define-key map (kbd "RET") 'cider-log-inspect-event) @@ -1032,6 +1014,26 @@ the CIDER Inspector and the CIDER stacktrace mode. ;; Framework actions +(transient-define-suffix cider-log-show-frameworks () + "Show the available log frameworks in a buffer." + :description "Show frameworks in a buffer" + (interactive) + (let ((frameworks (cider-sync-request:log-frameworks))) + (with-current-buffer (cider-popup-buffer cider-log-frameworks-buffer + cider-log-auto-select-frameworks-buffer) + (read-only-mode -1) + (insert (with-temp-buffer + (insert (propertize (cider-propertize "Cider Log Frameworks" 'ns) 'ns t) "\n\n") + (dolist (framework frameworks) + (insert (propertize (cider-propertize (cider-log-framework-name framework) 'ns) 'ns t) "\n\n") + (insert (format " Website ......... %s\n" (cider-log-framework-website-url framework))) + (insert (format " Javadocs ........ %s\n" (cider-log-framework-javadoc-url framework))) + (insert (format " Levels .......... %s\n" (string-join (cider-log-framework-level-names framework) ", "))) + (newline)) + (buffer-string))) + (read-only-mode 1) + (goto-char (point-min))))) + (transient-define-suffix cider-log-browse-javadocs (framework) "Browse the Javadoc of the log FRAMEWORK." :description "Browse Java documentation" @@ -1141,16 +1143,12 @@ the CIDER Inspector and the CIDER stacktrace mode. (cider-log-appender-display-name appender))) ;; Event actions - -(transient-define-suffix cider-log-show-stacktrace (framework appender event) - "Show the stacktrace of the log EVENT of FRAMEWORK and APPENDER." - :description "Show log event stacktrace" - :if #'cider-log-event-at-point - :inapt-if-not (lambda () - (when-let (event (cider-log-event-at-point)) - (cider-log-event-exception event))) - (interactive (list (cider-log--framework) (cider-log--appender) (cider-log-event-at-point))) - (cider-log-event--show-stacktrace framework appender event)) +(defun cider-log-show-stacktrace (&rest _) + "Removed." + (interactive) + (message "This function has been removed. +You can jump to functions and methods directly from the printed stacktrace now.")) +(make-obsolete 'cider-log-show-stacktrace nil "1.18") (transient-define-suffix cider-log-print-event (framework appender event) "Format the log EVENT of FRAMEWORK and APPENDER." @@ -1220,6 +1218,7 @@ the CIDER Inspector and the CIDER stacktrace mode. (transient-define-prefix cider-log-framework (framework) "Show the Cider log framework menu." [["Cider Log Framework\n\nActions:" + ("a" cider-log-show-frameworks) ("b" cider-log-set-buffer) ("j" cider-log-browse-javadocs) ("s" cider-log-set-framework) @@ -1406,7 +1405,6 @@ the CIDER Inspector and the CIDER stacktrace mode. (cider-log--threads-option)] ["Actions" ("c" cider-log-clear-event-buffer) - ("e" cider-log-show-stacktrace) ("i" cider-log-inspect-event) ("p" cider-log-print-event) ("s" cider-log--do-search-events)] @@ -1441,6 +1439,7 @@ based on `transient-mode'." (transient-define-prefix cider-log (framework appender) "Show the Cider log menu." [["Framework Actions" + ("fa" cider-log-show-frameworks) ("fs" cider-log-set-framework) ("fb" cider-log-set-buffer) ("fj" cider-log-browse-javadocs) @@ -1463,7 +1462,6 @@ based on `transient-mode'." ["Event Actions" ("eb" cider-log-switch-to-buffer) ("ec" cider-log-clear-event-buffer) - ("ee" cider-log-show-stacktrace) ("ei" cider-log-inspect-event) ("ep" cider-log-print-event) ("es" "Search log events" cider-log-event-search diff --git a/cider-macroexpansion.el b/cider-macroexpansion.el index e445c4331..484827273 100644 --- a/cider-macroexpansion.el +++ b/cider-macroexpansion.el @@ -1,7 +1,7 @@ ;;; cider-macroexpansion.el --- Macro expansion support -*- lexical-binding: t -*- -;; Copyright © 2012-2024 Tim King, Phil Hagelberg, Bozhidar Batsov -;; Copyright © 2013-2024 Bozhidar Batsov, Artur Malabarba and CIDER contributors +;; Copyright © 2012-2025 Tim King, Phil Hagelberg, Bozhidar Batsov +;; Copyright © 2013-2025 Bozhidar Batsov, Artur Malabarba and CIDER contributors ;; ;; Author: Tim King ;; Phil Hagelberg diff --git a/cider-mode.el b/cider-mode.el index ceea16a42..7a7d642ad 100644 --- a/cider-mode.el +++ b/cider-mode.el @@ -1,7 +1,7 @@ ;;; cider-mode.el --- Minor mode for REPL interactions -*- lexical-binding: t -*- -;; Copyright © 2012-2024 Tim King, Phil Hagelberg, Bozhidar Batsov -;; Copyright © 2013-2024 Bozhidar Batsov, Artur Malabarba and CIDER contributors +;; Copyright © 2012-2025 Tim King, Phil Hagelberg, Bozhidar Batsov +;; Copyright © 2013-2025 Bozhidar Batsov, Artur Malabarba and CIDER contributors ;; ;; Author: Tim King ;; Phil Hagelberg @@ -673,7 +673,7 @@ The result depends on the buffer CIDER connection type." (defface cider-traced-face '((((type graphic)) :box (:color "cyan" :line-width -1)) (t :underline t :background "#066")) - "Face used to mark code being traced." + "Face used to mark functions being traced or profiled." :group 'cider :package-version '(cider . "0.11.0")) @@ -805,18 +805,14 @@ with the given LIMIT." (push sym instrumented)) (`"\"light-form\"" (push sym enlightened))) - ;; The ::traced keywords can be inlined by MrAnderson, so - ;; we catch that case too. ;; FIXME: This matches values too, not just keys. - (when (seq-find (lambda (k) (and (stringp k) - (string-match (rx "clojure.tools.trace/traced" eos) k))) - meta) + (when (or (nrepl-dict-get meta "orchard.trace/traced") + (nrepl-dict-get meta "orchard.profile/profiled")) (push sym traced)) (when (and do-deprecated (nrepl-dict-get meta "deprecated")) (push sym deprecated)) (let ((is-macro (nrepl-dict-get meta "macro")) - (is-function (or (nrepl-dict-get meta "fn") - (nrepl-dict-get meta "arglists")))) + (is-function (nrepl-dict-get meta "fn"))) (cond ((and do-macro is-macro) (push sym macros)) ((and do-function is-function) @@ -982,7 +978,12 @@ before point." (not (eq (char-after) ?\())) (condition-case nil (progn (backward-up-list) t) - (scan-error nil)))) + (scan-error nil) + ;; In `clojure-ts-mode', when `backward-up-list' is used, + ;; `user-error' is signaled instead of `scan-error' because + ;; the operation is delegated to the `treesit-up-list' + ;; function. + (user-error nil)))) (setq beg (min beg (point))) ;; If there are locals above the current sexp, reapply them to the ;; current sexp. @@ -1062,7 +1063,12 @@ property." ;;; Minor-mode definition -(defvar x-gtk-use-system-tooltips) + +(if (and (> emacs-major-version 28) + (not (boundp 'x-gtk-use-system-tooltips))) + ;; The x-gtk prefix has been dropped in Emacs 29 + (defvaralias 'x-gtk-use-system-tooltips 'use-system-tooltips) + (defvar x-gtk-use-system-tooltips)) ;;;###autoload (define-minor-mode cider-mode @@ -1089,16 +1095,21 @@ property." ;; `tooltip' has variable-width by default, which looks terrible. (set-face-attribute 'tooltip nil :inherit 'unspecified) (when cider-dynamic-indentation - (setq-local clojure-get-indent-function #'cider--get-symbol-indent)) + (setq-local clojure-get-indent-function #'cider--get-symbol-indent) + (with-suppressed-warnings ((free-vars clojure-ts-get-indent-function)) + (setq-local clojure-ts-get-indent-function + #'cider--get-symbol-indent))) (setq-local clojure-expected-ns-function #'cider-expected-ns) (when cider-use-xref (add-hook 'xref-backend-functions #'cider--xref-backend cider-xref-fn-depth 'local)) + (cider-enable-cider-completion-style 1) (setq next-error-function #'cider-jump-to-compilation-error)) ;; Mode cleanup (mapc #'kill-local-variable '(next-error-function x-gtk-use-system-tooltips font-lock-fontify-region-function - clojure-get-indent-function)) + clojure-get-indent-function + clojure-ts-get-indent-function)) (remove-hook 'completion-at-point-functions #'cider-complete-at-point t) (when cider-use-xref (remove-hook 'xref-backend-functions #'cider--xref-backend 'local)) diff --git a/cider-ns.el b/cider-ns.el index 70e06d0be..1f56c2099 100644 --- a/cider-ns.el +++ b/cider-ns.el @@ -1,6 +1,6 @@ ;;; cider-ns.el --- Namespace manipulation functionality -*- lexical-binding: t -*- -;; Copyright © 2013-2024 Bozhidar Batsov, Artur Malabarba and CIDER contributors +;; Copyright © 2013-2025 Bozhidar Batsov, Artur Malabarba and CIDER contributors ;; ;; Author: Bozhidar Batsov ;; Artur Malabarba @@ -72,9 +72,9 @@ If nil, files are not saved. If 'prompt, the user is prompted to save files if they have been modified. If t, save the files without confirmation." - :type '(choice (const prompt :tag "Prompt to save files if they have been modified") - (const nil :tag "Don't save the files") - (const t :tag "Save the files without confirmation")) + :type '(choice (const :tag "Prompt to save files if they have been modified" prompt) + (const :tag "Don't save the files" nil) + (const :tag "Save the files without confirmation" t)) :group 'cider :package-version '(cider . "0.15.0")) @@ -83,8 +83,8 @@ If t, save the files without confirmation." If a list of modes, any buffers visiting files on the classpath whose major mode is derived from any of the modes might be saved. If t, all buffers visiting files on the classpath might be saved." - :type '(choice listp - (const t)) + :type '(choice (repeat :tag "Enable for specific modes" symbol) + (const :tag "Always" t)) :group 'cider :package-version '(cider . "0.21.0")) @@ -237,15 +237,14 @@ Its behavior is controlled by `cider-ns-save-files-on-refresh' and (defun cider-ns--reload-op (op-name) "Return the reload operation to use. Based on OP-NAME and the value of cider-ns-code-reload-tool defcustom." - (list "op" - (if (eq cider-ns-code-reload-tool 'tools.namespace) - (cond ((string= op-name "reload") "refresh") - ((string= op-name "reload-all") "refresh-all") - ((string= op-name "reload-clear") "refresh-clear")) + (if (eq cider-ns-code-reload-tool 'tools.namespace) + (cond ((string= op-name "reload") "refresh") + ((string= op-name "reload-all") "refresh-all") + ((string= op-name "reload-clear") "refresh-clear")) - (cond ((string= op-name "reload") "cider.clj-reload/reload") - ((string= op-name "reload-all") "cider.clj-reload/reload-all") - ((string= op-name "reload-clear") "cider.clj-reload/reload-clear"))))) + (cond ((string= op-name "reload") "cider.clj-reload/reload") + ((string= op-name "reload-all") "cider.clj-reload/reload-all") + ((string= op-name "reload-clear") "cider.clj-reload/reload-clear")))) ;;;###autoload (defun cider-ns-reload (&optional prompt) @@ -317,17 +316,14 @@ refresh functions (defined in `cider-ns-refresh-before-fn' and nil t)) (when clear? - (cider-nrepl-send-sync-request (cider-ns--reload-op "reload-clear") conn)) + (cider-nrepl-send-sync-request `("op" ,(cider-ns--reload-op "reload-clear")) conn)) (cider-nrepl-send-request - (thread-last - (map-merge 'list - `(,(cider-ns--reload-op (if all? "reload-all" "reload"))) - (cider--nrepl-print-request-map fill-column) - (when (and (not inhibit-refresh-fns) cider-ns-refresh-before-fn) - `(("before" ,cider-ns-refresh-before-fn))) - (when (and (not inhibit-refresh-fns) cider-ns-refresh-after-fn) - `(("after" ,cider-ns-refresh-after-fn)))) - (seq-mapcat #'identity)) + `("op" ,(cider-ns--reload-op (if all? "reload-all" "reload")) + ,@(cider--nrepl-print-request-plist fill-column) + ,@(when (and (not inhibit-refresh-fns) cider-ns-refresh-before-fn) + `("before" ,cider-ns-refresh-before-fn)) + ,@(when (and (not inhibit-refresh-fns) cider-ns-refresh-after-fn) + `("after" ,cider-ns-refresh-after-fn))) (lambda (response) (cider-ns-refresh--handle-response response log-buffer)) conn)))))) diff --git a/cider-overlays.el b/cider-overlays.el index 81c36e29e..8d0546606 100644 --- a/cider-overlays.el +++ b/cider-overlays.el @@ -1,6 +1,6 @@ ;;; cider-overlays.el --- Managing CIDER overlays -*- lexical-binding: t; -*- -;; Copyright © 2015-2024 Bozhidar Batsov, Artur Malabarba and CIDER contributors +;; Copyright © 2015-2025 Bozhidar Batsov, Artur Malabarba and CIDER contributors ;; Author: Artur Malabarba @@ -232,6 +232,8 @@ overlay." ;; Specify `default' face, otherwise unformatted text will ;; inherit the face of the following text. (display-string (format (propertize format 'face 'default) value)) + ;; Maximum value width at which we truncate it. + (truncation-threshold (* 3 (window-width))) (o nil)) ;; Remove any overlay at the position we're creating a new one, if it ;; exists. @@ -245,17 +247,22 @@ overlay." ;; If the display spans multiple lines or is very long, display it at ;; the beginning of the next line. (when (or (string-match "\n." display-string) + ;; string-width can be very slow on large results, so check + ;; with a cheaper predicate first. Conservatively limit to + ;; truncation threshold. + (> (length display-string) truncation-threshold) (> (string-width display-string) (- (window-width) (current-column)))) (setq display-string (concat " \n" display-string))) - ;; Put the cursor property only once we're done manipulating the - ;; string, since we want it to be at the first char. - (put-text-property 0 1 'cursor 0 display-string) - (when (> (string-width display-string) (* 3 (window-width))) + (when (or (> (length display-string) truncation-threshold) + (> (string-width display-string) truncation-threshold)) (setq display-string - (concat (substring display-string 0 (* 3 (window-width))) + (concat (substring display-string 0 truncation-threshold) (substitute-command-keys "...\nResult truncated. Type `\\[cider-inspect-last-result]' to inspect it.")))) + ;; Put the cursor property only once we're done manipulating the + ;; string, since we want it to be at the first char. + (put-text-property 0 1 'cursor 0 display-string) ;; Create the result overlay. (setq o (apply #'cider--make-overlay beg end type @@ -285,7 +292,7 @@ overlay." (when (and (<= (window-start win) (point) (window-end win)) ;; Right edge is visible. This is a little conservative ;; if the overlay contains line breaks. - (or (< (+ (current-column) (string-width value)) + (or (< (+ (current-column) (string-width display-string)) (window-width win)) (not truncate-lines))) o))))))) @@ -317,7 +324,7 @@ focused." :where point :duration cider-eval-result-duration :prepend-face (or overlay-face 'cider-result-overlay-face)))) - (msg (format "%s%s" cider-eval-result-prefix value)) + (msg (format "%s%s" cider-eval-result-prefix font-value)) (max-msg-length (* (floor (* (frame-height) max-mini-window-height)) (frame-width))) (msg (if (> (string-width msg) max-msg-length) diff --git a/cider-popup.el b/cider-popup.el index 0f0e43e0b..316b6a2b3 100644 --- a/cider-popup.el +++ b/cider-popup.el @@ -1,6 +1,6 @@ ;;; cider-popup.el --- Creating and quitting popup buffers -*- lexical-binding: t; -*- -;; Copyright © 2015-2024 Bozhidar Batsov, Artur Malabarba and CIDER contributors +;; Copyright © 2015-2025 Bozhidar Batsov, Artur Malabarba and CIDER contributors ;; Author: Artur Malabarba diff --git a/cider-profile.el b/cider-profile.el index 792d4b54e..da22b0075 100644 --- a/cider-profile.el +++ b/cider-profile.el @@ -1,6 +1,6 @@ ;;; cider-profile.el --- CIDER support for profiling -*- lexical-binding: t; -*- -;; Copyright © 2014-2024 Edwin Watkeys and CIDER contributors +;; Copyright © 2014-2025 Edwin Watkeys and CIDER contributors ;; Author: Edwin Watkeys ;; Juan E. Maya @@ -28,8 +28,7 @@ (require 'cider-client) (require 'cider-popup) (require 'cider-eval) - -(defconst cider-profile-buffer "*cider-profile*") +(require 'cider-inspector) (defvar cider-profile-map (let ((map (define-prefix-command 'cider-profile-map))) @@ -37,16 +36,10 @@ (define-key map (kbd "C-t") #'cider-profile-toggle) (define-key map (kbd "c") #'cider-profile-clear) (define-key map (kbd "C-c") #'cider-profile-clear) - (define-key map (kbd "S") #'cider-profile-summary) - (define-key map (kbd "C-S") #'cider-profile-summary) - (define-key map (kbd "s") #'cider-profile-var-summary) - (define-key map (kbd "C-s") #'cider-profile-var-summary) + (define-key map (kbd "s") #'cider-profile-summary) + (define-key map (kbd "C-s") #'cider-profile-summary) (define-key map (kbd "n") #'cider-profile-ns-toggle) (define-key map (kbd "C-n") #'cider-profile-ns-toggle) - (define-key map (kbd "v") #'cider-profile-var-profiled-p) - (define-key map (kbd "C-v") #'cider-profile-var-profiled-p) - (define-key map (kbd "+") #'cider-profile-samples) - (define-key map (kbd "C-+") #'cider-profile-samples) map) "CIDER profiler keymap.") @@ -55,9 +48,6 @@ ["Toggle var profiling" cider-profile-toggle] ["Toggle namespace profiling" cider-profile-ns-toggle] "--" - ["Display var profiling status" cider-profile-var-profiled-p] - ["Display max sample count" cider-profile-samples] - ["Display var summary" cider-profile-var-summary] ["Display summary" cider-profile-summary] ["Clear data" cider-profile-clear]) "CIDER profiling submenu.") @@ -69,52 +59,6 @@ Optional argument BUFFER defaults to current buffer." (nrepl-make-response-handler (or buffer (current-buffer)) handler nil nil nil)) -;;;###autoload -(defun cider-profile-samples (&optional query) - "Displays current max-sample-count. -If optional QUERY is specified, set max-sample-count and display new value." - (interactive "P") - (cider-ensure-op-supported "set-max-samples") - (cider-ensure-op-supported "get-max-samples") - (if (not (null query)) - (cider-nrepl-send-request - (let ((max-samples (if (numberp query) query '()))) - (message "query: %s" max-samples) - `("op" "set-max-samples" "max-samples" ,max-samples)) - (cider-profile--make-response-handler - (lambda (_buffer value) - (let ((value (if (zerop (length value)) "unlimited" value))) - (message "max-sample-count is now %s" value))))) - (cider-nrepl-send-request - '("op" "get-max-samples") - (cider-profile--make-response-handler - (lambda (_buffer value) - (let ((value (if (zerop (length value)) "unlimited" value))) - (message "max-sample-count is now %s" value)))))) - query) - -;;;###autoload -(defun cider-profile-var-profiled-p (query) - "Displays the profiling status of var under point. -Prompts for var if none under point or QUERY is present." - (interactive "P") - (cider-ensure-op-supported "is-var-profiled") - (cider-read-symbol-name - "Report profiling status for var: " - (lambda (sym) - (let ((ns (cider-current-ns))) - (cider-nrepl-send-request - `("op" "is-var-profiled" - "ns" ,ns - "sym" ,sym) - (cider-profile--make-response-handler - (lambda (_buffer value) - (pcase value - ("profiled" (message "Profiling is currently enabled for %s/%s" ns sym)) - ("unprofiled" (message "Profiling is currently disabled for %s/%s" ns sym)) - ("unbound" (message "%s/%s is unbound" ns sym))))))))) - query) - ;;;###autoload (defun cider-profile-ns-toggle (&optional query) "Toggle profiling for the ns associated with optional QUERY. @@ -122,13 +66,13 @@ Prompts for var if none under point or QUERY is present." If optional argument QUERY is non-nil, prompt for ns. Otherwise use current ns." (interactive "P") - (cider-ensure-op-supported "toggle-profile-ns") + (cider-ensure-op-supported "cider/profile-toggle-ns") (let ((ns (if query (completing-read "Toggle profiling for ns: " (cider-sync-request:ns-list)) (cider-current-ns)))) (cider-nrepl-send-request - `("op" "toggle-profile-ns" + `("op" "cider/profile-toggle-ns" "ns" ,ns) (cider-profile--make-response-handler (lambda (_buffer value) @@ -143,69 +87,42 @@ current ns." Defaults to the symbol at point. With prefix arg or no symbol at point, prompts for a var." (interactive "P") - (cider-ensure-op-supported "toggle-profile") + (cider-ensure-op-supported "cider/profile-toggle-var") (cider-read-symbol-name "Toggle profiling for var: " (lambda (sym) (let ((ns (cider-current-ns))) (cider-nrepl-send-request - `("op" "toggle-profile" + `("op" "cider/profile-toggle-var" "ns" ,ns "sym" ,sym) (cider-profile--make-response-handler (lambda (_buffer value) (pcase value ("profiled" (message "Profiling enabled for %s/%s" ns sym)) - ("unprofiled" (message "Profiling disabled for %s/%s" ns sym)) - ("unbound" (message "%s/%s is unbound" ns sym))))))))) + ("unprofiled" (message "Profiling disabled for %s/%s" ns sym))))))))) query) -(defun cider-profile-display-stats (stats-response) - "Displays the STATS-RESPONSE on `cider-profile-buffer`." - (let ((table (nrepl-dict-get stats-response "err"))) - (if cider-profile-buffer - (let ((buffer (cider-make-popup-buffer cider-profile-buffer))) - (with-current-buffer buffer - (let ((inhibit-read-only t)) (insert table))) - (display-buffer buffer) - (let ((window (get-buffer-window buffer))) - (set-window-point window 0) - (select-window window) - (fit-window-to-buffer window))) - (cider-emit-interactive-eval-err-output table)))) +(defun cider-profile--send-to-inspector (summary-response) + "Displays SUMMARY-RESPONSE using the inspector." + (let ((value (nrepl-dict-get summary-response "value"))) + (cider-inspector--render-value value))) ;;;###autoload (defun cider-profile-summary () "Display a summary of currently collected profile data." (interactive) - (cider-ensure-op-supported "profile-summary") - (cider-profile-display-stats - (cider-nrepl-send-sync-request '("op" "profile-summary")))) - -;;;###autoload -(defun cider-profile-var-summary (query) - "Display profile data for var under point QUERY. -Defaults to the symbol at point. With prefix arg or no symbol at point, -prompts for a var." - (interactive "P") - (cider-ensure-op-supported "profile-var-summary") - (cider-read-symbol-name - "Profile-summary for var: " - (lambda (sym) - (cider-profile-display-stats - (cider-nrepl-send-sync-request - `("op" "profile-var-summary" - "ns" ,(cider-current-ns) - "sym" ,sym))))) - query) + (cider-ensure-op-supported "cider/profile-summary") + (cider-inspector--render-value + (cider-nrepl-send-sync-request '("op" "cider/profile-summary")))) ;;;###autoload (defun cider-profile-clear () "Clear any collected profile data." (interactive) - (cider-ensure-op-supported "clear-profile") + (cider-ensure-op-supported "cider/profile-clear") (cider-nrepl-send-request - '("op" "clear-profile") + '("op" "cider/profile-clear") (cider-profile--make-response-handler (lambda (_buffer value) (when (equal value "cleared") diff --git a/cider-repl-history.el b/cider-repl-history.el index ec19298ad..618cadb62 100644 --- a/cider-repl-history.el +++ b/cider-repl-history.el @@ -1,6 +1,6 @@ ;;; cider-repl-history.el --- REPL input history browser -*- lexical-binding: t; -*- -;; Copyright (c) 2017-2024 John Valente and browse-kill-ring authors +;; Copyright (c) 2017-2025 John Valente and browse-kill-ring authors ;; This program is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by @@ -220,7 +220,6 @@ call `cider-repl-history' again.") (defvar cider-repl-history-previous-overlay nil "Previous overlay within *cider-repl-history* buffer.") - (defun cider-repl-history-get-history () "Function to retrieve history from the REPL buffer." (if cider-repl-history-repl-buffer @@ -576,6 +575,16 @@ text from the *cider-repl-history* buffer." (with-current-buffer cider-repl-history-repl-buffer (undo))) +(defun cider-repl-history-delete-entry-at-point () + "Delete history entry (at point)." + (interactive) + (let* ((orig (point)) + (str (cider-repl-history-current-string orig))) + (with-current-buffer cider-repl-history-repl-buffer + (delete str cider-repl-input-history)) + (cider-repl-history-update) + (goto-char orig))) + (defun cider-repl-history-setup (repl-win repl-buf history-buf &optional regexp) "Setup. REPL-WIN and REPL-BUF are where to insert commands; @@ -637,16 +646,17 @@ HISTORY-BUF is the history, and optional arg REGEXP is a filter." #'cider-repl-history-update-highlighted-entry nil t)) (message - (let ((entry (if (= 1 (length cider-command-history)) - "entry" - "entries"))) + (let* ((history-length (length cider-command-history)) + (entry (if (= 1 history-length) + "entry" + "entries"))) (concat (if (and (not regexp) cider-repl-history-display-duplicates) (format "%s %s in the command history." - (length cider-command-history) entry) + history-length entry) (format "%s (of %s) %s in the command history shown." - (length items) (length cider-command-history) entry)) + (length items) history-length entry)) (substitute-command-keys (concat " Type \\[cider-repl-history-quit] to quit. " "\\[describe-mode] for help."))))) @@ -693,6 +703,7 @@ HISTORY-BUF is the history, and optional arg REGEXP is a filter." (define-key map (kbd "g") #'cider-repl-history-update) (define-key map (kbd "q") #'cider-repl-history-quit) (define-key map (kbd "U") #'cider-repl-history-undo-other-window) + (define-key map (kbd "D") #'cider-repl-history-delete-entry-at-point) (define-key map (kbd "?") #'describe-mode) (define-key map (kbd "h") #'describe-mode) map)) diff --git a/cider-repl.el b/cider-repl.el index 675db2e28..12681f34c 100644 --- a/cider-repl.el +++ b/cider-repl.el @@ -1,7 +1,7 @@ ;;; cider-repl.el --- CIDER REPL mode interactions -*- lexical-binding: t -*- -;; Copyright © 2012-2024 Tim King, Phil Hagelberg, Bozhidar Batsov -;; Copyright © 2013-2024 Bozhidar Batsov, Artur Malabarba and CIDER contributors +;; Copyright © 2012-2025 Tim King, Phil Hagelberg, Bozhidar Batsov +;; Copyright © 2013-2025 Bozhidar Batsov, Artur Malabarba and CIDER contributors ;; ;; Author: Tim King ;; Phil Hagelberg @@ -95,8 +95,6 @@ focused. Otherwise the buffer is displayed and focused." "Controls whether the REPL buffer is displayed in the current window." :type 'boolean) -(make-obsolete-variable 'cider-repl-scroll-on-output 'scroll-conservatively "0.21") - (defcustom cider-repl-use-pretty-printing t "Control whether results in the REPL are pretty-printed or not. The REPL will use the printer specified in `cider-print-fn'. @@ -104,8 +102,6 @@ The `cider-toggle-pretty-printing' command can be used to interactively change the setting's value." :type 'boolean) -(make-obsolete-variable 'cider-repl-pretty-print-width 'cider-print-options "0.21") - (defcustom cider-repl-use-content-types nil "Control whether REPL results are presented using content-type information. The `cider-repl-toggle-content-types' command can be used to interactively @@ -149,9 +145,6 @@ you'd like to use the default Emacs behavior use `indent-for-tab-command'." :type 'symbol) -(make-obsolete-variable 'cider-repl-print-length 'cider-print-options "0.21") -(make-obsolete-variable 'cider-repl-print-level 'cider-print-options "0.21") - (defvar cider-repl-require-repl-utils-code '((clj . "(when-let [requires (resolve 'clojure.main/repl-requires)] (clojure.core/apply clojure.core/require @requires))") @@ -194,12 +187,6 @@ CIDER 1.7." This property value must be unique to avoid having adjacent inputs be joined together.") -(defvar-local cider-repl-input-history '() - "History list of strings read from the REPL buffer.") - -(defvar-local cider-repl-input-history-items-added 0 - "Variable counting the items added in the current session.") - (defvar-local cider-repl-output-start nil "Marker for the start of output. Currently its only purpose is to facilitate `cider-repl-clear-buffer'.") @@ -273,7 +260,7 @@ This cache is stored in the connection buffer.") (let* ((current-repl (cider-current-repl nil 'ensure)) (require-code (cdr (assoc (cider-repl-type current-repl) cider-repl-require-repl-utils-code)))) (nrepl-send-sync-request - (lax-plist-put + (cider-plist-put (nrepl--eval-request require-code (cider-current-ns)) "inhibit-cider-middleware" "true") current-repl))) @@ -296,9 +283,8 @@ Run CALLBACK once the evaluation is complete." "Evaluate `cider-repl-init-code' in the current REPL. Run CALLBACK once the evaluation is complete." (interactive) - (let* ((request (map-merge 'hash-table - (cider--repl-request-map fill-column) - '(("inhibit-cider-middleware" "true"))))) + (let* ((request `(,@(cider--repl-request-plist) + "inhibit-cider-middleware" "true"))) (cider-nrepl-request:eval ;; Ensure we evaluate _something_ so the initial namespace is correctly set (thread-first (or cider-repl-init-code '("nil")) @@ -307,10 +293,7 @@ Run CALLBACK once the evaluation is complete." nil (line-number-at-pos (point)) (cider-column-number-at-pos (point)) - (thread-last - request - (map-pairs) - (seq-mapcat #'identity))))) + request))) (defun cider-repl-init (buffer &optional callback) "Initialize the REPL in BUFFER. @@ -677,7 +660,7 @@ the input stream may block the whole colorization process." "Return non-nil if NS-FORM for CONNECTION changed since last eval." (when-let* ((ns (cider-ns-from-form ns-form))) (not (string= ns-form - (lax-plist-get + (cider-plist-get (buffer-local-value 'cider-repl--ns-forms-plist connection) ns))))) @@ -696,7 +679,7 @@ the input stream may block the whole colorization process." (when-let* ((ns (cider-ns-from-form ns-form))) ;; cache ns-form (setq cider-repl--ns-forms-plist - (lax-plist-put cider-repl--ns-forms-plist ns ns-form)) + (cider-plist-put cider-repl--ns-forms-plist ns ns-form)) ;; cache ns roots regexp (when (string-match "\\([^.]+\\)" ns) (let ((root (match-string-no-properties 1 ns))) @@ -1089,15 +1072,13 @@ and responding to them.") (lambda (buffer warning) (cider-repl-emit-stderr buffer warning))))) -(defun cider--repl-request-map (right-margin) - "Map to be merged into REPL eval requests. -RIGHT-MARGIN is as in `cider--nrepl-print-request-map'." - (map-merge 'hash-table - (cider--nrepl-print-request-map right-margin) - (unless cider-repl-use-pretty-printing - '(("nrepl.middleware.print/print" "cider.nrepl.pprint/pr"))) - (when cider-repl-use-content-types - (cider--nrepl-content-type-map)))) +(defun cider--repl-request-plist () + "Plist to be merged into REPL eval requests." + `(,@(cider--nrepl-print-request-plist fill-column) + ,@(unless cider-repl-use-pretty-printing + `("nrepl.middleware.print/print" "cider.nrepl.pprint/pr")) + ,@(when cider-repl-use-content-types + `("content-type" "true")))) (defun cider-repl--send-input (&optional newline) "Go to the end of the input and send the current input. @@ -1138,10 +1119,7 @@ If NEWLINE is true then add a newline at the end of the input." (cider-current-ns) (line-number-at-pos input-start) (cider-column-number-at-pos input-start) - (thread-last - (cider--repl-request-map fill-column) - (map-pairs) - (seq-mapcat #'identity))))))) + (cider--repl-request-plist)))))) (defun cider-repl-return (&optional end-of-input) "Evaluate the current input string, or insert a newline. @@ -1468,13 +1446,15 @@ WIN, BUFFER and POS are the window, buffer and point under mouse position." (defvar cider-repl-history-pattern nil "The regexp most recently used for finding input history.") +(defvar cider-repl-input-history '() + "History list of strings read from the REPL buffer.") + (defun cider-repl--add-to-input-history (string) "Add STRING to the input history. Empty strings and duplicates are ignored." (unless (or (equal string "") (equal string (car cider-repl-input-history))) - (push string cider-repl-input-history) - (cl-incf cider-repl-input-history-items-added))) + (push string cider-repl-input-history))) (defun cider-repl-delete-current-input () "Delete all text after the prompt." @@ -1593,9 +1573,11 @@ If USE-CURRENT-INPUT is non-nil, use the current input." :safe #'integerp) (defcustom cider-repl-history-file nil - "File to save the persistent REPL history to." - :type 'string - :safe #'stringp) + "File to save the persistent REPL history to. +If this is set to a path the history will be global to all projects. If this is +set to `per-project', the history will be stored in a file (.cider-history) at +the root of each project." + :type '(choice string symbol)) (defun cider-repl--history-read-filename () "Ask the user which file to use, defaulting `cider-repl-history-file'." @@ -1612,6 +1594,12 @@ It does not yet set the input history." (read (current-buffer)))) '())) +(defun cider-repl--find-dir-for-history () + "Find the first suitable directory to store the project's history." + (seq-find + (lambda (dir) (and (not (null dir)) (not (tramp-tramp-file-p dir)))) + (list nrepl-project-dir (clojure-project-dir) default-directory))) + (defun cider-repl-history-load (&optional filename) "Load history from FILENAME into current session. FILENAME defaults to the value of `cider-repl-history-file' but user @@ -1619,21 +1607,35 @@ defined filenames can be used to read special history files. The value of `cider-repl-input-history' is set by this function." (interactive (list (cider-repl--history-read-filename))) - (let ((f (or filename cider-repl-history-file))) - ;; TODO: probably need to set cider-repl-input-history-position as well. - ;; in a fresh connection the newest item in the list is currently - ;; not available. After sending one input, everything seems to work. - (setq cider-repl-input-history (cider-repl--history-read f)))) + (cond + (filename (setq cider-repl-history-file filename)) + ((equal 'per-project cider-repl-history-file) + (make-local-variable 'cider-repl-input-history) + (when-let ((dir (cider-repl--find-dir-for-history))) + (setq-local + cider-repl-history-file (expand-file-name ".cider-history" dir))))) + (when cider-repl-history-file + (condition-case nil + ;; TODO: probably need to set cider-repl-input-history-position as + ;; well. In a fresh connection the newest item in the list is + ;; currently not available. After sending one input, everything + ;; seems to work. + (setq + cider-repl-input-history + (cider-repl--history-read cider-repl-history-file)) + (error + (message + "Malformed cider-repl-history-file: %s" cider-repl-history-file))) + (add-hook 'kill-buffer-hook #'cider-repl-history-just-save t t) + (add-hook 'kill-emacs-hook #'cider-repl-history-save-all))) (defun cider-repl--history-write (filename) "Write history to FILENAME. Currently coding system for writing the contents is hardwired to utf-8-unix." - (let* ((mhist (cider-repl--histories-merge cider-repl-input-history - cider-repl-input-history-items-added - (cider-repl--history-read filename))) + (let* ((end (min (length cider-repl-input-history) cider-repl-history-size)) ;; newest items are at the beginning of the list, thus 0 - (hist (cl-subseq mhist 0 (min (length mhist) cider-repl-history-size)))) + (hist (cl-subseq cider-repl-input-history 0 end))) (unless (file-writable-p filename) (error (format "History file not writable: %s" filename))) (let ((print-length nil) (print-level nil)) @@ -1658,15 +1660,12 @@ This function is meant to be used in hooks to avoid lambda constructs." (cider-repl-history-save cider-repl-history-file)) -;; SLIME has different semantics and will not save any duplicates. -;; we keep track of how many items were added to the history in the -;; current session in `cider-repl--add-to-input-history' and merge only the -;; new items with the current history found in the file, which may -;; have been changed in the meantime by another session. -(defun cider-repl--histories-merge (session-hist n-added-items file-hist) - "Merge histories from SESSION-HIST adding N-ADDED-ITEMS into FILE-HIST." - (append (cl-subseq session-hist 0 n-added-items) - file-hist)) +(defun cider-repl-history-save-all () + "Save all histories." + (dolist (buffer (buffer-list)) + (with-current-buffer buffer + (when (equal major-mode 'cider-repl-mode) + (cider-repl-history-just-save))))) ;;; REPL shortcuts @@ -1692,7 +1691,6 @@ constructs." (declare-function cider-version "cider") (declare-function cider-test-run-loaded-tests "cider-test") (declare-function cider-test-run-project-tests "cider-test") -(declare-function cider-sideloader-start "cider-eval") (cider-repl-add-shortcut "clear-output" #'cider-repl-clear-output) (cider-repl-add-shortcut "clear" #'cider-repl-clear-buffer) (cider-repl-add-shortcut "clear-banners" #'cider-repl-clear-banners) @@ -1706,7 +1704,6 @@ constructs." (cider-repl-add-shortcut "classpath" #'cider-classpath) (cider-repl-add-shortcut "history" #'cider-repl-history) (cider-repl-add-shortcut "trace-ns" #'cider-toggle-trace-ns) -(cider-repl-add-shortcut "sideloader-start" #'cider-sideloader-start) (cider-repl-add-shortcut "undef" #'cider-undef) (cider-repl-add-shortcut "refresh" #'cider-ns-refresh) (cider-repl-add-shortcut "reload" #'cider-ns-reload) @@ -2053,13 +2050,7 @@ in an unexpected place." (setq-local prettify-symbols-alist clojure--prettify-symbols-alist) ;; apply dir-local variables to REPL buffers (hack-dir-local-variables-non-file-buffer) - (when cider-repl-history-file - (condition-case nil - (cider-repl-history-load cider-repl-history-file) - (error - (message "Malformed cider-repl-history-file: %s" cider-repl-history-file))) - (add-hook 'kill-buffer-hook #'cider-repl-history-just-save t t) - (add-hook 'kill-emacs-hook #'cider-repl-history-just-save)) + (cider-repl-history-load) (add-hook 'completion-at-point-functions #'cider-complete-at-point nil t) (add-hook 'paredit-mode-hook (lambda () (clojure-paredit-setup cider-repl-mode-map))) (cider-repl-setup-paredit)) diff --git a/cider-resolve.el b/cider-resolve.el index eaa49d630..421242078 100644 --- a/cider-resolve.el +++ b/cider-resolve.el @@ -1,6 +1,6 @@ ;;; cider-resolve.el --- Resolve clojure symbols according to current nREPL connection -*- lexical-binding: t; -*- -;; Copyright © 2015-2024 Bozhidar Batsov, Artur Malabarba and CIDER contributors +;; Copyright © 2015-2025 Bozhidar Batsov, Artur Malabarba and CIDER contributors ;; Author: Artur Malabarba diff --git a/cider-scratch.el b/cider-scratch.el index 07de719c6..d82699ba9 100644 --- a/cider-scratch.el +++ b/cider-scratch.el @@ -1,6 +1,6 @@ ;;; cider-scratch.el --- *scratch* buffer for Clojure -*- lexical-binding: t -*- -;; Copyright © 2014-2024 Bozhidar Batsov and CIDER contributors +;; Copyright © 2014-2025 Bozhidar Batsov and CIDER contributors ;; ;; Author: Tim King ;; Phil Hagelberg diff --git a/cider-selector.el b/cider-selector.el index ec4a6503d..1a34e0f56 100644 --- a/cider-selector.el +++ b/cider-selector.el @@ -1,7 +1,7 @@ ;;; cider-selector.el --- Buffer selection command inspired by SLIME's selector -*- lexical-binding: t -*- -;; Copyright © 2012-2024 Tim King, Phil Hagelberg, Bozhidar Batsov -;; Copyright © 2013-2024 Bozhidar Batsov, Artur Malabarba and CIDER contributors +;; Copyright © 2012-2025 Tim King, Phil Hagelberg, Bozhidar Batsov +;; Copyright © 2013-2025 Bozhidar Batsov, Artur Malabarba and CIDER contributors ;; ;; Author: Tim King ;; Phil Hagelberg @@ -34,7 +34,6 @@ (require 'cider-client) (require 'cider-eval) (require 'cider-scratch) -(require 'cider-profile) (defconst cider-selector-help-buffer "*CIDER Selector Help*" "The name of the selector's help buffer.") @@ -104,6 +103,7 @@ selects a buffer. BODY is a series of forms which are evaluated when the selector is chosen. The returned buffer is selected with `switch-to-buffer'." + (declare (indent 1)) (let ((method `(lambda () (let ((buffer (progn ,@body))) (cond ((not (and buffer (get-buffer buffer))) @@ -143,7 +143,8 @@ is chosen. The returned buffer is selected with "Most recently visited emacs-lisp-mode buffer." (cider-selector--recently-visited-buffer 'emacs-lisp-mode)) -(def-cider-selector-method ?q "Abort." +(def-cider-selector-method ?q + "Abort." (top-level)) (def-cider-selector-method ?r @@ -160,10 +161,6 @@ visited cider-repl-mode buffer." "*cider-error* buffer." cider-error-buffer) -(def-cider-selector-method ?p - "*cider-profile* buffer." - cider-profile-buffer) - (def-cider-selector-method ?d "*cider-doc* buffer." cider-doc-buffer) diff --git a/cider-stacktrace.el b/cider-stacktrace.el index 930193897..3ea309e2e 100644 --- a/cider-stacktrace.el +++ b/cider-stacktrace.el @@ -1,6 +1,6 @@ ;;; cider-stacktrace.el --- Stacktrace navigator -*- lexical-binding: t -*- -;; Copyright © 2014-2024 Jeff Valk, Bozhidar Batsov and CIDER contributors +;; Copyright © 2014-2025 Jeff Valk, Bozhidar Batsov and CIDER contributors ;; Author: Jeff Valk @@ -48,12 +48,15 @@ "Fill column for error messages in stacktrace display. If nil, messages will not be wrapped. If truthy but non-numeric, `fill-column' will be used." - :type 'list + :type '(radio + (integer :tag "Fill Column") + (const :tag "None" nil) + (const :tag "Use default fill-column" t)) :package-version '(cider . "0.7.0")) (defcustom cider-stacktrace-default-filters '(tooling dup) "Frame types to omit from initial stacktrace display." - :type 'list + :type '(repeat symbol) :package-version '(cider . "0.6.0")) (defcustom cider-stacktrace-navigate-to-other-window t @@ -62,10 +65,6 @@ Pick nil if you prefer the same window as *cider-error*." :type 'boolean :package-version '(cider . "1.8.0")) -(make-obsolete 'cider-stacktrace-print-length 'cider-stacktrace-print-options "0.20") -(make-obsolete 'cider-stacktrace-print-level 'cider-stacktrace-print-options "0.20") -(make-obsolete-variable 'cider-stacktrace-print-options 'cider-print-options "0.21") - (defvar cider-stacktrace-detail-max 2 "The maximum detail level for causes.") @@ -76,12 +75,10 @@ Pick nil if you prefer the same window as *cider-error*." (defconst cider-error-buffer "*cider-error*") -(make-obsolete 'cider-visit-error-buffer 'cider-selector "0.18") - (defcustom cider-stacktrace-suppressed-errors '() - "Errors that won't make the stacktrace buffer 'pop-over' your active window. + "Errors that won't make the stacktrace buffer pop over your active window. The error types are represented as strings." - :type 'list + :type '(list string) :package-version '(cider . "0.12.0")) ;; Faces @@ -209,7 +206,9 @@ The error types are represented as strings." (setq-local electric-indent-chars nil) (setq-local cider-stacktrace-hidden-frame-count 0) (setq-local cider-stacktrace-filters cider-stacktrace-default-filters) - (setq-local cider-stacktrace-cause-visibility (make-vector 10 0)) + ;; Expand all exception causes to "detail level 1" by default, meaning they + ;; will show the message and the data (but not the stacktrace). + (setq-local cider-stacktrace-cause-visibility (make-vector 10 1)) (buffer-disable-undo)) @@ -707,8 +706,7 @@ This associates text properties to enable filtering and source navigation." (put-text-property p1 p4 'font-lock-face 'cider-stacktrace-ns-face) (put-text-property p2 p3 'font-lock-face 'cider-stacktrace-fn-face) (put-text-property (line-beginning-position) (line-end-position) - 'cider-stacktrace-frame t))) - (insert "\n"))))))) + 'cider-stacktrace-frame t))))))))) (defun cider-stacktrace-render-compile-error (buffer cause) "Emit into BUFFER the compile error CAUSE, and enable jumping to it." @@ -801,34 +799,55 @@ the NAME. The whole group is prefixed by string INDENT." (declare-function cider-inspector-inspect-last-exception "cider-inspector") -(defun cider-stacktrace--inspect-class (event) - "Mouse handler for EVENT." +(defun cider-stacktrace--inspect-mouse (event &optional ex-data) + "Mouse handler for EVENT. +If EX-DATA is true, inspect ex-data of the exception instead." (interactive "e") (let* ((pos (posn-point (event-end event))) (window (posn-window (event-end event))) (buffer (window-buffer window)) (inspect-index (with-current-buffer buffer (get-text-property pos 'inspect-index)))) - (cider-inspector-inspect-last-exception inspect-index))) + (cider-inspector-inspect-last-exception inspect-index ex-data))) -(defun cider-stacktrace--inspect-class-kbd () - "Keyboard handler." +(defun cider-stacktrace--inspect-kbd (&optional ex-data) + "Keyboard handler. +If EX-DATA is true, inspect ex-data of the exception instead." (interactive) (when-let ((inspect-index (get-text-property (point) 'inspect-index))) - (cider-inspector-inspect-last-exception inspect-index))) + (cider-inspector-inspect-last-exception inspect-index ex-data))) + +(defun cider-stacktrace--inspect-ex-data-mouse (event) + "Mouse handler for EVENT." + (interactive "e") + (cider-stacktrace--inspect-mouse event t)) + +(defun cider-stacktrace--inspect-ex-data-kbd () + "Keyboard handler." + (interactive) + (cider-stacktrace--inspect-kbd t)) (defvar cider-stacktrace-exception-map (let ((map (make-sparse-keymap))) - (define-key map [mouse-1] #'cider-stacktrace--inspect-class) - (define-key map (kbd "p") #'cider-stacktrace--inspect-class-kbd) - (define-key map (kbd "i") #'cider-stacktrace--inspect-class-kbd) + (define-key map [mouse-1] #'cider-stacktrace--inspect-mouse) + (define-key map (kbd "p") #'cider-stacktrace--inspect-kbd) + (define-key map (kbd "i") #'cider-stacktrace--inspect-kbd) + (define-key map (kbd "RET") #'cider-stacktrace--inspect-kbd) + map)) + +(defvar cider-stacktrace-ex-data-map + (let ((map (make-sparse-keymap))) + (define-key map [mouse-1] #'cider-stacktrace--inspect-ex-data-mouse) + (define-key map (kbd "p") #'cider-stacktrace--inspect-ex-data-kbd) + (define-key map (kbd "i") #'cider-stacktrace--inspect-ex-data-kbd) + (define-key map (kbd "RET") #'cider-stacktrace--inspect-ex-data-kbd) map)) (defun cider-stacktrace-render-cause (buffer cause num note &optional inspect-index) "Emit into BUFFER the CAUSE NUM, exception class, message, data, and NOTE, make INSPECT-INDEX actionable if present." (with-current-buffer buffer - (nrepl-dbind-response cause (class message data spec stacktrace) + (nrepl-dbind-response cause (class message data spec triage stacktrace) (let ((indent " ") (class-face 'cider-stacktrace-error-class-face) (message-face 'cider-stacktrace-error-message-face)) @@ -854,18 +873,31 @@ make INSPECT-INDEX actionable if present." (propertize (or message "(No message)") 'font-lock-face message-face) indent t)) - (insert "\n") + (when triage + (insert "\n") + (cider-stacktrace-emit-indented + (propertize (string-trim triage) 'font-lock-face message-face) + indent nil)) (when spec + (insert "\n") (cider-stacktrace--emit-spec-problems spec (concat indent " "))) (when data - (cider-stacktrace-emit-indented data indent nil t))) + (insert "\n") + (cider-propertize-region `(inspect-index + ,inspect-index + keymap + ,cider-stacktrace-ex-data-map + mouse-face + highlight) + (cider-stacktrace-emit-indented data indent nil t))) + (insert "\n")) ;; Detail level 2: stacktrace (cider-propertize-region '(detail 2) - (insert "\n") (let ((beg (point)) (bg `(:background ,cider-stacktrace-frames-background-color :extend t))) (dolist (frame stacktrace) - (cider-stacktrace-render-frame buffer frame)) + (cider-stacktrace-render-frame buffer frame) + (insert "\n")) (overlay-put (make-overlay beg (point)) 'font-lock-face bg))) ;; Add line break between causes, even when collapsed. (cider-propertize-region '(detail 0) @@ -875,10 +907,6 @@ make INSPECT-INDEX actionable if present." "Set and apply CAUSES initial visibility, filters, and cursor position." (nrepl-dbind-response (car causes) (class) (let ((compile-error-p (equal class "clojure.lang.Compiler$CompilerException"))) - ;; Partially display outermost cause if it's a compiler exception (the - ;; description reports reader location of the error). - (when compile-error-p - (cider-stacktrace-cycle-cause (length causes) 1)) ;; Fully display innermost cause. This also applies visibility/filters. (cider-stacktrace-cycle-cause 1 cider-stacktrace-detail-max) ;; Move point (DWIM) to the compile error location if present, or to the @@ -927,53 +955,19 @@ through the `cider-stacktrace-suppressed-errors' variable." (cider-stacktrace-initialize causes) (font-lock-refresh-defaults))) -(defun cider-stacktrace--analyze-stacktrace-op (stacktrace) - "Return the Cider NREPL op to analyze STACKTRACE." - (list "op" "analyze-stacktrace" "stacktrace" stacktrace)) - -(defun cider-stacktrace--stacktrace-request (stacktrace) - "Return the Cider NREPL request to analyze STACKTRACE." - (thread-last - (map-merge 'list - (list (cider-stacktrace--analyze-stacktrace-op stacktrace)) - (cider--nrepl-print-request-map fill-column)) - (seq-mapcat #'identity))) - -(defun cider-stacktrace--analyze-render (causes) - "Render the CAUSES of the stacktrace analysis result." - (let ((buffer (get-buffer-create cider-error-buffer))) - (with-current-buffer buffer - (cider-stacktrace-mode) - (cider-stacktrace-render buffer (reverse causes)) - (display-buffer buffer cider-jump-to-pop-to-buffer-actions)))) - -(defun cider-stacktrace-analyze-string (stacktrace) - "Analyze the STACKTRACE string and show the result." - (when (stringp stacktrace) - (set-text-properties 0 (length stacktrace) nil stacktrace)) - (let (causes) - (cider-nrepl-send-request - (cider-stacktrace--stacktrace-request stacktrace) - (lambda (response) - (setq causes (nrepl-dbind-response response (class status) - (cond (class (cons response causes)) - ((and (member "done" status) causes) - (cider-stacktrace--analyze-render causes))))))))) - (defun cider-stacktrace-analyze-at-point () - "Analyze the stacktrace at point." + "Removed." + (interactive) + (message "This function has been removed. +You can jump to functions and methods directly from the printed stacktrace now.")) +(make-obsolete 'cider-stacktrace-analyze-at-point nil "1.18") + +(defun cider-stacktrace-analyze-in-region (&rest _) + "Removed." (interactive) - (cond ((thing-at-point 'sentence) - (cider-stacktrace-analyze-string (thing-at-point 'sentence))) - ((thing-at-point 'paragraph) - (cider-stacktrace-analyze-string (thing-at-point 'paragraph))) - (t (cider-stacktrace-analyze-in-region (region-beginning) (region-end))))) - -(defun cider-stacktrace-analyze-in-region (beg end) - "Analyze the stacktrace in the region between BEG and END." - (interactive (list (region-beginning) (region-end))) - (let ((stacktrace (buffer-substring beg end))) - (cider-stacktrace-analyze-string stacktrace))) + (message "This function has been removed. +You can jump to functions and methods directly from the printed stacktrace now.")) +(make-obsolete 'cider-stacktrace-analyze-in-region nil "1.18") (provide 'cider-stacktrace) diff --git a/cider-test.el b/cider-test.el index b67021cea..39cbdb154 100644 --- a/cider-test.el +++ b/cider-test.el @@ -1,6 +1,6 @@ ;;; cider-test.el --- Test result viewer -*- lexical-binding: t -*- -;; Copyright © 2014-2024 Jeff Valk, Bozhidar Batsov and CIDER contributors +;; Copyright © 2014-2025 Jeff Valk, Bozhidar Batsov and CIDER contributors ;; Author: Jeff Valk @@ -272,14 +272,11 @@ prompt and whether to use a new window. Similar to `cider-find-var'." "Display stacktrace for the erring NS VAR test with the assertion INDEX." (let (causes) (cider-nrepl-send-request - (thread-last - (map-merge 'list - `(("op" "test-stacktrace") - ("ns" ,ns) - ("var" ,var) - ("index" ,index)) - (cider--nrepl-print-request-map fill-column)) - (seq-mapcat #'identity)) + `("op" "test-stacktrace" + "ns" ,ns + "var" ,var + "index" ,index + ,@(cider--nrepl-print-request-plist fill-column)) (lambda (response) (nrepl-dbind-response response (class status) (cond (class (setq causes (cons response causes))) diff --git a/cider-tracing.el b/cider-tracing.el index f466fc8f9..bccaea407 100644 --- a/cider-tracing.el +++ b/cider-tracing.el @@ -1,6 +1,6 @@ ;;; cider-tracing.el --- Executing tracing functionality -*- lexical-binding: t -*- -;; Copyright © 2013-2024 Bozhidar Batsov, Artur Malabarba and CIDER contributors +;; Copyright © 2013-2025 Bozhidar Batsov, Artur Malabarba and CIDER contributors ;; ;; Author: Bozhidar Batsov ;; Artur Malabarba diff --git a/cider-util.el b/cider-util.el index 8316af962..779c64be7 100644 --- a/cider-util.el +++ b/cider-util.el @@ -1,7 +1,7 @@ ;; cider-util.el --- Common utility functions that don't belong anywhere else -*- lexical-binding: t -*- -;; Copyright © 2012-2024 Tim King, Phil Hagelberg, Bozhidar Batsov -;; Copyright © 2013-2024 Bozhidar Batsov, Artur Malabarba and CIDER contributors +;; Copyright © 2012-2025 Tim King, Phil Hagelberg, Bozhidar Batsov +;; Copyright © 2013-2025 Bozhidar Batsov, Artur Malabarba and CIDER contributors ;; ;; Author: Tim King ;; Phil Hagelberg @@ -69,6 +69,10 @@ Setting this to nil removes the fontification restriction." "Return non-nil if current buffer is managed by a ClojureC major mode." (derived-mode-p 'clojurec-mode 'clojure-ts-clojurec-mode)) +(defun cider-clojure-ts-mode-p () + "Return non-nil if current buffer is managed by a Clojure[TS] major mode." + (derived-mode-p 'clojure-ts-mode)) + (defun cider-util--clojure-buffers () "Return a list of all existing `clojure-mode' buffers." (seq-filter @@ -107,6 +111,18 @@ If BUFFER is provided act on that buffer instead." (with-current-buffer (or buffer (current-buffer)) (or (cider-clojurec-major-mode-p)))) +(defun cider-keyword-at-point-p (&optional point) + "Return non-nil if POINT is in a Clojure keyword. + +Take into consideration current major mode." + (let ((pos (or point (point)))) + (if (and (cider-clojure-ts-mode-p) + (fboundp 'clojure-ts--keyword-node-p) + (fboundp 'treesit-node-parent) + (fboundp 'treesit-node-at)) + (clojure-ts--keyword-node-p (treesit-node-parent (treesit-node-at pos))) + (member 'clojure-keyword-face (text-properties-at pos))))) + ;;; Thing at point @@ -235,6 +251,22 @@ Can only error if SKIP is non-nil." (forward-sexp 2) (cider-sexp-at-point)) (error nil))) + + +;;; Plists + +(defun cider-plist-get (plist prop) + "Extract PROP from PLIST using `equal'. + +An alternative `lax-plist-get' that got deprecated in Emacs 29." + (lax-plist-get plist prop)) + +(defun cider-plist-put (plist prop val) + "Change value in PLIST of PROP to VAL, comparing with `equal'. + +An alternative to `lax-plist-put' that got deprecated in Emacs 29." + (lax-plist-put plist prop val)) + ;;; Text properties diff --git a/cider-xref-backend.el b/cider-xref-backend.el index 4b3ca4b0d..0d6a4ca9c 100644 --- a/cider-xref-backend.el +++ b/cider-xref-backend.el @@ -1,6 +1,6 @@ ;;; cider-xref-backend.el --- CIDER's backend for Emacs' xref functionality -*- lexical-binding: t -*- -;; Copyright © 2013-2024 Bozhidar Batsov, Artur Malabarba and CIDER contributors +;; Copyright © 2013-2025 Bozhidar Batsov, Artur Malabarba and CIDER contributors ;; ;; Author: Bozhidar Batsov ;; Artur Malabarba diff --git a/cider-xref.el b/cider-xref.el index 453d1ef41..0d49073ac 100644 --- a/cider-xref.el +++ b/cider-xref.el @@ -1,6 +1,6 @@ ;;; cider-xref.el --- Xref functionality for Clojure -*- lexical-binding: t -*- -;; Copyright © 2019-2024 Bozhidar Batsov and CIDER contributors +;; Copyright © 2019-2025 Bozhidar Batsov and CIDER contributors ;; ;; Author: Bozhidar Batsov diff --git a/cider.el b/cider.el index d0ba96641..a80b9f9a5 100644 --- a/cider.el +++ b/cider.el @@ -1,7 +1,7 @@ ;;; cider.el --- Clojure Interactive Development Environment that Rocks -*- lexical-binding: t -*- -;; Copyright © 2012-2024 Tim King, Phil Hagelberg, Bozhidar Batsov -;; Copyright © 2013-2024 Bozhidar Batsov, Artur Malabarba and CIDER contributors +;; Copyright © 2012-2025 Tim King, Phil Hagelberg, Bozhidar Batsov +;; Copyright © 2013-2025 Bozhidar Batsov, Artur Malabarba and CIDER contributors ;; ;; Author: Tim King ;; Phil Hagelberg @@ -10,10 +10,20 @@ ;; Hugo Duncan ;; Steve Purcell ;; Maintainer: Bozhidar Batsov -;; URL: https://www.github.com/clojure-emacs/cider -;; Version: 1.15.0 -;; Package-Requires: ((emacs "26") (clojure-mode "5.19") (parseedn "1.2.1") (queue "0.2") (spinner "1.7") (seq "2.22") (sesman "0.3.2") (transient "0.4.1")) +;; +;; Homepage: https://www.github.com/clojure-emacs/cider ;; Keywords: languages, clojure, cider +;; +;; Version: 1.18.0 +;; Package-Requires: ( +;; (emacs "27") +;; (clojure-mode "5.19") +;; (parseedn "1.2.1") +;; (queue "0.2") +;; (spinner "1.7") +;; (seq "2.22") +;; (sesman "0.3.2") +;; (transient "0.4.1")) ;; This program is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by @@ -93,10 +103,10 @@ (require 'sesman) (require 'package) -(defconst cider-version "1.15.0" +(defconst cider-version "1.18.0" "The current version of CIDER.") -(defconst cider-codename "Cogne" +(defconst cider-codename "Athens" "Codename used to denote stable releases.") (defcustom cider-lein-command @@ -289,7 +299,9 @@ By default we favor the project-specific shadow-cljs over the system-wide." :package-version '(cider . "1.14.0")) (make-obsolete-variable 'cider-lein-global-options 'cider-lein-parameters "1.8.0") -(make-obsolete-variable 'cider-boot-global-options 'cider-boot-parameters "1.8.0") +(make-obsolete-variable 'cider-boot-command nil "1.8.0") +(make-obsolete-variable 'cider-boot-parameters nil "1.8.0") +(make-obsolete-variable 'cider-boot-global-options nil "1.8.0") (make-obsolete-variable 'cider-clojure-cli-global-options 'cider-clojure-cli-parameters "1.8.0") (make-obsolete-variable 'cider-shadow-cljs-global-options 'cider-shadow-cljs-parameters "1.8.0") (make-obsolete-variable 'cider-gradle-global-options 'cider-gradle-parameters "1.8.0") @@ -300,7 +312,7 @@ By default we favor the project-specific shadow-cljs over the system-wide." (if (executable-find "clojure") 'clojure-cli 'lein) "The default tool to use when doing `cider-jack-in' outside a project. This value will only be consulted when no identifying file types, i.e. -project.clj for leiningen or build.boot for boot, could be found. +project.clj for leiningen or deps.edn for clojure-cli, could be found. As the Clojure CLI is bundled with Clojure itself, it's the default. In the absence of the Clojure CLI (e.g. on Windows), we fallback @@ -320,7 +332,7 @@ to Leiningen." nil "Allow choosing a build system when there are many. When there are project markers from multiple build systems (e.g. lein and -boot) the user is prompted to select one of them. When non-nil, this +clojure-cli) the user is prompted to select one of them. When non-nil, this variable will suppress this behavior and will select whatever build system is indicated by the variable if present. Note, this is only when CIDER cannot decide which of many build systems to use and will never override a @@ -432,7 +444,6 @@ The plist supports the following keys "Determine the command `cider-jack-in' needs to invoke for the PROJECT-TYPE." (pcase project-type ('lein cider-lein-command) - ('boot cider-boot-command) ('clojure-cli cider-clojure-cli-command) ('babashka cider-babashka-command) ('shadow-cljs cider-shadow-cljs-command) @@ -477,7 +488,6 @@ Throws an error if PROJECT-TYPE is unknown." " " r) r))) - ('boot (cider--resolve-command cider-boot-command)) ('clojure-cli (if (and cider-enrich-classpath (not (eq system-type 'windows-nt)) (executable-find (cider--get-enrich-classpath-clojure-cli-script))) @@ -510,7 +520,6 @@ Throws an error if PROJECT-TYPE is unknown." "Determine the command line options for `cider-jack-in' for the PROJECT-TYPE." (pcase project-type ('lein cider-lein-global-options) - ('boot cider-boot-global-options) ('clojure-cli cider-clojure-cli-global-options) ('babashka cider-babashka-global-options) ('shadow-cljs cider-shadow-cljs-global-options) @@ -527,7 +536,6 @@ Throws an error if PROJECT-TYPE is unknown." ;; Please be careful when changing them. (pcase project-type ('lein cider-lein-parameters) - ('boot cider-boot-parameters) ('clojure-cli cider-clojure-cli-parameters) ('babashka cider-babashka-parameters) ('shadow-cljs cider-shadow-cljs-parameters) @@ -542,10 +550,10 @@ Throws an error if PROJECT-TYPE is unknown." "List of dependencies where elements are lists of artifact name and version.") (put 'cider-jack-in-dependencies 'risky-local-variable t) -(defcustom cider-injected-nrepl-version "1.2.0-beta2" +(defcustom cider-injected-nrepl-version "1.3.1" "The version of nREPL injected on jack-in. We inject the newest known version of nREPL just in case -your version of Boot or Leiningen is bundling an older one." +your version of Leiningen is bundling an older one." :type 'string :package-version '(cider . "1.2.0") :safe #'stringp) @@ -573,7 +581,7 @@ the artifact.") Used when `cider-jack-in-auto-inject-clojure' is set to `latest'.") -(defconst cider-required-middleware-version "0.49.0" +(defconst cider-required-middleware-version "0.55.7" "The CIDER nREPL version that's known to work properly with CIDER.") (defcustom cider-injected-middleware-version cider-required-middleware-version @@ -692,12 +700,6 @@ returned by this function only contains strings." (car spec) spec))))) -(defun cider--list-as-boot-artifact (list) - "Return a boot artifact string described by the elements of LIST. -LIST should have the form (ARTIFACT-NAME ARTIFACT-VERSION). The returned -string is quoted for passing as argument to an inferior shell." - (concat "-d " (shell-quote-argument (format "%s:%s" (car list) (cadr list))))) - (defun cider--jack-in-required-dependencies () "Returns the required CIDER deps. They are normally added to `cider-jack-in-dependencies', @@ -705,32 +707,6 @@ unless it's a Lein project." `(("nrepl/nrepl" ,cider-injected-nrepl-version) ("cider/cider-nrepl" ,cider-injected-middleware-version))) -(defun cider-boot-dependencies (dependencies) - "Return a list of boot artifact strings created from DEPENDENCIES." - (concat (mapconcat #'cider--list-as-boot-artifact dependencies " ") - (unless (seq-empty-p dependencies) " "))) - -(defun cider-boot-middleware-task (params middlewares) - "Create a command to add MIDDLEWARES with corresponding PARAMS." - (concat "cider.tasks/add-middleware " - (mapconcat (lambda (middleware) - (format "-m %s" (shell-quote-argument middleware))) - middlewares - " ") - " " params)) - -(defun cider-boot-jack-in-dependencies (global-opts params dependencies middlewares) - "Create boot jack-in dependencies. -Does so by concatenating GLOBAL-OPTS, DEPENDENCIES, -and MIDDLEWARES. PARAMS and MIDDLEWARES are passed on to -`cider-boot-middleware-task` before concatenating and DEPENDENCIES - are passed on to `cider-boot-dependencies`." - (concat global-opts - (unless (seq-empty-p global-opts) " ") - "-i \"(require 'cider.tasks)\" " ;; Note the space at the end here - (cider-boot-dependencies (append (cider--jack-in-required-dependencies) dependencies)) - (cider-boot-middleware-task params middlewares))) - (defun cider--gradle-dependency-notation (dependency) "Returns Gradle's GAV dependency syntax. For a \"group/artifact\" \"version\") DEPENDENCY list @@ -823,7 +799,7 @@ removed, LEIN-PLUGINS, LEIN-MIDDLEWARES and finally PARAMS." middleware)) lein-middlewares) (when cider-enable-nrepl-jvmti-agent - `(,(concat "update-in :jvm-opts conj -Djdk.attach.allowAttachSelf")))) + `(,(concat "update-in :jvm-opts conj '\"-Djdk.attach.allowAttachSelf\"'")))) " -- ") " -- " (if (not cider-enrich-classpath) @@ -959,8 +935,8 @@ See also `cider-jack-in-auto-inject-clojure'." These are set in `cider-jack-in-dependencies', `cider-jack-in-lein-plugins' and `cider-jack-in-nrepl-middlewares' are injected from the CLI according to the used PROJECT-TYPE, and COMMAND if provided. Eliminates the need for -hacking profiles.clj or the boot script for supporting CIDER with its nREPL -middleware and dependencies." +hacking profiles.clj for supporting CIDER with its nREPL middleware and +dependencies." (pcase project-type ('lein (cider-lein-jack-in-dependencies global-opts @@ -970,12 +946,6 @@ middleware and dependencies." cider-jack-in-dependencies-exclusions (cider-jack-in-normalized-lein-plugins) cider-jack-in-lein-middlewares)) - ('boot (cider-boot-jack-in-dependencies - global-opts - params - (cider-add-clojure-dependencies-maybe - cider-jack-in-dependencies) - (cider-jack-in-normalized-nrepl-middlewares))) ('clojure-cli (cider-clojure-cli-jack-in-dependencies global-opts params @@ -1057,12 +1027,6 @@ Generally you should not disable this unless you run into some faulty check." (unless (cider-library-present-p "weasel.repl.server") (user-error "Weasel in not available. Please check https://docs.cider.mx/cider/basics/clojurescript/#browser-connected-clojurescript-repl for details"))) -(defun cider-check-boot-requirements () - "Check whether we can start a Boot ClojureScript REPL." - (cider-verify-piggieback-is-present) - (unless (cider-library-present-p "adzerk.boot-cljs-repl") - (user-error "The Boot ClojureScript REPL is not available. Please check https://github.com/adzerk-oss/boot-cljs-repl/blob/master/README.md for details"))) - (defun cider-check-krell-requirements () "Check whether we can start a Krell ClojureScript REPL." (cider-verify-piggieback-is-present) @@ -1223,8 +1187,6 @@ The supplied string will be wrapped in a do form if needed." cider-check-node-requirements) (weasel "(do (require 'weasel.repl.websocket) (cider.piggieback/cljs-repl (weasel.repl.websocket/repl-env :ip \"127.0.0.1\" :port 9001)))" cider-check-weasel-requirements) - (boot "(do (require 'adzerk.boot-cljs-repl) (adzerk.boot-cljs-repl/start-repl))" - cider-check-boot-requirements) (shadow cider-shadow-cljs-init-form cider-check-shadow-cljs-requirements) (shadow-select cider-shadow-select-cljs-init-form cider-check-shadow-cljs-requirements) (krell "(require '[clojure.edn :as edn] @@ -2072,7 +2034,6 @@ Search for lein or java processes including nrepl.command nREPL." PROJECT-DIR defaults to current project." (let* ((default-directory (or project-dir (clojure-project-dir (cider-current-dir)))) (build-files '((lein . "project.clj") - (boot . "build.boot") (clojure-cli . "deps.edn") (babashka . "bb.edn") (shadow-cljs . "shadow-cljs.edn") diff --git a/dev/docker-sample-project/project.clj b/dev/docker-sample-project/project.clj index 01dc35412..061d69987 100644 --- a/dev/docker-sample-project/project.clj +++ b/dev/docker-sample-project/project.clj @@ -2,4 +2,4 @@ :dependencies [[org.clojure/clojure "1.11.1"] [clj-http "3.12.3"]] :source-paths ["src"] - :plugins [[cider/cider-nrepl "0.49.0"]]) + :plugins [[cider/cider-nrepl "0.55.7"]]) diff --git a/dev/tramp-sample-project/project.clj b/dev/tramp-sample-project/project.clj index edf9464f5..8cef2c0d0 100644 --- a/dev/tramp-sample-project/project.clj +++ b/dev/tramp-sample-project/project.clj @@ -2,5 +2,5 @@ :dependencies [[org.clojure/clojure "1.11.1"] [clj-http "3.12.3"]] :source-paths ["src"] - :plugins [[cider/cider-nrepl "0.49.0"] + :plugins [[cider/cider-nrepl "0.55.7"] [refactor-nrepl "3.9.0"]]) diff --git a/doc/antora.yml b/doc/antora.yml index 5a9304229..fd9dacd59 100644 --- a/doc/antora.yml +++ b/doc/antora.yml @@ -2,6 +2,6 @@ name: cider title: CIDER # We always provide version without patch here (e.g. 1.1), # as patch versions should not appear in the docs. -version: 1.15 +version: ~ nav: - modules/ROOT/nav.adoc diff --git a/doc/modules/ROOT/assets/images/cider-stacktrace-inspect.gif b/doc/modules/ROOT/assets/images/cider-stacktrace-inspect.gif new file mode 100644 index 000000000..9b1d35541 Binary files /dev/null and b/doc/modules/ROOT/assets/images/cider-stacktrace-inspect.gif differ diff --git a/doc/modules/ROOT/pages/about/compatibility.adoc b/doc/modules/ROOT/pages/about/compatibility.adoc index a0304a1a1..ffae9187f 100644 --- a/doc/modules/ROOT/pages/about/compatibility.adoc +++ b/doc/modules/ROOT/pages/about/compatibility.adoc @@ -2,7 +2,7 @@ == Emacs -CIDER supports Emacs 26.1+. More generally we try to support the last 3 major Emacs releases +CIDER supports Emacs 27.1+. More generally we try to support the last 3 major Emacs releases when that's feasible and doesn't add a lot of maintenance overhead. NOTE: We pay special attention to supporting whatever Emacs is bundled with the current stable Debian @@ -16,15 +16,17 @@ NOTE: We pay special attention to supporting whatever nREPL is bundled with the == Java -CIDER officially targets Java 8, Java 11, Java 17, Java 21 and the most recent rapid -release version (e.g. Java 22). Generally speaking, we aim -to support all Java releases that are currently officially supported -by Oracle.footnote:[You can find more information about the supported Java releases https://www.oracle.com/java/technologies/java-se-support-roadmap.html[here].] +CIDER officially targets Java 8, 11, 17, 21, and the most recent +non-LTS version. Generally speaking, we aim to support all Java releases that +are currently officially supported by Oracle.footnote:[You can find more +information about the supported Java releases +https://www.oracle.com/java/technologies/java-se-support-roadmap.html[here].] NOTE: The requirements for Java are pretty much ``cider-nrepl``'s requirements. -On Linux you are also required to make sure that JDK sources and javadocs are installed. -You can find example commands in xref:troubleshooting.adoc#navigation-to-jdk-sources-doesnt-work[Troubleshooting]. +While CIDER can work with +https://www.ibm.com/think/topics/jvm-vs-jre-vs-jdk[JRE], it is recommended to +use the full JDK distribution. == Clojure @@ -46,58 +48,12 @@ Currently we apply the same policy for Clojure and ClojureScript support. == Compatibility Matrix -NOTE: For a very long time CIDER and cider-nrepl were released in lock-step, but -this changed in CIDER 0.18. The actual releases diverged for the first time in -CIDER 0.23. - Below you can find the official compatibility matrix for CIDER. .Compatibility Matrix |=== | CIDER | Emacs | nREPL | cider-nrepl | Required JDK | Required Clojure -| 0.22 -| 25.1 -| 0.6 -| 0.22 -| 8 -| 1.8 - -| 0.23 -| 25.1 -| 0.6 -| 0.22 -| 8 -| 1.8 - -| 0.24 -| 25.1 -| 0.6 -| 0.24 -| 8 -| 1.8 - -| 0.25 -| 25.1 -| 0.6 -| 0.25 -| 8 -| 1.8 - -| 0.26 -| 25.1 -| 0.6 -| 0.25 -| 8 -| 1.8 - -| 1.0 -| 25.1 -| 0.6 -| 0.25 -| 8 -| 1.8 - | 1.1 | 25.1 | 0.6 @@ -105,55 +61,6 @@ Below you can find the official compatibility matrix for CIDER. | 8 | 1.8 -| 1.2 -| 26.1 -| 0.9 -| 0.27 -| 8 -| 1.8 - -| 1.3 -| 26.1 -| 0.9 -| 0.28 -| 8 -| 1.8 - -| 1.4 -| 26.1 -| 0.9 -| 0.28 -| 8 -| 1.8 - -| 1.5 -| 26.1 -| 0.9 -| 0.28 -| 8 -| 1.8 - -| 1.6 -| 26.1 -| 1.0 -| 0.29 -| 8 -| 1.8 - -| 1.7 -| 26.1 -| 1.0 -| 0.30 -| 8 -| 1.8 - -| 1.8 -| 26.1 -| 1.0 -| 0.40 -| 8 -| 1.8 - | 1.9 | 26.1 | 1.0 @@ -161,24 +68,24 @@ Below you can find the official compatibility matrix for CIDER. | 8 | 1.8 -| 1.12 +| 1.13 | 26.1 | 1.0 | 0.44 | 8 | 1.9 -| 1.13 +| 1.17 | 26.1 | 1.0 -| 0.44 +| 0.50 | 8 -| 1.9 +| 1.10 -| 1.14 -| 26.1 +| 1.18 +| 27.1 | 1.0 -| 0.47 +| 0.55 | 8 | 1.10 diff --git a/doc/modules/ROOT/pages/about/license.adoc b/doc/modules/ROOT/pages/about/license.adoc index afc25969e..fa68717cc 100644 --- a/doc/modules/ROOT/pages/about/license.adoc +++ b/doc/modules/ROOT/pages/about/license.adoc @@ -21,4 +21,4 @@ NOTE: Reach out to Bozhidar if you have any questions about licensing. == Copyright -© 2012-2024 Bozhidar Batsov, Artur Malabarba, Tim King, Phil Hagelberg and CIDER contributors. +© 2012-2025 Bozhidar Batsov, Artur Malabarba, Tim King, Phil Hagelberg and CIDER contributors. diff --git a/doc/modules/ROOT/pages/about/support.adoc b/doc/modules/ROOT/pages/about/support.adoc index 655aa7a34..e4096508d 100644 --- a/doc/modules/ROOT/pages/about/support.adoc +++ b/doc/modules/ROOT/pages/about/support.adoc @@ -17,14 +17,6 @@ https://github.com/clojure-emacs/cider/discussions[here]. It's a great place to share ideas, help other CIDER users and just be up-to-date with interesting developments. -== Discord - -CIDER has its own Discord chat server, created by CIDER's -author Bozhidar. You can join the Discord server -https://discord.com/invite/nFPpynQPME[here]. - -NOTE: As of 2021, Discord is Bozhidar's preferred chat for CIDER. - == Slack We've got an official https://clojurians.slack.com/[Clojurians @@ -43,14 +35,3 @@ We're also encouraging users to ask CIDER-related questions on StackOverflow. When doing so you should use the http://stackoverflow.com/questions/tagged/cider[cider] tag (ideally combined with the tags `emacs` and `clojure`). - -== Mailing list - -The https://groups.google.com/forum/#!forum/cider-emacs[official mailing list] is -hosted at Google Groups. It's a low-traffic list, so don't be too hesitant to subscribe. - -== Freenode - -We've got an unofficial Freenode channel - `#clojure-emacs`. It's not actively -monitored by the CIDER maintainers themselves, but still you can get support -from other CIDER users there. diff --git a/doc/modules/ROOT/pages/about/team.adoc b/doc/modules/ROOT/pages/about/team.adoc index 558918b80..d0b24ebbc 100644 --- a/doc/modules/ROOT/pages/about/team.adoc +++ b/doc/modules/ROOT/pages/about/team.adoc @@ -8,9 +8,7 @@ does a lot of the groundwork on major new features. Here are the current members of the CIDER core team, listed in the order of joining it: * https://github.com/bbatsov[Bozhidar Batsov] (author & head maintainer) -* https://github.com/vspinu[Vitalie Spinu] -* https://github.com/cichli[Michael Griffiths] -* https://github.com/expez[Lars Andersen] +* https://github.com/alexander-yakushev[Oleksandr Yakushev] == CIDER Alumni @@ -23,3 +21,6 @@ core team members. Lovingly known as The Alumni: * https://github.com/purcell[Steve Purcell] * https://github.com/malabarba[Artur Malabarba] * https://github.com/jeffvalk[Jeff Valk] +* https://github.com/vspinu[Vitalie Spinu] +* https://github.com/cichli[Michael Griffiths] +* https://github.com/expez[Lars Andersen] diff --git a/doc/modules/ROOT/pages/basics/installation.adoc b/doc/modules/ROOT/pages/basics/installation.adoc index 182906cbb..bcac31567 100644 --- a/doc/modules/ROOT/pages/basics/installation.adoc +++ b/doc/modules/ROOT/pages/basics/installation.adoc @@ -12,16 +12,13 @@ release. If you're new to Emacs you might want to go through https://www.gnu.org/software/emacs/tour/index.html[the guided tour of Emacs] and the built-in tutorial (just press kbd:[C-h t]). -CIDER officially supports Emacs 26.1+, Java 8+ and Clojure(Script) -1.8+. CIDER 0.17 (Andalucía) was the final release which supported -Java 7 and Clojure(Script) 1.7. +CIDER officially supports Emacs 27.1+, Java 8+, and Clojure(Script) 1.10+. If +you need to work with earlier versions, check +xref:about/compatibility.adoc#compatibility-matrix[compatibility matrix]. -You'll also need a recent version of either the Clojure CLI tools or your -favorite build tool (Leiningen, Boot, or Gradle) to be able to start CIDER via -`cider-jack-in`. Generally it's a good idea to use the latest stable versions. - -On Linux you are also required to make sure that JDK sources and javadocs are installed. -You can find example commands in xref:troubleshooting.adoc#navigation-to-jdk-sources-doesnt-work[Troubleshooting]. +You'll also need a recent version of your favorite build tool (Clojure CLI, +Leiningen, or Gradle) to be able to start CIDER via `cider-jack-in`. Generally +it's a good idea to use the latest stable versions. == Installation via package.el diff --git a/doc/modules/ROOT/pages/basics/middleware_setup.adoc b/doc/modules/ROOT/pages/basics/middleware_setup.adoc index 1898d017a..02bb3762d 100644 --- a/doc/modules/ROOT/pages/basics/middleware_setup.adoc +++ b/doc/modules/ROOT/pages/basics/middleware_setup.adoc @@ -4,69 +4,36 @@ NOTE: You can skip this section if you don't plan to use `cider-connect` or don't care about the advanced functionality that requires `cider-nrepl`. -Much of CIDER's functionality depends on its own https://github.com/clojure-emacs/cider-nrepl[nREPL -middleware]. Starting -with version 0.11, `cider-jack-in` (kbd:[C-c C-x (C-)j (C-)j]) -automatically injects this middleware and other dependencies as required. - -NOTE: In the past, if you were setting up CIDER, you might have had to -modify `profiles.clj` or `profile.boot`. CIDER now handles -everything automatically and you don't need to add anything -special to these files. The same is true of your `deps.edn` file and -your `build.gradle` (as of Clojurephant 0.7.0-alpha.6). - -If you prefer a standalone REPL, you will need to invoke -`cider-connect` instead of `cider-jack-in` and manually add the -dependencies to your Clojure project (explained in the following -sections). +Much of CIDER's functionality depends on its own +https://github.com/clojure-emacs/cider-nrepl[nREPL middleware]. `cider-jack-in` +(kbd:[C-c C-x (C-)j (C-)j]) automatically injects this middleware and other +dependencies as required. But if you prefer a standalone REPL, you will need to +invoke `cider-connect` instead of `cider-jack-in` and manually add the +dependencies to your Clojure project (explained in the following sections). == Setting Up a Standalone REPL === Using Leiningen -NOTE: Make sure you're using Leiningen 2.9.0 or newer, as 2.9.0 is the first -release to ship with nREPL 0.6. - Use the convenient plugin for defaults, either in your project's -`project.clj` file or in the :repl profile in `~/.lein/profiles.clj`. +`project.clj` file or in the `:repl` profile in `~/.lein/profiles.clj`. [source,clojure] ---- -:plugins [[cider/cider-nrepl "0.49.0"]] +:plugins [[cider/cider-nrepl "0.55.7"]] ---- A minimal `profiles.clj` for CIDER would be: [source,clojure] ---- -{:repl {:plugins [[cider/cider-nrepl "0.49.0"]]}} +{:repl {:plugins [[cider/cider-nrepl "0.55.7"]]}} ---- WARNING: Be careful not to place this in the `:user` profile, as this way CIDER's middleware will always get loaded, causing `lein` to start slower. You really need it just for `lein repl` and this is what the `:repl` profile is for. -=== Using Boot - -NOTE: Make sure you're using Boot 2.8.3 or newer, as 2.8.3 is the first -release to ship with nREPL 0.6. - -Boot users can configure the tool to include the middleware automatically in -all of their projects using a `~/.boot/profile.boot` file like so: - -[source,clojure] ----- -(require 'boot.repl) - -(swap! boot.repl/*default-dependencies* - concat '[[cider/cider-nrepl "0.49.0"]]) - -(swap! boot.repl/*default-middleware* - conj 'cider.nrepl/cider-middleware) ----- - -For more information visit https://github.com/boot-clj/boot/wiki/Cider-REPL[boot-clj wiki]. - === Using tools.deps You can add the following aliases to your deps.edn in order to launch @@ -76,12 +43,12 @@ run `cider-connect` or `cider-connect-cljs`. [source,clojure] ---- - :cider-clj {:extra-deps {cider/cider-nrepl {:mvn/version "0.49.0"}} + :cider-clj {:extra-deps {cider/cider-nrepl {:mvn/version "0.55.7"}} :main-opts ["-m" "nrepl.cmdline" "--middleware" "[cider.nrepl/cider-middleware]"]} :cider-cljs {:extra-deps {org.clojure/clojurescript {:mvn/version "1.10.339"} - cider/cider-nrepl {:mvn/version "0.49.0"} - cider/piggieback {:mvn/version "0.5.3"}} + cider/cider-nrepl {:mvn/version "0.55.7"} + cider/piggieback {:mvn/version "0.6.0"}} :main-opts ["-m" "nrepl.cmdline" "--middleware" "[cider.nrepl/cider-middleware,cider.piggieback/wrap-cljs-repl]"]} ---- @@ -99,7 +66,7 @@ NOTE: Make sure you're using https://github.com/clojurephant/clojurephant[Clojur ---- dependencies { devImplementation 'nrepl:nrepl:0.9.0' - devImplementation 'cider:cider-nrepl:0.49.0' + devImplementation 'cider:cider-nrepl:0.55.7' } tasks.named('clojureRepl') { @@ -133,11 +100,10 @@ server with CIDER's own nREPL handler. It goes without saying that your project should depend on `cider-nrepl`. -NOTE: Prior to CIDER 0.18, CIDER and cider-nrepl were always released together -and their versions had to match for things to work (e.g. CIDER 0.15 required -cider-nrepl 0.15). But as the prominence of cider-nrepl grew and many other -tools started using it, the two projects evolved separately and are no longer in -tight lock-step. Usually, any recent version of cider-nrepl should be (mostly) -compatible with a recent version of CIDER. You can check the required version of -cider-nrepl for your version of CIDER by looking at -`cider-required-middleware-version`. +NOTE: CIDER and cider-nrepl projects are co-developed, but are not released in a +lock-step — they have differing versions. Usually, any recent version of +cider-nrepl should be (mostly) compatible with a recent version of CIDER. You +can check the required version of cider-nrepl for your version of CIDER by +looking at `cider-required-middleware-version`. See also the +xref:about/compatibility.adoc#compatibility-matrix[compatibility +matrix]. diff --git a/doc/modules/ROOT/pages/basics/up_and_running.adoc b/doc/modules/ROOT/pages/basics/up_and_running.adoc index 0baa0666e..7f3e0dcad 100644 --- a/doc/modules/ROOT/pages/basics/up_and_running.adoc +++ b/doc/modules/ROOT/pages/basics/up_and_running.adoc @@ -3,13 +3,13 @@ To use CIDER, you'll need to connect it to a running nREPL server that is associated with your program. Most Clojure developers use standard -build tooling such as Leiningen, Boot, or Gradle, and CIDER can +build tooling such as tools.deps, Leiningen, or Gradle, and CIDER can automatically work with those tools to get you up and running quickly. But those tools are not required; CIDER can connect to an nREPL server that is already started and is managed separately. -NOTE: CIDER will automatically work with Leiningen 2.9.0+ or Boot -2.8.3+. Older versions are not supported. +NOTE: CIDER will automatically work with Leiningen 2.9.0+ or a recent +tools.deps. Older versions are not supported. There are two ways to connect CIDER to an nREPL server: @@ -56,29 +56,27 @@ NOTE: `cider-jack-in` is mainly designed for local development (with files on a local machine and the nREPL process running on the same machine). It does support various common remote/container scenarios, as documented later in this section. Due to the large variation of remote scenarios it cannot support all of them, so in -some cases a manual nREPL start and usage of `cider-connect` might be a better option. +some cases, a better option would be to start nREPL manually and connect to it with `cider-connect`. === Auto-Injecting Dependencies While CIDER's core functionality requires nothing more than an nREPL server, -there are many advanced features that depend on the presence of additional -nREPL middleware. In the early versions of CIDER (up to CIDER 0.11) users had -to add those dependencies themselves, which was a painful and error-prone process. -Fortunately today that's handled auto-magically when you're using `cider-jack-in`. +there are many advanced features that depend on the presence of additional nREPL +middleware. Fortunately, if you're using `cider-jack-in`, that's handled +auto-magically. -If your project uses `lein`, `boot` or `tools.deps` (`deps.edn`), CIDER will +If your project uses `lein` or `tools.deps` (`deps.edn`), CIDER will automatically inject all the necessary nREPL dependencies (e.g. `cider-nrepl` or `piggieback`) when it starts the server. The injection process is extremely -simple - CIDER simply passes the extra dependencies and nREPL configuration to +simple - CIDER passes the extra dependencies and nREPL configuration to your build tool in the command it runs to start the nREPL server. Here's how this looks for `tools.deps`: - $ clojure -Sdeps '{:deps {nrepl {:mvn/version "1.1.2"} cider/cider-nrepl {:mvn/version "0.49.0"}}}' -m nrepl.cmdline --middleware '["cider.nrepl/cider-middleware"]' + $ clojure -Sdeps '{:deps {nrepl {:mvn/version "1.3.1"} cider/cider-nrepl {:mvn/version "0.55.7"}}}' -m nrepl.cmdline --middleware '["cider.nrepl/cider-middleware"]' TIP: If you don't want `cider-jack-in` to inject dependencies automatically, set `cider-inject-dependencies-at-jack-in` to `nil`. Note that you'll have to setup -the dependencies yourself (see xref:basics/middleware_setup.adoc[nREPL Middleware Setup]), -just as in CIDER 0.10 and older. +the dependencies yourself (see xref:basics/middleware_setup.adoc[nREPL Middleware Setup]). Normally `cider-jack-in` would inject only `cider-nrepl` and `cider-jack-in-cljs` would add `piggieback` as well. The injection mechanism is configurable and @@ -119,14 +117,43 @@ for example, if your project defaults to an older version of Clojure than that supported by the CIDER middleware. Set `cider-jack-in-auto-inject-clojure` appropriately to enable this. +=== Enabling nREPL JVMTI agent + +Since version 1.2.0, nREPL includes a native JVMTI agent which makes the eval +interrupts work properly on Java 21 and later. To enable the agent, the Java +process should be launched with `-Djdk.attach.allowAttachSelf`. CIDER will do it +automatically during jack-in if `cider-enable-nrepl-jvmti-agent` variable is set +to `t`. + +[IMPORTANT] +==== +`cider-enable-nrepl-jvmti-agent` has no effect if you start a REPL +process outside of Emacs and connect to it with `cider-connect`. In that +scenario, you have to add `-Djdk.attach.allowAttachSelf` Java property manually +through the means of the build tool. In Leiningen, add this to `project.clj`: + +[source,lisp] +---- +:jvm-opts ["-Djdk.attach.allowAttachSelf"] +---- + +In tools.deps, add this to one of the aliases that you enable with the REPL: + +[source,lisp] +---- +:aliases {:dev + {:jvm-opts ["-Djdk.attach.allowAttachSelf"] + ...}} +---- +==== + === Jacking-in without a Project If you try to run `cider-jack-in` outside a project directory, CIDER will warn you and ask you to confirm whether you really want to do this; more often than not, this is an accident. If you decide to proceed, CIDER will invoke the command configured in -`cider-jack-in-default`. Prior to CIDER 0.17, this defaulted to `lein` -but was subsequently switched to `clj`, Clojure's basic startup command. +`cider-jack-in-default` (defaults to `clj`, Clojure's basic startup command). TIP: You can set `cider-allow-jack-in-without-project` to `t` if you'd like to disable the warning displayed when jacking-in outside a project. @@ -185,17 +212,16 @@ with === Customizing the Jack-in Command Behavior -You can use kbd:[C-u M-x] `cider-jack-in` kbd:[RET] to -specify the exact command that `cider-jack-in` would run. -This option is very useful is you want to specify a something like a `lein` -or `deps.edn` profile. +You can use kbd:[C-u M-x] `cider-jack-in` kbd:[RET] to specify the exact command +that `cider-jack-in` would run. This option is very useful is you want to e.g. +specify extra Leiningen profiles or `deps.edn` aliases. Alternatively you can kbd:[C-u C-u M-x] `cider-jack-in` kbd:[RET], which is a variation of the previous command. This command will first prompt you for the project you want to launch `cider-jack-in` in, which is pretty handy if you're in some other directory currently. This option is also useful if your project -contains some combination of project.clj, build.boot and deps.edn and you want -to launch a REPL for one or the other. +contains some combination of `project.clj` and `deps.edn` and you want to launch +a REPL for one or the other. NOTE: The examples use only `cider-jack-in`, but this behavior is consistent for all `cider-jack-in-\*` commands. @@ -204,36 +230,6 @@ You can further customize the command line CIDER uses for `cider-jack-in` by modifying the some options. Those differ a bit between the various tools, so we'll examine them tool by tool. -==== Enabling nREPL JVMTI agent - -Since version 1.2.0, nREPL includes a native JVMTI agent which makes the eval -interrupts work properly on Java 21 and later. To enable the agent, the Java -process should be launched with `-Djdk.attach.allowAttachSelf`. CIDER will do it -automatically during jack-in if `cider-enable-nrepl-jvmti-agent` variable is set -to `t`. - -[IMPORTANT] -==== -`cider-enable-nrepl-jvmti-agent` has no effect if you start a REPL -process outside of Emacs and connect to it with `cider-connect`. In that -scenario, you have to add `-Djdk.attach.allowAttachSelf` Java property manually -through the means of the build tool. In Leiningen, add this to `project.clj`: - -[source,lisp] ----- -:jvm-opts ["-Djdk.attach.allowAttachSelf"] ----- - -In tools.deps, add this to one of the aliases that you enable with the REPL: - -[source,lisp] ----- -:aliases {:dev - {:jvm-opts ["-Djdk.attach.allowAttachSelf"] - ...}} ----- -==== - ==== Leiningen Options * `cider-lein-command` - the name of the Leiningen executable (`lein` by default) @@ -264,13 +260,6 @@ with the following alternatives NOTE: Alternatively you can use WSL (e.g. to run nREPL and Emacs there), which will likely result in a better overall development experience. -==== Boot Options - -* `cider-boot-command` - the name of the Boot executable (`boot` by default) -* `cider-boot-parameters` - these are usually task names and their parameters -(e.g., `dev` for launching boot's dev task instead of the standard `repl -s -wait`) - ==== Gradle Options * `cider-gradle-command` - the name of the Gradle executable (`./gradlew` by default) @@ -287,9 +276,9 @@ Which Jack-In Command is used is based on the project type. You can override the This allows for fine-grained control over how cider starts the nrepl-server. The precedence order for determining the Jack-In Command is: -1) :jack-in-cmd if provided as a parameter, -2) `cider-jack-in-command` if set as a directory local variable, and -3) inferred from the project type (the default). +1. `:jack-in-cmd` if provided as a parameter, +2. `cider-jack-in-command` if set as a directory local variable, and +3. Inferred from the project type (the default). ==== Setting a project-wide command @@ -339,18 +328,11 @@ $ lein repl :headless This will start the project's nREPL server. -If your project uses `boot`, do this instead: - -[source,sh] ----- -$ boot repl -s wait (or whatever task launches a repl) ----- - It is also possible for plain `clj`, although the command is somewhat longer: [source,sh] ---- -$ clj -Sdeps '{:deps {cider/cider-nrepl {:mvn/version "0.49.0"}}}' -m nrepl.cmdline --middleware "[cider.nrepl/cider-middleware]" +$ clj -Sdeps '{:deps {cider/cider-nrepl {:mvn/version "0.55.7"}}}' -m nrepl.cmdline --middleware "[cider.nrepl/cider-middleware]" ---- Alternatively, you can start nREPL either manually or using the facilities @@ -390,7 +372,7 @@ connect via ssh to remote hosts when unable to connect directly. It's There's also `nrepl-force-ssh-for-remote-hosts` which will force the use of ssh for remote connection unconditionally. -WARNING: As nREPL connections are insecure by default you're encouraged to use only SSH +WARNING: As nREPL connections are insecure by default, you're encouraged to use only SSH tunneling when connecting to servers running outside of your network. There's a another case in which CIDER may optionally leverage the `ssh` command - when diff --git a/doc/modules/ROOT/pages/cljs/other_repls.adoc b/doc/modules/ROOT/pages/cljs/other_repls.adoc index 010a922f4..b954b80de 100644 --- a/doc/modules/ROOT/pages/cljs/other_repls.adoc +++ b/doc/modules/ROOT/pages/cljs/other_repls.adoc @@ -48,45 +48,6 @@ documentation lookup, the namespace browser, and macroexpansion). TIP: For more information on Weasel you should consult its https://github.com/nrepl/weasel/blob/master/README.md[documentation]. -== Boot ClojureScript REPL - -`boot-cljs` is another browser-connected ClojureScript REPL, that's targeting the Boot build tool. -Internally, it's powered by Weasel. Let's go over the steps required to use it. - -. Add this to your dependencies in `build.boot`: -+ -[source,clojure] ----- -[adzerk/boot-cljs "X.Y.Z" :scope "test"] -[adzerk/boot-cljs-repl "X.Y.Z" :scope "test"] -[pandeiro/boot-http "X.Y.Z" :scope "test"] -[weasel "0.7.1" :scope "test"] -[cider/piggieback "0.5.3" :scope "test"] ; not needed for cider-jack-in-cljs ----- -+ -and this at the end of `build.boot`: -+ -[source,clojure] ----- -(require - '[adzerk.boot-cljs :refer [cljs]] - '[adzerk.boot-cljs-repl :refer [cljs-repl]] - '[pandeiro.boot-http :refer [serve]]) - -(deftask dev [] - (comp (serve) - (watch) - (cljs-repl) ; order is important!! - (cljs))) ----- -+ -. Type kbd:[M-x] `customize-variable` kbd:[RET] `cider-boot-parameters` -and insert `dev`. -. Open a file in your project and type kbd:[M-x] `cider-jack-in-cljs`. -. Connect to the running server with your browser. The address is printed on the terminal, but it's probably `+http://localhost:3000+`. - -For more information visit https://github.com/adzerk-oss/boot-cljs-repl[boot-cljs-repl]. - == nbb (node.js babashka) CIDER has built-in support for `nbb`. You can either jack in to an nbb project with `M-x clojure-jack-in-cljs`. diff --git a/doc/modules/ROOT/pages/cljs/shadow-cljs.adoc b/doc/modules/ROOT/pages/cljs/shadow-cljs.adoc index b676e9883..b3df64aad 100644 --- a/doc/modules/ROOT/pages/cljs/shadow-cljs.adoc +++ b/doc/modules/ROOT/pages/cljs/shadow-cljs.adoc @@ -62,7 +62,7 @@ And connect to it with `cider-connect`. ...For that to work, `shadow-cljs.edn` contents like the following are assumed: ```clj - :dependencies [[cider/cider-nrepl "0.49.0"] ;; mandatory (unless it's inherited from deps.edn or otherwise present in the classpath of shadow-cljs's JVM process) + :dependencies [[cider/cider-nrepl "0.55.7"] ;; mandatory (unless it's inherited from deps.edn or otherwise present in the classpath of shadow-cljs's JVM process) [refactor-nrepl/refactor-nrepl "3.9.0"]] ;; refactor-nrepl is optional :nrepl {:middleware [cider.nrepl/cider-middleware ;; it's advisable to explicitly add this middleware. It's automatically added by shadow-cljs (if available in the classpath), unless `:nrepl {:cider false}` diff --git a/doc/modules/ROOT/pages/cljs/up_and_running.adoc b/doc/modules/ROOT/pages/cljs/up_and_running.adoc index a7cdf1eca..e4bc2feb9 100644 --- a/doc/modules/ROOT/pages/cljs/up_and_running.adoc +++ b/doc/modules/ROOT/pages/cljs/up_and_running.adoc @@ -19,14 +19,13 @@ to connect to an already running nREPL server using === Manual Piggieback Setup To setup piggieback, add the following dependencies to your project -(`project.clj` in a Leiningen based project or `build.boot` in a Boot -project or `deps.edn`): +(`project.clj` or `deps.edn`): [source,clojure] ---- ;; use whatever are the most recent versions here -[cider/piggieback "0.5.3"] -[org.clojure/clojure "1.11.1"] +[cider/piggieback "0.6.0"] +[org.clojure/clojure "1.12.0"] ---- as well as `piggieback` nREPL middleware: @@ -38,14 +37,6 @@ in `project.clj`: :repl-options {:nrepl-middleware [cider.piggieback/wrap-cljs-repl]} ---- -or in `build.boot`: - -[source,clojure] ----- -(task-options! - repl {:middleware '[cider.piggieback/wrap-cljs-repl]}) ----- - or in `deps.edn`: [source,clojure] @@ -60,8 +51,8 @@ or in `build.gradle`: [source, groovy] ---- dependencies { - devImplementation 'nrepl:nrepl:1.1.2' - devImplementation 'cider:cider-nrepl:0.49.0' + devImplementation 'nrepl:nrepl:1.3.1' + devImplementation 'cider:cider-nrepl:0.55.7' devImplementation 'cider:cider-piggieback:0.5.3' } diff --git a/doc/modules/ROOT/pages/config/syntax_highlighting.adoc b/doc/modules/ROOT/pages/config/syntax_highlighting.adoc index 748da6370..2050c0866 100644 --- a/doc/modules/ROOT/pages/config/syntax_highlighting.adoc +++ b/doc/modules/ROOT/pages/config/syntax_highlighting.adoc @@ -56,5 +56,5 @@ should take care of them): * `cider-deprecated-face` - used for syntax highlighting deprecated vars * `cider-instrumented-face` - used for syntax highlighting instrumented for debugging vars -* `cider-traced-face` - used for syntax highlighting traced vars +* `cider-traced-face` - used for syntax highlighting traced and profiled vars * `cider-reader-conditional-face` - used for syntax highlighting inactive reader conditional branches diff --git a/doc/modules/ROOT/pages/contributing/docs.adoc b/doc/modules/ROOT/pages/contributing/docs.adoc index eabe5c14f..123af5e12 100644 --- a/doc/modules/ROOT/pages/contributing/docs.adoc +++ b/doc/modules/ROOT/pages/contributing/docs.adoc @@ -8,7 +8,7 @@ of the primary ways to discover those. Please, consider improving and extending The manual is generated from the AsciiDoc files in the https://github.com/clojure-emacs/cider/tree/master/doc[doc] folder of CIDER's GitHub repo and is published to https://docs.cider.mx. https://antora.org[Antora] is used to convert the AsciiDoc source into HTML. -Antora's filesystem layout is described https://docs.antora.org/antora/2.0/component-structure/[here]. +Antora's filesystem layout is described https://docs.antora.org/antora/3.1/component-structure/[here]. == Installing Antora @@ -18,10 +18,10 @@ Installing the Antora is super simple: [source] ---- -$ npm i -g @antora/cli@2.0 @antora/site-generator-default@2.0 +$ npm install ---- -Check out https://docs.antora.org/antora/2.0/install/install-antora/[the detailed installation instructions] +Check out https://docs.antora.org/antora/3.1/install/install-antora/[the detailed installation instructions] if you run into any problems. == Editing the Docs @@ -37,27 +37,43 @@ You can build the documentation locally from the https://github.com/clojure-emac [source,shell] ---- +$ git clone https://github.com/clojure-emacs/docs.cider.mx $ cd docs.cider.mx -$ antora antora-playbook.yml +$ make build ---- +To check the generated site you can simply open `build/site/index.html` in your favourite browser. + == Deploying the Docs Site NOTE: The manual will be regenerated manually periodically by CIDER's Core Team. We're currently publishing the user manual to GitHub Pages. -The deployment process is simply pushing the generated HTML to GitHub. Simple as that. -There's a simple script in the documentation repository that automates the process of -fetching the latest updates and publishing them: +The deployment is handled by a GitHub Actions workflow that builds and deploys the +documentation every time something is changed in the documentation site's repository. +It can also be triggered manually if needed. -[source,shell] ----- -$ cd docs.cider.mx -$ ./deploy ----- +== Updating the Playbook IMPORTANT: Don't forget to update the manual's version metadata when cutting CIDER releases. It lives in `doc/antora.yml`. -Down the road we plan to automate the process and deploy automatically changes to the manual. -Ideally this should be done by our CI. +When cutting new releases you'll have to updated `antora-playbook.yml` to mention +their relevant tags from which the documentation needs to be build. Here's how this +looks for one of the projects: + +[source] +---- +- url: https://github.com/clojure-emacs/cider.git + branches: master + tags: ['v1.7.0', 'v1.8.0'] + start_path: docs +---- + +TIP: You need to add one such block for each new CIDER module you're adding to the docs site. + +== Troubleshooting + +The most common mistake that people make is to forget to update the version of an Antora docs module +after cutting a release. This will result in an error saying you've got the same version in two branches (e.g. `master` +and `v1.0`). Fixing this is pretty simple - just update the version to `master` in `antora.yml`. diff --git a/doc/modules/ROOT/pages/debugging/debugger.adoc b/doc/modules/ROOT/pages/debugging/debugger.adoc index e06cecbf0..3df79dcdd 100644 --- a/doc/modules/ROOT/pages/debugging/debugger.adoc +++ b/doc/modules/ROOT/pages/debugging/debugger.adoc @@ -7,7 +7,7 @@ http://www.gnu.org/software/emacs/manual/html_node/elisp/Edebug.html[Edebug]. Yo image::cider_debugger.gif[CIDER Debugger] WARNING: The debugger **does not** support ClojureScript. -You might want to use https://github.com/jpmonettas/cider-storm[Cider Storm] instead. +Check out https://github.com/jpmonettas/cider-storm[Cider Storm] if you need to debug ClojureScript code. == Using the Debugger diff --git a/doc/modules/ROOT/pages/debugging/inspector.adoc b/doc/modules/ROOT/pages/debugging/inspector.adoc index 72bc376e1..6c40aaeca 100644 --- a/doc/modules/ROOT/pages/debugging/inspector.adoc +++ b/doc/modules/ROOT/pages/debugging/inspector.adoc @@ -36,10 +36,6 @@ You'll have access to additional keybindings in the inspector buffer | `cider-inspector-next-inspectable-object` | Navigate inspectable sub-objects -| kbd:[f] and kbd:[b] -| `forward-char`, `backward-char` -| Navigate across characters on a line - | kbd:[Return] | `cider-inspector-operate-on-point` | Inspect sub-objects @@ -52,7 +48,7 @@ You'll have access to additional keybindings in the inspector buffer | `cider-inspector-refresh` | Refresh the inspector (e.g. if viewing an atom/ref/agent) -| kbd:[SPC] or kbd:[Next] +| kbd:[SPC] or kbd:[Next] | `cider-inspector-next-page` | Jump to next page in paginated view @@ -60,6 +56,10 @@ You'll have access to additional keybindings in the inspector buffer | `cider-inspector-prev-page` | Jump to previous page in paginated view +| kbd:[y] +| `cider-inspector-display-analytics` +| Calculate and display analytics for the inspected object. Analytics is supported for lists of numbers, strings, tuples, maps; for large key-value maps. + | kbd:[s] | `cider-inspector-set-page-size` | Set a new page size in paginated view @@ -78,12 +78,21 @@ You'll have access to additional keybindings in the inspector buffer | kbd:[v] | `cider-inspector-toggle-view-mode` -| Switch the rendering of the current value between `:normal` and `:object` view mode. In `:object` mode, any value is rendered as a plain Java object (by displaying its fields) instead of custom rendering rules that the Inspector applies in `:normal` mode. +| Switch the rendering of the current value between `:normal`, `:table`, and + `:object` view modes. In `:table` mode, render the value as a table (only supported for sequences of maps or tuples). In `:object` mode, any value is rendered as a plain Java object (by displaying its fields) instead of custom rendering rules that the Inspector applies in `:normal` mode. + +| kbd:[P] +| `cider-inspector-toggle-pretty-print` +| Toggle the pretty printing of values in the inspector. You can set the `cider-inspector-pretty-print` customization option to `t`, if you always want values to be be pretty printed. | kbd:[d] | `cider-inspector-def-current-val` | Defines a var in the REPL namespace with current inspector value. If you tend to always choose the same name(s), you may want to set the `cider-inspector-preferred-var-names` customization option. +| kbd:[C-c C-p] +| `cider-inspector-print-current-value` +| Print the current value of the inspector to the `cider-result-buffer`. + | kbd:[9] | `cider-inspector-previous-sibling` | Navigates to the previous sibling, within a sequential collection. @@ -110,11 +119,6 @@ You'll have access to additional keybindings in the inspector buffer |=== -== Use `enrich-classpath` for best results - -If xref:config/basic_config.adoc#use-enrich-classpath[enrich-classpath] is activated, inspecting a Java class, method or field -will richly display its Java doc comment documentation at the bottom of the inspector. - == Configuration By default, navigation skips over values like nils, numbers and @@ -134,6 +138,10 @@ listed in the table above. If you enable `cider-inspector-fill-frame`, the inspector window fills its frame. +You can toggle the pretty printing of values in the inspector with +kbd:[P] and customize their initial presentation by adjusting the +`cider-inspector-pretty-print` customization option. + When you define a var using kbd:[d], a var name can be suggested (default none). You can customize this value via the `cider-inspector-preferred-var-names` configuration option. Even after setting it, you are free to choose new names on diff --git a/doc/modules/ROOT/pages/debugging/logging.adoc b/doc/modules/ROOT/pages/debugging/logging.adoc index 02ff10ba9..1913b0ea0 100644 --- a/doc/modules/ROOT/pages/debugging/logging.adoc +++ b/doc/modules/ROOT/pages/debugging/logging.adoc @@ -4,19 +4,15 @@ CIDER Log Mode allows you to capture, debug, inspect and view log events emitted by Java logging frameworks. The captured log events can be searched, streamed to the client, pretty-printed, and are integrated -with the CIDER link:inspector.html[Inspector] and -link:../usage/dealing_with_errors.html[Stacktrace Mode]. Here is a +with the CIDER link:inspector.html[Inspector]. Here is a screenshot of CIDER Log Mode in action. image::cider-log.png[CIDER Log] -NOTE: The screenshot displays the list of log events in the -`+*cider-log*+` buffer on the left. To the right, a log event is -visible in the `+*cider-inspect*+` buffer, where the exception of the -event is also displayed in the CIDER Stacktrace Mode. From the -Stacktrace Mode buffer you can jump to the source of each frame. At -the bottom the CIDER log menu is shown from which you can perform -logging related actions. +NOTE: The screenshot displays the list of log events in the `+*cider-log*+` +buffer on the left. To the right, a log event is visible in the +`+*cider-inspect*+` buffer. At the bottom the CIDER log menu is shown from which +you can perform logging related actions. == Features @@ -24,7 +20,6 @@ logging related actions. - Search log events and show them in buffers. - link:../usage/pretty_printing.html[Pretty-print] log events. - Show log events in the CIDER link:inspector.html[Inspector]. -- Show log event exceptions in the CIDER link:../usage/dealing_with_errors.html[Stacktrace Mode]. - Integration with https://github.com/doublep/logview[logview]. == Dependencies @@ -49,6 +44,7 @@ Its usage is mostly self-describing, since each command has its keybinding attac To use CIDER Log Mode, there two main ways to get started: +* `M-x cider-log-show-frameworks`, to see the available logging frameworks. If your logging framework is supported but not shown, see the troubleshooting section. * `M-x cider-log-event`, which uses transient-mode and will not immediately show the logs (you should use transient-mode to show the `+*cider-log*+` buffer) * `M-x cider-log-show` is a newer function that intends to be an "all-in-one" command, intended for a streamlined experience, which can be useful to get started, or for casual usage. ** It doesn't use transient-mode - it aims to do everything in one step @@ -178,6 +174,10 @@ or Clojure CLI aliases. |=== | Command | Keyboard shortcut | Description +| `cider-log-show-frameworks` +| kbd:[C-c M-l f a] +| Show all available log frameworks in a buffer. + | `cider-log-set-framework` | kbd:[C-c M-l f s] | Select the log framework to use. @@ -273,7 +273,7 @@ The following keybindings can be used to interact with log consumers. == Log Event Log events can be searched, streamed to a client or viewed in CIDER's -Inspector and Stacktrace Mode. When searching log events the user can +Inspector Mode. When searching log events the user can specify a set of filters. Events that match the filters are shown in the `+*cider-log*+` buffer. Additionally a log consumer will be attached to the appender to receive log events matching the search @@ -296,10 +296,6 @@ The following keybindings can be used to interact with log events. | kbd:[C-c M-l e c] | Clear all events from the log event buffer. -| `cider-log-show-stacktrace` -| kbd:[C-c M-l e e] -| Show the stacktrace of the log event at point in the CIDER Stacktrace Mode. - | `cider-log-inspect-event` | kbd:[C-c M-l e i] | Show the log event in the CIDER Inspector. @@ -351,3 +347,9 @@ using logical AND condition. The following filters are available: | kbd:[-t] | Only include log events that were emitted by a thread in the list of `threads`. |=== + +== Troubleshooting + +- Make sure the logging library is actually supported by CIDER Log Mode and that it is on your classpath. +- Try requiring the https://github.com/clojure-emacs/logjam/tree/master/src/logjam/framework[Logjam] namespace of the logging library, e.g. `(require 'logjam.framework. :reload)` and make sure it can be loaded without errors. +- Timbre and Encore often have to be upgraded in concert, they use "break versioning". It's often useful to have Timbre + Encore at the latest stable version. diff --git a/doc/modules/ROOT/pages/debugging/profiling.adoc b/doc/modules/ROOT/pages/debugging/profiling.adoc index 8e568c108..d7c20a380 100644 --- a/doc/modules/ROOT/pages/debugging/profiling.adoc +++ b/doc/modules/ROOT/pages/debugging/profiling.adoc @@ -1,47 +1,56 @@ = Profiling -CIDER has a built-in profiler that can help you identify hot-spots in your -application code. It's built on top of the https://github.com/thunknyc/profile[thunknyc/profile] library. +CIDER has a simple built-in profiler that can you to quickly measure the running +time of individual functions. It is similar to wrapping your functions with +`time` macro, except it records every timing and displays a summarized result. + +NOTE: Profiling is different from benchmarking. Benchmarking more accurately +tells you how long the code executes. If you need accurate timing results, use a +serious benchmarking library like +https://github.com/hugoduncan/criterium[Criterium]. If you need to understand +where most of the time is spent, use a serious profiler like +https://github.com/clojure-goes-fast/clj-async-profiler[clj-async-profiler]. NOTE: The profiler doesn't support ClojureScript. == Usage -Using CIDER's profiler is super easy. You'd just identify -the vars you'd want to profile and invoke -`M-x cider-profile-toggle` (kbd:[C-c C-= t]). By defaults it operates on the symbol -at point, but will prompt for a var if there's nothing under the point. - -TIP: There's also `cider-profile-ns-toggle` (kbd:[C-c C-= n]) that will profiles all vars in a -namespace. +To start using CIDER profiler, choose the vars you want to profile and invoke +`M-x cider-profile-toggle` (kbd:[C-c C-= t]). By defaults it operates on the +symbol at point, but will prompt for a var if there's nothing under the point. +You can also mark all functions in the namespace for profiling via +`cider-profile-ns-toggle` (kbd:[C-c C-= n]). -Afterwards you can evaluate some code making use of those vars and their -invocations will be automatically profiled. +Then, evaluate some code making use of those vars and their invocations will be +automatically profiled. -You can display a report of the collected profiling data with `M-x cider-profile-summary` (kbd:[C-c C-= S]). If you'd like to limit the displayed data to a particular var you should try -`M-x cider-profile-var-summary` (kbd:[C-c C-= s]). +You can display a report of the collected profiling data with `M-x +cider-profile-summary` (kbd:[C-c C-= s]). == Understanding the Report Format -A typical profiling report looks something like this: +Profiling reports are rendered by xref:debugging/inspector.adoc[CIDER +inspector]. A typical profiling report looks like this: .... -| :name | :n | :sum | :q1 | :med | :q3 | :sd | :mad | -|----------------+----+------+-----+------+-----+-----+------| -| #'user/my-add | 1 | 2µs | 2µs | 2µs | 2µs | 0µs | 0µs | -| #'user/my-mult | 2 | 11µs | 3µs | 8µs | 3µs | 3µs | 5µs | +| # | :name | :n | :mean | :std | :sum | :min | :max | :med | :samples | +|---+-----------------+------+--------+---------+--------+--------+--------+--------+------------| +| 0 | #'sample-ns/bar | 1000 | 3 us | ±14 us | 3 ms | 791 ns | 384 us | 2 us | [791 ...] | +| 1 | #'sample-ns/baz | 1000 | 307 ns | ±710 ns | 307 us | 84 ns | 22 us | 250 ns | [84 ...] | +| 2 | #'sample-ns/foo | 1000 | 7 us | ±18 us | 7 ms | 3 us | 495 us | 5 us | [2584 ...] | +| 3 | #'sample-ns/qux | 1000 | 8 us | ±20 us | 8 ms | 3 us | 543 us | 5 us | [3125 ...] | .... Let's demystify all the column names: * `:n`: Number of samples. +* `:mean`: Average time spent in fn. +* `:std`: Standard deviation. * `:sum`: Aggregate time spent in fn. -* `:q1`: First quartile i.e. twenty-fifth percentile. +* `:min`: Minimal recorded time for fn. +* `:min`: Maximal recorded time for fn. * `:med`: Median i.e. fiftieth percentile. -* `:q3`: Third quartile i.e. seventy-fifth percentile. -* `:sd`: Standard deviation i.e. the square root of the sum of squares - of differences from the mean. -* `:mad`: Mean absolute deviation. See https://en.wikipedia.org/wiki/Average_absolute_deviation[this article] for more details. +* `:samples`: A list of all timing samples. You can click it to see the full list in the inspector. == Keybindings @@ -56,22 +65,10 @@ Let's demystify all the column names: | kbd:[C-c C-= n] | Toggle profiling for the current ns. -| `cider-profile-var-profiled-p` -| kbd:[C-c C-= v] -| Show whether some var has profiling enabled or not. - -| `cider-profile-var-summary` -| kbd:[C-c C-= s] -| Display the profiling summary for some var. - | `cider-profile-summary` -| kbd:[C-c C-= S] +| kbd:[C-c C-= s] | Display the profiling summary for all vars. -| `cider-profile-samples` -| kbd:[C-c C-= +] -| Display or update `max-sample-count`. - | `cider-profile-clear` | kbd:[C-c C-= c] | Clear profiling data. diff --git a/doc/modules/ROOT/pages/index.adoc b/doc/modules/ROOT/pages/index.adoc index b1cb104b3..6661d1e0c 100644 --- a/doc/modules/ROOT/pages/index.adoc +++ b/doc/modules/ROOT/pages/index.adoc @@ -73,7 +73,7 @@ CIDER packs plenty of features. Here are some of them (in no particular order): * `clojure.test` integration * `clojure.spec` integration * Interactive debugger -* Data Inspector +* Data inspector * Integration with Java logging frameworks * Profiling & tracing * ClojureScript support @@ -92,7 +92,7 @@ and then jumped to a REPL buffer to try out something there. Here's also a video demo of CIDER's essential functionality: -video::aYA4AAjLfT0[youtube] +video::aYA4AAjLfT0[youtube,width=640,height=480] You can find several other demo videos on the xref:additional_resources.adoc[Additional Resources] page. diff --git a/doc/modules/ROOT/pages/platforms/basilisp.adoc b/doc/modules/ROOT/pages/platforms/basilisp.adoc index 1859f5d80..79d9b519f 100644 --- a/doc/modules/ROOT/pages/platforms/basilisp.adoc +++ b/doc/modules/ROOT/pages/platforms/basilisp.adoc @@ -1,7 +1,7 @@ = Basilisp Integration with CIDER https://github.com/basilisp-lang/basilisp[basilisp] -since CIDER 1.14 +NOTE: Basilisp support was added in CIDER 1.14. == Overview @@ -28,7 +28,7 @@ If you don't have or want a basilisp project file, you can use universal jack in NOTE: an alternative to kbd:[M-5] is kbd:[C-u 5] -You can also bind the universal jack-in to Basilisp to a function to use as a shortcut, for example +You can also bind the universal jack-in to Basilisp to a function to use as a shortcut, for example: [source,lisp] ---- @@ -51,7 +51,7 @@ To see available options, type `basilisp nrepl-server -h` in a shell prompt. == Configuration -The jack-in command can be configured with the following defcustoms +The jack-in command can be configured with the following defcustoms: === `cider-basilisp-command` @@ -81,7 +81,7 @@ For example, to set the path to the basilisp executable within a virtual environ - kbd:[M-x add-dir-local-variable] - Mode or subdirectory: `clojure-mode` - Add directory-local variable: `cider-basilisp-command` -- Add cider-basilisp-command with value: `"c:/dev/venvs/312/Scripts/basilisp"` +- Add `cider-basilisp-command` with value: `"c:/dev/venvs/312/Scripts/basilisp"` This should result to updating or creating a `.dir-local.el` file like below diff --git a/doc/modules/ROOT/pages/repl/configuration.adoc b/doc/modules/ROOT/pages/repl/configuration.adoc index 774abc889..39458d474 100644 --- a/doc/modules/ROOT/pages/repl/configuration.adoc +++ b/doc/modules/ROOT/pages/repl/configuration.adoc @@ -340,12 +340,21 @@ reset automatically by the `track-state` middleware. (setq cider-repl-history-size 1000) ; the default is 500 ---- -* To store the REPL history in a file: +* To store the REPL history of all projects in a single file: [source,lisp] ---- (setq cider-repl-history-file "path/to/file") ---- -Note that CIDER writes the history to the file when you kill the REPL -buffer, which includes invoking `cider-quit`, or when you quit Emacs. +* To store the REPL history per project (by creating a + `.cider-history` file at the root of each): + +[source,lisp] +---- +(setq cider-repl-history-file 'per-project) +---- + +Note that CIDER writes the history to the file(s) when you kill the +REPL buffer, which includes invoking `cider-quit`, or when you quit +Emacs. diff --git a/doc/modules/ROOT/pages/repl/history.adoc b/doc/modules/ROOT/pages/repl/history.adoc index 7e095ee43..88a34a1a2 100644 --- a/doc/modules/ROOT/pages/repl/history.adoc +++ b/doc/modules/ROOT/pages/repl/history.adoc @@ -175,4 +175,7 @@ There are a number of important keybindings in history buffers. | kbd:[U] | Undo in the REPL buffer. + +| kbd:[D] +| Delete history item (at point). |=== diff --git a/doc/modules/ROOT/pages/troubleshooting.adoc b/doc/modules/ROOT/pages/troubleshooting.adoc index 803a24bb3..11271457a 100644 --- a/doc/modules/ROOT/pages/troubleshooting.adoc +++ b/doc/modules/ROOT/pages/troubleshooting.adoc @@ -161,8 +161,8 @@ least what's being required). === Warning saying you have to use newer nREPL -CIDER currently requires at least nREPL 0.6 to work properly. As nREPL comes -bundled with Leiningen and Boot, from time to time you might have to override the +CIDER currently requires at least nREPL 1.0 to work properly. As nREPL comes +bundled with Leiningen, from time to time you might have to override the version supplied by them (e.g. if you're forced to use an older version of Leiningen or there's no release bundling the required nREPL version yet). Leiningen users can add this to their `profiles.clj` to force the proper dependency: @@ -172,8 +172,6 @@ users can add this to their `profiles.clj` to force the proper dependency: {:repl {:dependencies [[nrepl/nrepl "x.y.z"]]}} ---- -The procedure is pretty similar for Boot. - IMPORTANT: Make sure you add the newer nREPL dependency to the `:dependencies` key instead of `:plugins` (where the `cider-nrepl` Lein plugin resides). That's a pretty common mistake. @@ -230,7 +228,6 @@ in the "Middleware Setup" section. * Do `C-h v cider-inject-dependencies-at-jack-in`, and check that this variable is non-nil. * Make sure your project depends on at least Clojure `1.7.0`. * If you use Leiningen, make sure your `lein --version` is at least `2.9.0`. -* If you use Boot and you've changed `cider-boot-parameters`, that's probably the cause. If the above doesn't work, you can try specifying the cider-nrepl middleware manually, as per the @@ -242,8 +239,8 @@ in the "Middleware Setup" section. This means you're manually adding the cider-nrepl middleware in your project, but you shouldn't do that because `cider-jack-in` already does that for you. Look into the following files, and ensure you've removed all references to -`cider-nrepl` and `nrepl`: `project.clj`, `build.boot`, -`~/.lein/profiles.clj` and `~/.boot/profile.boot`. +`cider-nrepl` and `nrepl`: `project.clj`, `deps.edn`, +`~/.lein/profiles.clj` and `~/.clojure/deps.edn`. === I get some error related to refactor-nrepl on startup diff --git a/doc/modules/ROOT/pages/usage/code_completion.adoc b/doc/modules/ROOT/pages/usage/code_completion.adoc index 9665b99c7..43e97ae57 100644 --- a/doc/modules/ROOT/pages/usage/code_completion.adoc +++ b/doc/modules/ROOT/pages/usage/code_completion.adoc @@ -35,31 +35,6 @@ Normally kbd:[TAB] only indents, but now it will also do completion if the code is already properly indented. ==== -== Completion styles - -CIDER defines a specialized completion category through the `cider-complete-at-point` function, -added to `completion-at-point-functions`, establishing a dedicated completion category named -`cider`. - -The CIDER completion at point function supports most completion styles, including -`partial-completion`, `orderless` and `flex` (read more below). - - -Sometimes the user may want to use a different completion style just for the CIDER -complete at point function. That can be achieved by setting -`completion-category-overrides`, overwriting the completion style of the CIDER -complete at point function. The following snippet accomplishes that: - -[source,lisp] ----- -(add-to-list 'completion-category-overrides '(cider (styles basic))) ----- - -This specifies that the `cider` completion category should employ the basic completion style by -default. - -You can also enable the `flex` completion style by activating xref:usage/code_completion.adoc#fuzzy-candidate-matching[fuzzy candidate matching]. - == Auto-completion While the standard Emacs tooling works just fine, we suggest that @@ -138,33 +113,30 @@ without needing to hit an extra key, please customize: (custom-set-variables '(company-auto-update-doc t)) ---- -=== Fuzzy candidate matching +=== Rich candidate matching + +Starting with version 1.18, CIDER by default enables a custom completion style +that provides richer and more useful candidate matching, for example: + +- Long vars that contain dashes by first characters of individual parts, e.g. + `mi` or `mai` complete to `map-indexed`. +- Namespaces by first characters of parts, e.g. `cji` completes to + `clojure.java.io`. +- Not imported classnames by their short name prefixes, e.g. `BiFun` completes + to `java.util.function.BiFunction`. -By default, CIDER will use the completion styles defined in -`completion-styles`, the defaults being `(basic partial-completion -emacs22)` since Emacs 23. For a better description of how those -completion styles operates, refer to the official Emacs manual on -https://www.gnu.org/software/emacs/manual/html_node/emacs/Completion-Styles.html[how completion alternatives are chosen]. +You can learn all completion scenarios and features +https://github.com/alexander-yakushev/compliment/wiki/Examples[here]. -CIDER provides a function to enable the `flex` completion style for CIDER-specific -completions. If you wish to enable that, you can add this to your config: +If you only want to receive standard prefix-restricted completions (where the +candidate must contain the prefix at the beginning verbatim), you can disable +this feature by adding this to your config: [source,lisp] ---- -(cider-enable-flex-completion) +(cider-enable-cider-completion-style -1) ---- -This adds the `flex` completion style, as introduced in Emacs 27. - -Now, `company-mode` (and other completion packages like `corfu`) will -accept certain fuzziness when matching candidates against the -prefix. For example, typing `mi` will show you `map-indexed` as one of -the possible completion candidates and `cji` will complete to -`clojure.java.io`. Different completion examples are shown -https://github.com/alexander-yakushev/compliment/wiki/Examples[here]. - -NOTE: `cider-company-enable-fuzzy-completion` (now deprecated) should be used for Emacs < 27. - === Completion annotations Completion candidates will be annotated by default with an abbreviation @@ -179,6 +151,29 @@ image::completion-annotations.png[Completion Annotations] TIP: Completion annotations can be disabled by setting `cider-annotate-completion-candidates` to `nil`. +=== Completion styles + +The CIDER completion at point function supports most completion styles, +including `partial-completion`, `orderless`, `flex`, and its own custom +completion style named `cider`. The latter is enabled by default. Sometimes the +user may want to use a different completion style for the CIDER complete at +point function. That can be achieved by setting `completion-category-overrides`, +overwriting the completion style of the CIDER complete at point function. The +following snippet accomplishes that: + +[source,lisp] +---- +(add-to-list 'completion-category-overrides '(cider (styles basic))) +---- + +For a better description of how those completion styles operates, refer to the +official Emacs manual on +https://www.gnu.org/software/emacs/manual/html_node/emacs/Completion-Styles.html[how +completion alternatives are chosen]. + +This specifies that the `cider` completion category should employ the basic completion style by +default. + === Notes on class disambiguation Sometimes, the completion user experience may be interrupted by a `completing-read` @@ -203,9 +198,9 @@ keys to cancel the prompt by customizing: Sometimes, the completion fails to recognize new classes that came with dependencies that were loaded dynamically after the REPL was started (e.g. via -Boot). Executing `M-x cider-completion-flush-caches` (or going through the menu -`+CIDER Interaction->Misc->Flush completion cache+`) forces the completion backend -to re-read all classes it can find on the classpath. +Clojure 1.12 `add-lib`). Executing `M-x cider-completion-flush-caches` (or going +through the menu `+CIDER Interaction->Misc->Flush completion cache+`) forces the +completion backend to re-read all classes it can find on the classpath. == Implementation Details @@ -213,6 +208,6 @@ NOTE: You don't really need to know any of this if you're using only `cider-jack The bulk of the code completion logic resides in `cider-nrepl` https://github.com/clojure-emacs/cider-nrepl/blob/master/src/cider/nrepl/middleware/complete.clj[completion middleware]. Internally it delegates to `compliment` for the Clojure completion and `clj-suitable` for the ClojureScript completion. -Starting with nREPL 0.8, there's also a built-in `completions` nREPL op that CIDER will fallback to, in the absence of `cider-nrepl`. Its API is similar to that of the `complete` op in `cider-nrepl` and it can be configured to use different completion functions. The built-in op currently supports only Clojure. See the https://nrepl.org/nrepl/usage/misc.html#code-completion[nREPL docs] for more details. +nREPL also has a built-in `completions` op that CIDER will fallback to, in the absence of `cider-nrepl`. Its API is similar to that of the `complete` op in `cider-nrepl` and it can be configured to use different completion functions. The built-in op currently supports only Clojure. See the https://nrepl.org/nrepl/usage/misc.html#code-completion[nREPL docs] for more details. Basically, you'll get great code completion in the presence of `cider-nrepl` and basic completion otherwise. diff --git a/doc/modules/ROOT/pages/usage/code_evaluation.adoc b/doc/modules/ROOT/pages/usage/code_evaluation.adoc index a2f21dc45..60340e8a7 100644 --- a/doc/modules/ROOT/pages/usage/code_evaluation.adoc +++ b/doc/modules/ROOT/pages/usage/code_evaluation.adoc @@ -183,6 +183,16 @@ Set this variable to `nil` to disable it. NOTE: CIDER uses internally the excellent package https://github.com/Malabarba/spinner.el[spinner.el]. +=== Syntax Highlighting of Results + +By default the results of interactive evaluation (both those displayed in the minibuffer and in overlays) are font-locked as Clojure code. +You can disable this by tweaking the configuration option `cider-result-use-clojure-font-lock`: + +[source,lisp] +---- +(setq cider-result-use-clojure-font-lock nil) +---- + === Overlays When you evaluate code in Clojure files, the result is displayed in the buffer diff --git a/doc/modules/ROOT/pages/usage/dealing_with_errors.adoc b/doc/modules/ROOT/pages/usage/dealing_with_errors.adoc index bd44bef17..afb19e0e5 100644 --- a/doc/modules/ROOT/pages/usage/dealing_with_errors.adoc +++ b/doc/modules/ROOT/pages/usage/dealing_with_errors.adoc @@ -210,106 +210,23 @@ for instance: (setq cider-stacktrace-fill-column 80) ---- -=== Inspecting printed stacktraces - -Some of the errors you encounter as a Clojurists aren't necessarily -evaluation errors that happened in your REPL. Many times, you see -errors printed in a textual representation in other buffers as well, -like log files or the REPL for example. Cider can parse and analyze -some of those printed errors as well and show them in -`cider-stacktrace-mode` with the following commands: - -* The `cider-stacktrace-analyze-at-point` command uses the `thingatpt` - library to extract the current stacktrace at point. It sends the - extracted stacktrace to the middleware in order to parse and analyze - it, and then shows the result in Cider's `cider-stacktrace-mode`. - -* The `cider-stacktrace-analyze-in-region` command does the same as - `cider-stacktrace-analyze-at-point`, but uses the current region to - extract the stacktrace. - -==== Examples - -Here is an example of a stacktrace printed with the Java -`printStackTrace` method: - -[source,text] ----- -clojure.lang.ExceptionInfo: BOOM-1 {:boom "1"} - at java.base/java.lang.Thread.run(Thread.java:829) ----- - -To open this stacktrace in the Cider stacktrace inspector, move point -somewhere over the exception and run `M-x -cider-stacktrace-analyze-at-point`. - -This also works to some extent for exceptions that are buried inside a -string like the following exception: - -[source,text] ----- -"clojure.lang.ExceptionInfo: BOOM-1 {:boom \"1\"}\n at java.base/java.lang.Thread.run(Thread.java:829)" ----- - -Those exceptions are often hard to read. The Cider stacktrace -inspector can help you navigating exceptions even in those cases. - -==== Supported formats - -Cider recognizes stacktraces printed in the following formats: - -- `Aviso` - Exceptions printed with the - https://ioavisopretty.readthedocs.io/en/latest/exceptions.html[write-exception] - function of the https://github.com/AvisoNovate/pretty[Aviso] - library. - -- `clojure.repl` - Exceptions printed with the - https://clojure.github.io/clojure/branch-master/clojure.repl-api.html#clojure.repl/pst[clojure.repl/pst] - function. - -- `clojure.stacktrace` - Exceptions printed with the - https://clojure.github.io/clojure/branch-master/clojure.stacktrace-api.html#clojure.stacktrace/print-cause-trace[clojure.stacktrace/print-cause-trace] - function. - -- `Java` - Exceptions printed with the - https://docs.oracle.com/javase/8/docs/api/java/lang/Throwable.html#printStackTrace--[Throwable/printStackTrace] - method. - -- `Tagged Literal` - Exceptions printed with the - https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/pr[clojure.core/pr] - function. - -==== Limitations - -- Cider only recognizes stacktraces that have been printed in one of - the supported formats. - -- Stacktraces are analyzed with the classpath of the Cider session the - buffer is associated with. If the stacktrace contains references to - classes not on this classpath, some information might be missing - from the analysis. - -- The `cider-stacktrace-analyze-at-point` function might not detect - the stacktrace at point in every situation. The thing at point might - be different depending on which major mode is active in a - buffer. When `cider-stacktrace-analyze-at-point` fails to detect the - stacktrace, `cider-stacktrace-analyze-in-region` can be used to - select the stacktrace manually. - == Inspector integration -Within `*cider-error*`, when clicking directly a top-level exception (any of them in the cause chain), -that specific exception will be inspected with the CIDER xref:debugging/inspector.adoc[Inspector]. +Within `*cider-error*`, when clicking directly a top-level exception (any of +them in the cause chain), that specific exception will be inspected with the +CIDER xref:debugging/inspector.adoc[Inspector]. You can also click on the +rendered exception data to inspect it directly. -This allows you to better understand intrincate `ex-data`. +This clicking is defined and customizable in `cider-stacktrace-exception-map` +and `cider-stacktrace-ex-data-map`. -This clicking is defined and customizable in `cider-stacktrace-exception-map`, which has the following defaults: +image::cider-stacktrace-inspect.gif[Inspect the exceptions and ex-data] === Keybindings |=== | Action | Description -| kbd:[click] or kbd:[i] or kbd:[p] -| Open the given exception in the Inspector. +| kbd:[click] or kbd:[i] or kbd:[p] or kbd:[Return] +| Open the given exception or ex-data in the Inspector. |=== diff --git a/doc/modules/ROOT/pages/usage/misc_features.adoc b/doc/modules/ROOT/pages/usage/misc_features.adoc index dfceba105..f601992e6 100644 --- a/doc/modules/ROOT/pages/usage/misc_features.adoc +++ b/doc/modules/ROOT/pages/usage/misc_features.adoc @@ -94,9 +94,6 @@ Here's a list of all of ``cider-selector``'s keybindings: | kbd:[d] | `+*cider-doc*+` buffer. -| kbd:[p] -| `+*cider-profile*+` buffer. - | kbd:[s] | `+*cider-scratch*+` buffer. diff --git a/doc/modules/ROOT/pages/usage/working_with_documentation.adoc b/doc/modules/ROOT/pages/usage/working_with_documentation.adoc index fe72f33f5..72dc04654 100644 --- a/doc/modules/ROOT/pages/usage/working_with_documentation.adoc +++ b/doc/modules/ROOT/pages/usage/working_with_documentation.adoc @@ -17,11 +17,21 @@ as some people prefer to keep holding `Control` and some don't. Normally the command operates on the symbol at point. If invoked with a prefix argument, or no symbol is found at point, it will prompt for a symbol. -NOTE: If using `enrich-classpath`, Java doc comments are available and rendered in the same way that Clojure docstrings are. -They're often much more handy than opening Javadoc in a browser. Starting from CIDER 1.8.0, -the HTML-like language that they use is nicely rendered into syntax-colored strings, well-aligned tables, etc +== Local JavaDoc -== JavaDoc +Most JDK distributions ship with a `src.zip` file (an archive with all base Java source files). If you have such archive present in your JDK, CIDER will automatically parse the source file when you query the documentation for a Java class (e.g. `java.lang.Thread`) or a method (e.g. `java.lang.Thread/currentThread`) and will display the properly formatted JavaDoc in the documentation buffer. You will also see better Eldoc documentation (minibuffer hints) for Java methods. If the source file are present, you are able to jump to class or method definition by pressing kbd:[M-.] on the class name or method name. + +Furthermore, CIDER is able to parse JavaDoc source files and jump to definitions for third-party Java libraries if you have downloaded the special `-sources.jar` file for that library. See the next section on how to download source JARs. + +== Obtaining source JARs + +Since version 1.17, CIDER is able to download the necessary source JAR file automatically when you either request the documentation for a Java class/method or when you jump to the definition of a Java class/method. In order for the sources to be downloaded, you need to enable custom variable `cider-download-java-sources`. When the download triggers, CIDER displays a minibuffer message about that. Fetching a single source JAR usually takes a few seconds. CIDER will make only one attempt to download the source JAR for a particular dependency per process — if it failed to download (usually, because the dependency doesn't have a source JAR published to Maven), CIDER will not retry that until the next restart. + +NOTE: While Eldoc functionality benefits from having Java sources, the eldoc itself will not trigger the downloading of Java source JARs. You will have to lookup the documentation once manually or jump to the definition in order for the JAR is downloaded. After that, Eldoc will pick up the Java sources and display better hints. + +Alternatively, you can use https://github.com/clojure-emacs/enrich-classpath[`enrich-classpath`] to download all source JARs used by your current project at once. This will incur longer startup time, but will not trigger individual JARs fetching at the runtime. + +== Online JavaDoc CIDER provides a quick access to the online Javadoc documentation via the command `cider-javadoc` (kbd:[C-c C-d j] or kbd:[C-c C-d C-j]), using your default browser. diff --git a/nrepl-client.el b/nrepl-client.el index ed29fc8a1..f056ff8b2 100644 --- a/nrepl-client.el +++ b/nrepl-client.el @@ -1,7 +1,7 @@ ;;; nrepl-client.el --- Client for Clojure nREPL -*- lexical-binding: t -*- -;; Copyright © 2012-2024 Tim King, Phil Hagelberg, Bozhidar Batsov -;; Copyright © 2013-2024 Bozhidar Batsov, Artur Malabarba and CIDER contributors +;; Copyright © 2012-2025 Tim King, Phil Hagelberg, Bozhidar Batsov +;; Copyright © 2013-2025 Bozhidar Batsov, Artur Malabarba and CIDER contributors ;; ;; Author: Tim King ;; Phil Hagelberg @@ -86,19 +86,6 @@ :prefix "nrepl-" :group 'applications) -;; (defcustom nrepl-buffer-name-separator " " -;; "Used in constructing the REPL buffer name. -;; The `nrepl-buffer-name-separator' separates cider-repl from the project name." -;; :type '(string) -;; :group 'nrepl) -(make-obsolete-variable 'nrepl-buffer-name-separator 'cider-session-name-template "0.18") - -;; (defcustom nrepl-buffer-name-show-port nil -;; "Show the connection port in the nrepl REPL buffer name, if set to t." -;; :type 'boolean -;; :group 'nrepl) -(make-obsolete-variable 'nrepl-buffer-name-show-port 'cider-session-name-template "0.18") - (defcustom nrepl-connected-hook nil "List of functions to call when connecting to the nREPL server." :type 'hook) @@ -436,13 +423,30 @@ decoded message or nil if the strings were completely decoded." (erase-buffer) (cons string-q response-q)))) +(defun nrepl--bencode-dict (dict) + "Encode DICT with bencode. +According to the Bittorrent protocol specification[1], when bencoding +dictionaries, keys must be strings and appear in sorted order (sorted as +raw strings, not alphanumerics). + +[1] https://www.bittorrent.org/beps/bep_0003.html#bencoding" + (let* ((sorted-keys (sort (nrepl-dict-keys dict) + (lambda (a b) + (string< a b)))) + (sorted-dict (nrepl-dict))) + (dolist (k sorted-keys sorted-dict) + (nrepl-dict-put sorted-dict + k + (nrepl-dict-get dict k))) + (mapconcat #'nrepl-bencode (cdr sorted-dict) ""))) + (defun nrepl-bencode (object) "Encode OBJECT with bencode. Integers, lists and nrepl-dicts are treated according to bencode specification. Everything else is encoded as string." (cond ((integerp object) (format "i%de" object)) - ((nrepl-dict-p object) (format "d%se" (mapconcat #'nrepl-bencode (cdr object) ""))) + ((nrepl-dict-p object) (format "d%se" (nrepl--bencode-dict object))) ((listp object) (format "l%se" (mapconcat #'nrepl-bencode object ""))) (t (format "%s:%s" (string-bytes object) object)))) @@ -833,13 +837,13 @@ to the REPL." truncated-handler) "Make a response handler for connection BUFFER. A handler is a function that takes one argument - response received from -the server process. The response is an alist that contains at least 'id' -and 'session' keys. Other standard response keys are 'value', 'out', 'err', -and 'status'. +the server process. The response is an alist that contains at least `id' +and `session' keys. Other standard response keys are `value', `out', `err', +and `status'. The presence of a particular key determines the type of the response. For -example, if 'value' key is present, the response is of type 'value', if -'out' key is present the response is 'stdout' etc. +example, if `value' key is present, the response is of type `value', if +`out' key is present the response is `stdout' etc. Depending on the type, the handler dispatches the appropriate value to one of the supplied handlers: VALUE-HANDLER, STDOUT-HANDLER, STDERR-HANDLER, @@ -886,7 +890,7 @@ the corresponding type of response." (when (member "interrupted" status) (message "Evaluation interrupted.")) (when (member "eval-error" status) - (funcall (or eval-error-handler nrepl-err-handler))) + (funcall (or eval-error-handler nrepl-err-handler) buffer)) (when (member "namespace-not-found" status) (message "Namespace `%s' not found." ns)) (when (member "need-input" status) @@ -926,7 +930,7 @@ the standard session." (when-let* ((session (if tooling nrepl-tooling-session nrepl-session))) (setq request (append request `("session" ,session)))) (let* ((id (nrepl-next-request-id connection)) - (request (cons 'dict (lax-plist-put request "id" id))) + (request (cons 'dict (cider-plist-put request "id" id))) (message (nrepl-bencode request))) (nrepl-log-message request 'request) (puthash id callback nrepl-pending-requests) @@ -939,21 +943,30 @@ the standard session." (declare-function cider-repl-emit-interactive-stderr "cider-repl") (declare-function cider--render-stacktrace-causes "cider-eval") -(defun nrepl-send-sync-request (request connection &optional abort-on-input tooling) +(defun nrepl-send-sync-request (request connection &optional abort-on-input + tooling callback) "Send REQUEST to the nREPL server synchronously using CONNECTION. Hold till final \"done\" message has arrived and join all response messages of the same \"op\" that came along. If ABORT-ON-INPUT is non-nil, the function will return nil at the first sign of user input, so as not to hang the interface. -If TOOLING, use the tooling session rather than the standard session." +If TOOLING, use the tooling session rather than the standard session. + +If CALLBACK is non-nil, it will additionally be called on all received +messages. This shouldn't be used this for any control logic — use the +asynchronous `nrepl-send-request' directly for that. CALLBACK here should +be used to react to some intermediate events in an otherwise synchronous +command and e.g. notify the user about them." (let* ((time0 (current-time)) (response (cons 'dict nil)) (nrepl-ongoing-sync-request t) + (cb (lambda (resp) + ;; If caller has provided `callback', call it on the response. + (when callback + (funcall callback resp)) + (nrepl--merge response resp))) status) - (nrepl-send-request request - (lambda (resp) (nrepl--merge response resp)) - connection - tooling) + (nrepl-send-request request cb connection tooling) (while (and (not (member "done" status)) (not (and abort-on-input (input-pending-p)))) @@ -962,7 +975,7 @@ If TOOLING, use the tooling session rather than the standard session." ;; anywhere, and we'll just timeout. So we forward it to the user. (if (member "need-input" status) (progn (cider-need-input (current-buffer)) - ;; If the used took a few seconds to respond, we might + ;; If the user took a few seconds to respond, we might ;; unnecessarily timeout, so let's reset the timer. (setq time0 (current-time))) ;; break out in case we don't receive a response for a while @@ -1036,12 +1049,16 @@ ADDITIONAL-PARAMS is a plist to be appended to the request message." connection tooling)) +(defvar cider-version) + (defun nrepl-sync-request:clone (connection &optional tooling) "Sent a :clone request to create a new client session. The request is dispatched via CONNECTION. Optional argument TOOLING Tooling is set to t if wanting the tooling session from CONNECTION." - (nrepl-send-sync-request '("op" "clone") + (nrepl-send-sync-request `("op" "clone" + "client-name" "CIDER" + "client-version" ,cider-version) connection nil tooling)) @@ -1210,7 +1227,7 @@ up." (defun nrepl-server-sentinel (process event) "Handle nREPL server PROCESS EVENT. If the nREPL PROCESS failed to initiate and encountered a fatal EVENT -signal, raise an 'error'. Additionally, if the EVENT signal is SIGHUP, +signal, raise an `error'. Additionally, if the EVENT signal is SIGHUP, close any existing client connections." ;; only interested on fatal signals. (when (not (process-live-p process)) @@ -1307,8 +1324,8 @@ described by `nrepl-message-buffer-name-template'." ;; append a time-stamp to the message before logging it ;; the time-stamps are quite useful for debugging (setq msg (cons (car msg) - (lax-plist-put (cdr msg) "time-stamp" - (format-time-string "%Y-%m-%0d %H:%M:%S.%N")))) + (cider-plist-put (cdr msg) "time-stamp" + (format-time-string "%Y-%m-%0d %H:%M:%S.%N")))) (with-current-buffer (nrepl-messages-buffer (current-buffer)) (setq buffer-read-only nil) (when (> (buffer-size) nrepl-message-buffer-max-size) @@ -1317,7 +1334,7 @@ described by `nrepl-message-buffer-name-template'." (delete-region (point-min) (- (point) 1))) (goto-char (point-max)) (nrepl-log-pp-object (nrepl-decorate-msg msg type) - (nrepl-log--message-color (lax-plist-get (cdr msg) "id")) + (nrepl-log--message-color (cider-plist-get (cdr msg) "id")) t) (when-let* ((win (get-buffer-window))) (set-window-point win (point-max))) @@ -1502,8 +1519,6 @@ The default buffer name is *nrepl-error*." (set-window-point win (point-max))) (setq buffer-read-only t))) -(make-obsolete 'nrepl-default-client-buffer-builder nil "0.18") - (provide 'nrepl-client) ;;; nrepl-client.el ends here diff --git a/nrepl-dict.el b/nrepl-dict.el index f1846d473..bbf1b8f30 100644 --- a/nrepl-dict.el +++ b/nrepl-dict.el @@ -1,7 +1,7 @@ ;;; nrepl-dict.el --- Dictionary functions for Clojure nREPL -*- lexical-binding: t -*- -;; Copyright © 2012-2024 Tim King, Phil Hagelberg, Bozhidar Batsov -;; Copyright © 2013-2024 Bozhidar Batsov, Artur Malabarba and CIDER contributors +;; Copyright © 2012-2025 Tim King, Phil Hagelberg, Bozhidar Batsov +;; Copyright © 2013-2025 Bozhidar Batsov, Artur Malabarba and CIDER contributors ;; ;; Author: Tim King ;; Phil Hagelberg @@ -28,15 +28,19 @@ ;;; Commentary: ;; ;; Provides functions to interact with and create `nrepl-dict's. These are -;; simply plists with an extra element at the head. +;; simply plists with an extra element at the head, and using `equal' for +;; comparison of string keys. ;;; Code: (require 'cl-lib) +(require 'cider-util) (defun nrepl-dict (&rest key-vals) "Create nREPL dict from KEY-VALS." - (cons 'dict key-vals)) + (if (cl-evenp (length key-vals)) + (cons 'dict key-vals) + (error "An even number of KEY-VALS is needed to build a dict object"))) (defun nrepl-dict-from-hash (hash) "Create nREPL dict from HASH." @@ -44,12 +48,11 @@ (maphash (lambda (k v) (nrepl-dict-put dict k v)) hash) dict)) -(defun nrepl-dict-p (object) +(defsubst nrepl-dict-p (object) "Return t if OBJECT is an nREPL dict." - (and (listp object) - (eq (car object) 'dict))) + (eq (car-safe object) 'dict)) -(defun nrepl-dict-empty-p (dict) +(defsubst nrepl-dict-empty-p (dict) "Return t if nREPL dict DICT is empty." (null (cdr dict))) @@ -61,14 +64,23 @@ whose car is KEY. Comparison is done with `equal'." (member key (nrepl-dict-keys dict))) (defun nrepl-dict-get (dict key &optional default) - "Get from DICT value associated with KEY, optional DEFAULT if KEY not in DICT. -If dict is nil, return nil. If DEFAULT not provided, and KEY not in DICT, -return nil. If DICT is not an nREPL dict object, an error is thrown." + "Get from DICT value associated with KEY. +If DICT is nil, return nil. +If DICT is not an nREPL dict object, an error is thrown. + +If KEY is not in DICT, return DEFAULT (if provided). +Note that the use of DEFAULT is deprecated and will be +removed in a future release." + (declare (advertised-calling-convention (dict key) "1.16")) (when dict (if (nrepl-dict-p dict) - (if (nrepl-dict-contains dict key) - (lax-plist-get (cdr dict) key) - default) + ;; Note: The structure of the following expression avoids the + ;; expensive containment check in nearly all cases, see #3717 + (or (cider-plist-get (cdr dict) key) + ;; TODO: remove DEFAULT argument and the following clause + (when default + (and (not (nrepl-dict-contains dict key)) + default))) (error "Not an nREPL dict object: %s" dict)))) (defun nrepl-dict-put (dict key value) @@ -78,7 +90,7 @@ Return new dict. Dict is modified by side effects." `(dict ,key ,value) (if (not (nrepl-dict-p dict)) (error "Not an nREPL dict object: %s" dict) - (setcdr dict (lax-plist-put (cdr dict) key value)) + (setcdr dict (cider-plist-put (cdr dict) key value)) dict))) (defun nrepl-dict-keys (dict) @@ -165,6 +177,16 @@ FUNCTION should be a function taking two arguments, key and value." (cons obj (car stack))) (cdr stack)))) +(defun nrepl--alist-to-plist (maybe-alist) + "Transform MAYBE-ALIST into a plist if it is an alist. +Compatibility function for functions that used to accepts nrepl request +options as alists. A warning will be printed if alist is received." + (let ((first-arg (car-safe maybe-alist))) + (if (or (null first-arg) (not (listp first-arg))) + maybe-alist ;; It is a plist - don't have to convert + (warn "Received alist where it should have been plist: %s" maybe-alist) + (seq-mapcat #'identity maybe-alist)))) + (defun nrepl--merge (dict1 dict2 &optional no-join) "Join nREPL dicts DICT1 and DICT2 in a meaningful way. String values for non \"id\" and \"session\" keys are concatenated. Lists @@ -190,7 +212,7 @@ If NO-JOIN is given, return the first non nil dict." (t `(,dict1 ,dict2))))) -;;; Dbind +;;; Destructuring-bind of string keys (defmacro nrepl-dbind-response (response keys &rest body) "Destructure an nREPL RESPONSE dict. Bind the value of the provided KEYS and execute BODY." diff --git a/test/cider-apropos-tests.el b/test/cider-apropos-tests.el index c8114459b..0d3f04b41 100644 --- a/test/cider-apropos-tests.el +++ b/test/cider-apropos-tests.el @@ -1,6 +1,6 @@ ;;; cider-apropos-tests.el -*- lexical-binding: t; -*- -;; Copyright © 2012-2024 Tim King, Bozhidar Batsov +;; Copyright © 2012-2025 Tim King, Bozhidar Batsov ;; Author: Tim King ;; Bozhidar Batsov diff --git a/test/cider-browse-ns-tests.el b/test/cider-browse-ns-tests.el index 7fe5cf226..fa6c4a156 100644 --- a/test/cider-browse-ns-tests.el +++ b/test/cider-browse-ns-tests.el @@ -1,6 +1,6 @@ ;;; cider-browse-ns-tests.el -*- lexical-binding: t; -*- -;; Copyright © 2012-2024 Tim King, Bozhidar Batsov +;; Copyright © 2012-2025 Tim King, Bozhidar Batsov ;; Author: Tim King ;; Bozhidar Batsov diff --git a/test/cider-browse-spec-tests.el b/test/cider-browse-spec-tests.el index df359d792..58e66f382 100644 --- a/test/cider-browse-spec-tests.el +++ b/test/cider-browse-spec-tests.el @@ -1,6 +1,6 @@ ;;; cider-browse-spec-tests.el -*- lexical-binding: t; -*- -;; Copyright © 2012-2024 r0man, Bozhidar Batsov +;; Copyright © 2012-2025 r0man, Bozhidar Batsov ;; Author: r0man ;; Bozhidar Batsov diff --git a/test/cider-classpath-tests.el b/test/cider-classpath-tests.el index 99aaf9857..a72d73061 100644 --- a/test/cider-classpath-tests.el +++ b/test/cider-classpath-tests.el @@ -1,6 +1,6 @@ ;;; cider-classpath-tests.el -*- lexical-binding: t; -*- -;; Copyright © 2012-2024 Tim King, Bozhidar Batsov +;; Copyright © 2012-2025 Tim King, Bozhidar Batsov ;; Author: Tim King ;; Bozhidar Batsov diff --git a/test/cider-client-tests.el b/test/cider-client-tests.el index 99516cdd0..b83b2494d 100644 --- a/test/cider-client-tests.el +++ b/test/cider-client-tests.el @@ -1,6 +1,6 @@ ;;; cider-client-tests.el -*- lexical-binding: t; -*- -;; Copyright © 2012-2024 Tim King, Bozhidar Batsov +;; Copyright © 2012-2025 Tim King, Bozhidar Batsov ;; Author: Tim King ;; Bozhidar Batsov @@ -132,6 +132,17 @@ (expect (cider-ensure-op-supported "foo") :to-throw 'user-error))) +(describe "cider-ns-form-p" + (it "doesn't match ns in a string" + (let ((ns-in-string "\"\n(ns bar)\n\"")) + (expect (cider-ns-form-p ns-in-string) :to-equal nil))) + (it "matches ns" + (let ((ns "(ns bar)\n")) + (expect (cider-ns-form-p ns) :to-equal 0))) + (it "matches ns with leading spaces" + (let ((ns " (ns bar)\n")) + (expect (cider-ns-form-p ns) :to-equal 0)))) + (describe "cider-expected-ns" (before-each (spy-on 'cider-connected-p :and-return-value t) diff --git a/test/cider-clojuredocs-tests.el b/test/cider-clojuredocs-tests.el index bac2a4e2e..564dec73f 100644 --- a/test/cider-clojuredocs-tests.el +++ b/test/cider-clojuredocs-tests.el @@ -1,6 +1,6 @@ ;;; cider-clojuredocs-tests.el -*- lexical-binding: t; -*- -;; Copyright © 2012-2024 Tim King, Bozhidar Batsov +;; Copyright © 2012-2025 Tim King, Bozhidar Batsov ;; Author: Tim King ;; Bozhidar Batsov diff --git a/test/cider-common-tests.el b/test/cider-common-tests.el index 3d42e0a41..2f1666bf6 100644 --- a/test/cider-common-tests.el +++ b/test/cider-common-tests.el @@ -1,6 +1,6 @@ ;;; cider-common-tests.el --- -*- lexical-binding: t; -*- -;; Copyright © 2012-2024 Tim King, Bozhidar Batsov +;; Copyright © 2012-2025 Tim King, Bozhidar Batsov ;; Author: Tim King ;; Bozhidar Batsov diff --git a/test/cider-completion-context-tests.el b/test/cider-completion-context-tests.el index e1e970451..9bd40b0db 100644 --- a/test/cider-completion-context-tests.el +++ b/test/cider-completion-context-tests.el @@ -1,6 +1,6 @@ ;;; cider-completion-context-tests.el -*- lexical-binding: t; -*- -;; Copyright © 2012-2024 Bozhidar Batsov +;; Copyright © 2012-2025 Bozhidar Batsov ;; Author: Bozhidar Batsov diff --git a/test/cider-completion-tests.el b/test/cider-completion-tests.el index a8f58f798..f8368c39d 100644 --- a/test/cider-completion-tests.el +++ b/test/cider-completion-tests.el @@ -1,6 +1,6 @@ ;;; cider-completion-tests.el -*- lexical-binding: t; -*- -;; Copyright © 2012-2024 Bozhidar Batsov +;; Copyright © 2012-2025 Bozhidar Batsov ;; Author: Bozhidar Batsov diff --git a/test/cider-connection-tests.el b/test/cider-connection-tests.el index 37875eddc..60a6765e2 100644 --- a/test/cider-connection-tests.el +++ b/test/cider-connection-tests.el @@ -1,7 +1,7 @@ ;; -*- lexical-binding: t; -*- ;;; cider-connection-tests.el -;; Copyright © 2012-2024 Tim King, Bozhidar Batsov, Vitalie Spinu +;; Copyright © 2012-2025 Tim King, Bozhidar Batsov, Vitalie Spinu ;; Author: Tim King ;; Bozhidar Batsov diff --git a/test/cider-debug-tests.el b/test/cider-debug-tests.el index 2bb5d6e60..ecdfe553a 100644 --- a/test/cider-debug-tests.el +++ b/test/cider-debug-tests.el @@ -1,6 +1,6 @@ ;;; cider-debug-tests.el -*- lexical-binding: t; -*- -;; Copyright © 2012-2024 Tim King, Bozhidar Batsov +;; Copyright © 2012-2025 Tim King, Bozhidar Batsov ;; Author: Tim King ;; Bozhidar Batsov diff --git a/test/cider-doc-tests.el b/test/cider-doc-tests.el index bc272dc08..6abae8137 100644 --- a/test/cider-doc-tests.el +++ b/test/cider-doc-tests.el @@ -1,6 +1,6 @@ ;;; cider-doc-tests.el -*- lexical-binding: t; -*- -;; Copyright © 2023-2024 Bozhidar Batsov +;; Copyright © 2023-2025 Bozhidar Batsov ;; Author: Bozhidar Batsov diff --git a/test/cider-eldoc-tests.el b/test/cider-eldoc-tests.el index 5611df447..143be9baf 100644 --- a/test/cider-eldoc-tests.el +++ b/test/cider-eldoc-tests.el @@ -1,6 +1,6 @@ ;;; cider-eldoc-tests.el -*- lexical-binding: t; -*- -;; Copyright © 2012-2024 Tim King, Bozhidar Batsov +;; Copyright © 2012-2025 Tim King, Bozhidar Batsov ;; Author: Tim King ;; Bozhidar Batsov diff --git a/test/cider-error-parsing-tests.el b/test/cider-error-parsing-tests.el index a4361ac3f..cc5e624a7 100644 --- a/test/cider-error-parsing-tests.el +++ b/test/cider-error-parsing-tests.el @@ -1,6 +1,6 @@ ;;; cider-error-parsing-tests.el -*- lexical-binding: t; -*- -;; Copyright © 2012-2024 Tim King, Bozhidar Batsov +;; Copyright © 2012-2025 Tim King, Bozhidar Batsov ;; Author: Tim King ;; Bozhidar Batsov @@ -125,7 +125,7 @@ (match-string 1 clojure-compiler-warning)) :to-equal "warning"))) ;; FIXME: duplicate spec names - (dolist (regexp (list cider-clojure-compilation-regexp cider-clojure-compilation-error-regexp)) + (let ((regexp cider-clojure-compilation-regexp)) (it "Recognizes a clojure-1.10 error message" (let ((clojure-1.10-compiler-error "Syntax error compiling at (src/ardoq/service/workspace_service.clj:227:3).")) (expect clojure-1.10-compiler-error :to-match regexp) @@ -139,65 +139,6 @@ (match-string 2 clojure-1.10-compiler-error)) :to-equal "src/haystack/parser.cljc"))))) -(describe "cider-clojure-runtime-error-regexp" - (it "Recognizes a clojure-1.10 runtime error message" - - ;; Something like "(ArithmeticException)" will be absent for Exception and RuntimeException in particular - (let ((specimen "Execution error at foo/foo (src/haystack/parser.cljc:4).")) - (expect specimen :to-match cider-clojure-runtime-error-regexp) - (expect (progn - (string-match cider-clojure-runtime-error-regexp specimen) - (match-string 2 specimen)) - :to-equal "src/haystack/parser.cljc")) - - (let ((specimen "Execution error (ArithmeticException) at foo/foo (src/haystack/parser.cljc:4).")) - (expect specimen :to-match cider-clojure-runtime-error-regexp) - (expect (progn - (string-match cider-clojure-runtime-error-regexp specimen) - (match-string 2 specimen)) - :to-equal "src/haystack/parser.cljc")) - - ;; without exception class cause-type - (let ((specimen "Execution error at (src/haystack/parser.cljc:4).")) - (expect specimen :to-match cider-clojure-runtime-error-regexp) - (expect (progn - (string-match cider-clojure-runtime-error-regexp specimen) - (match-string 2 specimen)) - :to-equal "src/haystack/parser.cljc")) - - ;; without foo/foo symbol - (let ((specimen "Execution error (ArithmeticException) at (src/haystack/parser.cljc:4).")) - (expect specimen :to-match cider-clojure-runtime-error-regexp) - (expect (progn - (string-match cider-clojure-runtime-error-regexp specimen) - (match-string 2 specimen)) - :to-equal "src/haystack/parser.cljc"))) - - (it "Recognizes a clojure-1.10 runtime spec validation error message" - (let ((specimen "Execution error - invalid arguments to foo/bar at (src/haystack/parser.cljc:4).")) - (expect specimen :to-match cider-clojure-runtime-error-regexp) - (expect (progn - (string-match cider-clojure-runtime-error-regexp specimen) - (match-string 2 specimen)) - :to-equal "src/haystack/parser.cljc"))) - - ;; Java source locations may be negative (#3687) - (it "Recognizes an error thrown from a java source file" - (let ((specimen "Execution error (FileNotFoundException) at java.io.FileInputStream/open0 (FileInputStream.java:-2).")) - (expect specimen :to-match cider-clojure-runtime-error-regexp) - (expect (progn - (string-match cider-clojure-runtime-error-regexp specimen) - (match-string 2 specimen)) - :to-equal "FileInputStream.java"))) - - (it "Recognizes errors thrown during the result printing phase" - (let ((specimen "Error printing return value (ClassCastException) at clojure.core/file-seq$fn (core.clj:4997).")) - (expect specimen :to-match cider-clojure-runtime-error-regexp) - (expect (progn - (string-match cider-clojure-runtime-error-regexp specimen) - (match-string 2 specimen)) - :to-equal "core.clj")))) - (describe "cider-module-info-regexp" (it "Matches module info provided by Java" (expect " (java.lang.Long is in module java.base of loader 'bootstrap'; clojure.lang.IObj is in unnamed module of loader 'app')" diff --git a/test/cider-eval-tests.el b/test/cider-eval-tests.el index 113012d08..8f638e664 100644 --- a/test/cider-eval-tests.el +++ b/test/cider-eval-tests.el @@ -1,6 +1,6 @@ ;;; cider-eval-tests.el -*- lexical-binding: t; -*- -;; Copyright © 2012-2024 Arne Brasseur +;; Copyright © 2012-2025 Arne Brasseur ;; Author: Arne Brasseur @@ -30,26 +30,6 @@ ;; Please, for each `describe', ensure there's an `it' block, so that its execution is visible in CI. -(describe "cider-provide-file" - (let ((tmp-dir (temporary-file-directory))) - (it "returns an empty string when the file is not found" - (expect (cider-provide-file "abc.clj") :to-equal "")) - (it "base64 encodes without newlines" - (let ((cider-sideloader-path (list tmp-dir)) - (default-directory tmp-dir) - (filename (make-temp-file "abc.clj"))) - (with-temp-file filename - (dotimes (_ 60) (insert "x"))) - (expect (cider-provide-file filename) :not :to-match "\n"))) - (it "can handle multibyte characters" - (let ((cider-sideloader-path (list tmp-dir)) - (default-directory tmp-dir) - (filename (make-temp-file "abc.clj")) - (coding-system-for-write 'utf-8-unix)) - (with-temp-file filename - (insert "🍻")) - (expect (cider-provide-file filename) :to-equal "8J+Nuw=="))))) - (describe "cider-extract-error-info" (it "Matches Clojure compilation exceptions" (expect (cider-extract-error-info cider-compilation-regexp "Syntax error compiling clojure.core/let at (src/haystack/analyzer.clj:18:1).\n[1] - failed: even-number-of-forms? at: [:bindings] spec: :clojure.core.specs.alpha/bindings\n") diff --git a/test/cider-find-tests.el b/test/cider-find-tests.el index 465dfc5dc..96a5fbf37 100644 --- a/test/cider-find-tests.el +++ b/test/cider-find-tests.el @@ -1,6 +1,6 @@ ;;; cider-find-tests.el -*- lexical-binding: t; -*- -;; Copyright © 2012-2024 Bozhidar Batsov +;; Copyright © 2012-2025 Bozhidar Batsov ;; Author: Bozhidar Batsov diff --git a/test/cider-inspector-tests.el b/test/cider-inspector-tests.el index 0d2060de5..a2c2b1106 100644 --- a/test/cider-inspector-tests.el +++ b/test/cider-inspector-tests.el @@ -1,6 +1,6 @@ ;;; cider-inspectors-tests.el -*- lexical-binding: t; -*- -;; Copyright © 2012-2024 Bozhidar Batsov +;; Copyright © 2012-2025 Bozhidar Batsov ;; Author: Bozhidar Batsov diff --git a/test/cider-interaction-tests.el b/test/cider-interaction-tests.el index f511267c8..ad9f30edc 100644 --- a/test/cider-interaction-tests.el +++ b/test/cider-interaction-tests.el @@ -1,6 +1,6 @@ ;;; cider-eval-tests.el -*- lexical-binding: t; -*- -;; Copyright © 2012-2024 Tim King, Bozhidar Batsov +;; Copyright © 2012-2025 Tim King, Bozhidar Batsov ;; Author: Tim King ;; Bozhidar Batsov diff --git a/test/cider-jar-tests.el b/test/cider-jar-tests.el index faf173109..0892eba3c 100644 --- a/test/cider-jar-tests.el +++ b/test/cider-jar-tests.el @@ -1,6 +1,6 @@ ;;; cider-jar-tests.el -*- lexical-binding: t; -*- -;; Copyright © 2012-2024 Arne Brasseur +;; Copyright © 2012-2025 Arne Brasseur ;; Author: Arne Brasseur diff --git a/test/cider-log-tests.el b/test/cider-log-tests.el index 200874c60..e83eafee1 100644 --- a/test/cider-log-tests.el +++ b/test/cider-log-tests.el @@ -1,6 +1,6 @@ ;;; cider-log-tests.el -*- lexical-binding: t; -*- -;; Copyright © 2023-2024 Bozhidar Batsov and CIDER contributors +;; Copyright © 2023-2025 Bozhidar Batsov and CIDER contributors ;; Author: r0man diff --git a/test/cider-ns-tests.el b/test/cider-ns-tests.el index ab98499be..2bfedf333 100644 --- a/test/cider-ns-tests.el +++ b/test/cider-ns-tests.el @@ -1,6 +1,6 @@ ;;; cider-ns-tests.el -*- lexical-binding: t; -*- -;; Copyright © 2019-2024 Bozhidar Batsov +;; Copyright © 2019-2025 Bozhidar Batsov ;; Author: Bozhidar Batsov diff --git a/test/cider-overlay-tests.el b/test/cider-overlay-tests.el index 362e472ed..f7102ecd0 100644 --- a/test/cider-overlay-tests.el +++ b/test/cider-overlay-tests.el @@ -1,6 +1,6 @@ ;;; cider-overlay-tests.el -*- lexical-binding: t; -*- -;; Copyright © 2015-2024 Bozhidar Batsov, Artur Malabarba and CIDER contributors +;; Copyright © 2015-2025 Bozhidar Batsov, Artur Malabarba and CIDER contributors ;; Author: Artur Malabarba diff --git a/test/cider-repl-tests.el b/test/cider-repl-tests.el index 9d7535d30..250c32181 100644 --- a/test/cider-repl-tests.el +++ b/test/cider-repl-tests.el @@ -1,6 +1,6 @@ ;;; cider-repl-tests.el -*- lexical-binding: t; -*- -;; Copyright © 2012-2024 Tim King, Bozhidar Batsov +;; Copyright © 2012-2025 Tim King, Bozhidar Batsov ;; Author: Tim King ;; Bozhidar Batsov diff --git a/test/cider-selector-tests.el b/test/cider-selector-tests.el index 76243e68f..ed579895c 100644 --- a/test/cider-selector-tests.el +++ b/test/cider-selector-tests.el @@ -1,6 +1,6 @@ ;;; cider-selector-tests.el -*- lexical-binding: t; -*- -;; Copyright © 2012-2024 Tim King, Bozhidar Batsov +;; Copyright © 2012-2025 Tim King, Bozhidar Batsov ;; Author: Tim King ;; Bozhidar Batsov diff --git a/test/cider-stacktrace-tests.el b/test/cider-stacktrace-tests.el index d531a32df..34ba01416 100644 --- a/test/cider-stacktrace-tests.el +++ b/test/cider-stacktrace-tests.el @@ -1,6 +1,6 @@ ;;; cider-stacktrace-tests.el -*- lexical-binding: t; -*- -;; Copyright © 2012-2024 Tim King, Bozhidar Batsov +;; Copyright © 2012-2025 Tim King, Bozhidar Batsov ;; Author: Tim King ;; Bozhidar Batsov @@ -257,57 +257,3 @@ :to-be-truthy) (expect (or both shown1 shown2) :to-be nil)))) - -(defun cider-stacktrace-tests--analyze-at-point (stacktrace pos) - "Test `cider-stacktrace-analyze-at-point' with STACKTRACE at POS." - (with-temp-buffer - (erase-buffer) - (insert stacktrace) - (goto-char pos) - (cider-stacktrace-analyze-at-point))) - -(describe "cider-stacktrace-analyze-at-point" - :var (cider-stacktrace-analyze-string) - (before-each (spy-on 'cider-stacktrace-analyze-string)) - - (it "should analyze the Aviso stacktrace with point at beginning" - (cider-stacktrace-tests--analyze-at-point cider-stacktrace-tests-boom-aviso 0) - (expect 'cider-stacktrace-analyze-string :to-have-been-called-with cider-stacktrace-tests-boom-aviso)) - - (it "should analyze the Clojure stacktrace with point at beginning" - (cider-stacktrace-tests--analyze-at-point cider-stacktrace-tests-boom-clojure 0) - (expect 'cider-stacktrace-analyze-string :to-have-been-called-with cider-stacktrace-tests-boom-clojure)) - - (it "should analyze the Java stacktrace with point at beginning" - (cider-stacktrace-tests--analyze-at-point cider-stacktrace-tests-boom-java 0) - (expect 'cider-stacktrace-analyze-string :to-have-been-called-with cider-stacktrace-tests-boom-java)) - - (it "should analyze the Clojure stacktrace with point inside" - (cider-stacktrace-tests--analyze-at-point cider-stacktrace-tests-boom-clojure 10) - (expect 'cider-stacktrace-analyze-string :to-have-been-called-with cider-stacktrace-tests-boom-clojure)) - - (it "should analyze the Java stacktrace with point inside" - (cider-stacktrace-tests--analyze-at-point cider-stacktrace-tests-boom-java 10) - (expect 'cider-stacktrace-analyze-string :to-have-been-called-with cider-stacktrace-tests-boom-java))) - -(defun cider-stacktrace-tests--analyze-in-region (stacktrace) - "Test `cider-stacktrace-analyze-in-region' with STACKTRACE." - (with-temp-buffer - (insert stacktrace) - (cider-stacktrace-analyze-in-region (point-min) (point-max)))) - -(describe "cider-stacktrace-analyze-in-region" - :var (cider-stacktrace-analyze-string) - (before-each (spy-on 'cider-stacktrace-analyze-string)) - - (it "should analyze the Aviso stacktrace in region" - (cider-stacktrace-tests--analyze-in-region cider-stacktrace-tests-boom-aviso) - (expect 'cider-stacktrace-analyze-string :to-have-been-called-with cider-stacktrace-tests-boom-aviso)) - - (it "should analyze the Clojure stacktrace in region" - (cider-stacktrace-tests--analyze-in-region cider-stacktrace-tests-boom-clojure) - (expect 'cider-stacktrace-analyze-string :to-have-been-called-with cider-stacktrace-tests-boom-clojure)) - - (it "should analyze the Java stacktrace in region" - (cider-stacktrace-tests--analyze-in-region cider-stacktrace-tests-boom-java) - (expect 'cider-stacktrace-analyze-string :to-have-been-called-with cider-stacktrace-tests-boom-java))) diff --git a/test/cider-test-tests.el b/test/cider-test-tests.el index 4e636407f..a090b8a06 100644 --- a/test/cider-test-tests.el +++ b/test/cider-test-tests.el @@ -1,6 +1,6 @@ ;;; cider-test-tests.el -*- lexical-binding: t; -*- -;; Copyright © 2023-2024 Bozhidar Batsov +;; Copyright © 2023-2025 Bozhidar Batsov ;; Author: Bozhidar Batsov diff --git a/test/cider-tests--no-auto.el b/test/cider-tests--no-auto.el index 9a5851a95..53961d5fa 100644 --- a/test/cider-tests--no-auto.el +++ b/test/cider-tests--no-auto.el @@ -1,6 +1,6 @@ ;;; cider-tests--no-auto.el --- Non-automated tests -*- lexical-binding: t -*- -;; Copyright © 2014-2024 Jeff Valk, Bozhidar Batsov and CIDER contributors +;; Copyright © 2014-2025 Jeff Valk, Bozhidar Batsov and CIDER contributors ;; Author: Jeff Valk diff --git a/test/cider-tests.el b/test/cider-tests.el index b61669491..5cac4d59b 100644 --- a/test/cider-tests.el +++ b/test/cider-tests.el @@ -1,6 +1,6 @@ ;;; cider-tests.el -*- lexical-binding: t; -*- -;; Copyright © 2012-2024 Tim King, Bozhidar Batsov +;; Copyright © 2012-2025 Tim King, Bozhidar Batsov ;; Author: Tim King ;; Bozhidar Batsov @@ -144,7 +144,7 @@ (describe "when there is a single dependency" (before-each (setq-local cider-injected-nrepl-version "0.9.0") - (setq-local cider-injected-middleware-version "0.49.0") + (setq-local cider-injected-middleware-version "0.55.7") (setq-local cider-jack-in-nrepl-middlewares '("cider.nrepl/cider-middleware")) (setq-local cider-jack-in-dependencies-exclusions '()) (setq-local cider-enrich-classpath t) @@ -155,10 +155,10 @@ :to-equal (concat "update-in :dependencies conj " (shell-quote-argument "[nrepl/nrepl \"0.9.0\"]") " -- update-in :plugins conj " - (shell-quote-argument "[cider/cider-nrepl \"0.49.0\"]") + (shell-quote-argument "[cider/cider-nrepl \"0.55.7\"]") " -- update-in :plugins conj " (shell-quote-argument "[mx.cider/lein-enrich-classpath \"1.19.3\"]") - " -- update-in :jvm-opts conj -Djdk.attach.allowAttachSelf" + " -- update-in :jvm-opts conj '\"-Djdk.attach.allowAttachSelf\"'" " -- update-in :middleware conj cider.enrich-classpath.plugin-v2/middleware" " -- repl :headless"))) @@ -169,10 +169,10 @@ "update-in :dependencies conj " (shell-quote-argument "[nrepl/nrepl \"0.9.0\" :exclusions [org.clojure/clojure]]") " -- update-in :plugins conj " - (shell-quote-argument "[cider/cider-nrepl \"0.49.0\"]") + (shell-quote-argument "[cider/cider-nrepl \"0.55.7\"]") " -- update-in :plugins conj " (shell-quote-argument "[mx.cider/lein-enrich-classpath \"1.19.3\"]") - " -- update-in :jvm-opts conj -Djdk.attach.allowAttachSelf" + " -- update-in :jvm-opts conj '\"-Djdk.attach.allowAttachSelf\"'" " -- update-in :middleware conj cider.enrich-classpath.plugin-v2/middleware" " -- repl :headless"))) @@ -182,31 +182,18 @@ :to-equal (concat "update-in :dependencies conj " (shell-quote-argument "[nrepl/nrepl \"0.9.0\" :exclusions [org.clojure/clojure foo.bar/baz]]") " -- update-in :plugins conj " - (shell-quote-argument "[cider/cider-nrepl \"0.49.0\"]") + (shell-quote-argument "[cider/cider-nrepl \"0.55.7\"]") " -- update-in :plugins conj " (shell-quote-argument "[mx.cider/lein-enrich-classpath \"1.19.3\"]") - " -- update-in :jvm-opts conj -Djdk.attach.allowAttachSelf" + " -- update-in :jvm-opts conj '\"-Djdk.attach.allowAttachSelf\"'" " -- update-in :middleware conj cider.enrich-classpath.plugin-v2/middleware" " -- repl :headless"))) - (it "can inject dependencies in a boot project" - (expect (cider-inject-jack-in-dependencies "" "repl -s wait" 'boot) - :to-equal (concat - "-i \"(require 'cider.tasks)\"" - " -d " - (shell-quote-argument "nrepl/nrepl:0.9.0") - " -d " - (shell-quote-argument "cider/cider-nrepl:0.49.0") - " cider.tasks/add-middleware" - " -m " - (shell-quote-argument "cider.nrepl/cider-middleware") - " repl -s wait"))) - (it "can inject dependencies in a gradle project" (expect (cider-inject-jack-in-dependencies "--no-daemon" ":clojureRepl" 'gradle) :to-equal (concat "--no-daemon " "-Pjdk.attach.allowAttachSelf " - (shell-quote-argument "-Pdev.clojurephant.jack-in.nrepl=nrepl:nrepl:0.9.0,cider:cider-nrepl:0.49.0") + (shell-quote-argument "-Pdev.clojurephant.jack-in.nrepl=nrepl:nrepl:0.9.0,cider:cider-nrepl:0.55.7") " :clojureRepl " (shell-quote-argument "--middleware=cider.nrepl/cider-middleware"))))) @@ -223,29 +210,12 @@ " -- update-in :plugins conj " (shell-quote-argument "[refactor-nrepl \"2.0.0\"]") " -- update-in :plugins conj " - (shell-quote-argument "[cider/cider-nrepl \"0.49.0\"]") + (shell-quote-argument "[cider/cider-nrepl \"0.55.7\"]") " -- update-in :plugins conj " (shell-quote-argument "[mx.cider/lein-enrich-classpath \"1.19.3\"]") - " -- update-in :jvm-opts conj -Djdk.attach.allowAttachSelf" + " -- update-in :jvm-opts conj '\"-Djdk.attach.allowAttachSelf\"'" " -- update-in :middleware conj cider.enrich-classpath.plugin-v2/middleware" - " -- repl :headless"))) - - (it "can inject dependencies in a boot project" - (setq-local cider-jack-in-dependencies '(("refactor-nrepl" "2.0.0"))) - (expect (cider-inject-jack-in-dependencies "" "repl -s wait" 'boot) - :to-equal (concat "-i \"(require 'cider.tasks)\"" - " -d " - (shell-quote-argument "nrepl/nrepl:0.9.0") - " -d " - (shell-quote-argument "cider/cider-nrepl:0.49.0") - " -d " - (shell-quote-argument "refactor-nrepl:2.0.0") - " cider.tasks/add-middleware" - " -m " - (shell-quote-argument "refactor-nrepl.middleware/wrap-refactor") - " -m " - (shell-quote-argument "cider.nrepl/cider-middleware") - " repl -s wait")))) + " -- repl :headless")))) (describe "when there are global options" (before-each @@ -259,28 +229,17 @@ :to-equal (concat "-o -U update-in :dependencies conj " (shell-quote-argument "[nrepl/nrepl \"0.9.0\"]") " -- update-in :plugins conj " - (shell-quote-argument "[cider/cider-nrepl \"0.49.0\"]") + (shell-quote-argument "[cider/cider-nrepl \"0.55.7\"]") " -- update-in :plugins conj " (shell-quote-argument "[mx.cider/lein-enrich-classpath \"1.19.3\"]") - " -- update-in :jvm-opts conj -Djdk.attach.allowAttachSelf" + " -- update-in :jvm-opts conj '\"-Djdk.attach.allowAttachSelf\"'" " -- update-in :middleware conj cider.enrich-classpath.plugin-v2/middleware" " -- repl :headless"))) - (it "can concat in a boot project" - (expect (cider-inject-jack-in-dependencies "-C -o" "repl -s wait" 'boot) - :to-equal (concat "-C -o -i \"(require 'cider.tasks)\"" - " -d " - (shell-quote-argument "nrepl/nrepl:0.9.0") - " -d " - (shell-quote-argument "cider/cider-nrepl:0.49.0") - " cider.tasks/add-middleware" - " -m " - (shell-quote-argument "cider.nrepl/cider-middleware") - " repl -s wait"))) (it "can concat in a gradle project" (expect (cider-inject-jack-in-dependencies "--no-daemon" ":clojureRepl" 'gradle) :to-equal (concat "--no-daemon " "-Pjdk.attach.allowAttachSelf " - (shell-quote-argument "-Pdev.clojurephant.jack-in.nrepl=nrepl:nrepl:0.9.0,cider:cider-nrepl:0.49.0") + (shell-quote-argument "-Pdev.clojurephant.jack-in.nrepl=nrepl:nrepl:0.9.0,cider:cider-nrepl:0.55.7") " :clojureRepl " (shell-quote-argument "--middleware=cider.nrepl/cider-middleware"))))) @@ -295,14 +254,14 @@ (setq-local cider-jack-in-nrepl-middlewares '(("refactor-nrepl.middleware/wrap-refactor" :predicate middlewares-predicate) "cider.nrepl/cider-middleware" ("another/middleware")))) (it "includes plugins whose predicates return true" (expect (cider-jack-in-normalized-lein-plugins) - :to-equal '(("refactor-nrepl" "2.0.0") ("cider/cider-nrepl" "0.49.0")))) + :to-equal '(("refactor-nrepl" "2.0.0") ("cider/cider-nrepl" "0.55.7")))) (it "includes middlewares whose predicates return true" (expect (cider-jack-in-normalized-nrepl-middlewares) :to-equal '("refactor-nrepl.middleware/wrap-refactor" "cider.nrepl/cider-middleware" "another/middleware"))) (it "ignores plugins whose predicates return false" (spy-on 'plugins-predicate :and-return-value nil) (expect (cider-jack-in-normalized-lein-plugins) - :to-equal '(("cider/cider-nrepl" "0.49.0"))) + :to-equal '(("cider/cider-nrepl" "0.55.7"))) (spy-on 'middlewares-predicate :and-return-value nil) (expect (cider-jack-in-normalized-nrepl-middlewares) :to-equal '("cider.nrepl/cider-middleware" "another/middleware"))) @@ -331,7 +290,7 @@ :and-return-value '("refactor-nrepl.middleware/wrap-refactor" "cider.nrepl/cider-middleware")) (spy-on 'cider-jack-in-normalized-lein-plugins :and-return-value '(("refactor-nrepl" "2.0.0") - ("cider/cider-nrepl" "0.49.0") + ("cider/cider-nrepl" "0.55.7") ("mx.cider/lein-enrich-classpath" "1.19.3"))) (setq-local cider-jack-in-dependencies-exclusions '()) (setq-local cider-enrich-classpath t)) @@ -342,34 +301,12 @@ " -- update-in :plugins conj " (shell-quote-argument "[refactor-nrepl \"2.0.0\"]") " -- update-in :plugins conj " - (shell-quote-argument "[cider/cider-nrepl \"0.49.0\"]") + (shell-quote-argument "[cider/cider-nrepl \"0.55.7\"]") " -- update-in :plugins conj " (shell-quote-argument "[mx.cider/lein-enrich-classpath \"1.19.3\"]") - " -- update-in :jvm-opts conj -Djdk.attach.allowAttachSelf" + " -- update-in :jvm-opts conj '\"-Djdk.attach.allowAttachSelf\"'" " -- update-in :middleware conj cider.enrich-classpath.plugin-v2/middleware" - " -- repl :headless")))) - - (describe "when the middleware lists have been normalized (Boot)" - (before-each - (spy-on 'cider-jack-in-normalized-nrepl-middlewares - :and-return-value '("refactor-nrepl.middleware/wrap-refactor" "cider.nrepl/cider-middleware")) - (setq-local cider-jack-in-dependencies '(("refactor-nrepl" "2.0.0"))) - (setq-local cider-jack-in-dependencies-exclusions '())) - (it "uses them in a boot project" - (expect (cider-inject-jack-in-dependencies "" "repl -s wait" 'boot) - :to-equal (concat "-i \"(require 'cider.tasks)\"" - " -d " - (shell-quote-argument "nrepl/nrepl:0.9.0") - " -d " - (shell-quote-argument "cider/cider-nrepl:0.49.0") - " -d " - (shell-quote-argument "refactor-nrepl:2.0.0") - " cider.tasks/add-middleware" - " -m " - (shell-quote-argument "refactor-nrepl.middleware/wrap-refactor") - " -m " - (shell-quote-argument "cider.nrepl/cider-middleware") - " repl -s wait"))))) + " -- repl :headless"))))) (describe "cider-jack-in-auto-inject-clojure" (it "injects `cider-minimum-clojure-version' when `cider-jack-in-auto-inject-clojure' is set to minimal" @@ -456,7 +393,7 @@ (setq-local cider-jack-in-dependencies nil) (setq-local cider-jack-in-nrepl-middlewares '("cider.nrepl/cider-middleware")) (let ((expected (string-join `("clojure -Sdeps " - ,(shell-quote-argument "{:deps {nrepl/nrepl {:mvn/version \"0.9.0\"} cider/cider-nrepl {:mvn/version \"0.49.0\"}} :aliases {:cider/nrepl {:jvm-opts [\"-Djdk.attach.allowAttachSelf\"], :main-opts [\"-m\" \"nrepl.cmdline\" \"--middleware\" \"[cider.nrepl/cider-middleware]\"]}}}") + ,(shell-quote-argument "{:deps {nrepl/nrepl {:mvn/version \"0.9.0\"} cider/cider-nrepl {:mvn/version \"0.55.7\"}} :aliases {:cider/nrepl {:jvm-opts [\"-Djdk.attach.allowAttachSelf\"], :main-opts [\"-m\" \"nrepl.cmdline\" \"--middleware\" \"[cider.nrepl/cider-middleware]\"]}}}") " -M:cider/nrepl") ""))) (setq-local cider-allow-jack-in-without-project t) @@ -471,7 +408,7 @@ (it "allows specifying custom aliases with `cider-clojure-cli-aliases`" (let ((expected (string-join `("clojure -Sdeps " - ,(shell-quote-argument "{:deps {nrepl/nrepl {:mvn/version \"0.9.0\"} cider/cider-nrepl {:mvn/version \"0.49.0\"}} :aliases {:cider/nrepl {:jvm-opts [\"-Djdk.attach.allowAttachSelf\"], :main-opts [\"-m\" \"nrepl.cmdline\" \"--middleware\" \"[cider.nrepl/cider-middleware]\"]}}}") + ,(shell-quote-argument "{:deps {nrepl/nrepl {:mvn/version \"0.9.0\"} cider/cider-nrepl {:mvn/version \"0.55.7\"}} :aliases {:cider/nrepl {:jvm-opts [\"-Djdk.attach.allowAttachSelf\"], :main-opts [\"-m\" \"nrepl.cmdline\" \"--middleware\" \"[cider.nrepl/cider-middleware]\"]}}}") " -M:dev:test:cider/nrepl") ""))) (setq-local cider-jack-in-dependencies nil) @@ -489,7 +426,7 @@ (it (format "should remove duplicates, yielding the same result (for %S command invocation)" command) ;; repeat the same test for PowerShell too (let ((expected (string-join `("-Sdeps " - ,(cider--shell-quote-argument "{:deps {cider/cider-nrepl {:mvn/version \"0.49.0\"} nrepl/nrepl {:mvn/version \"0.9.0\"}} :aliases {:cider/nrepl {:jvm-opts [\"-Djdk.attach.allowAttachSelf\"], :main-opts [\"-m\" \"nrepl.cmdline\" \"--middleware\" \"[cider.nrepl/cider-middleware]\"]}}}" + ,(cider--shell-quote-argument "{:deps {cider/cider-nrepl {:mvn/version \"0.55.7\"} nrepl/nrepl {:mvn/version \"0.9.0\"}} :aliases {:cider/nrepl {:jvm-opts [\"-Djdk.attach.allowAttachSelf\"], :main-opts [\"-m\" \"nrepl.cmdline\" \"--middleware\" \"[cider.nrepl/cider-middleware]\"]}}}" command) " -M:dev:test:cider/nrepl") ""))) @@ -499,7 +436,7 @@ :to-equal expected)))) (it "handles aliases correctly" (let ((expected (string-join `("-Sdeps " - ,(shell-quote-argument "{:deps {cider/cider-nrepl {:mvn/version \"0.49.0\"} nrepl/nrepl {:mvn/version \"0.9.0\"}} :aliases {:cider/nrepl {:jvm-opts [\"-Djdk.attach.allowAttachSelf\"], :main-opts [\"-m\" \"nrepl.cmdline\" \"--middleware\" \"[cider.nrepl/cider-middleware]\"]}}}") + ,(shell-quote-argument "{:deps {cider/cider-nrepl {:mvn/version \"0.55.7\"} nrepl/nrepl {:mvn/version \"0.9.0\"}} :aliases {:cider/nrepl {:jvm-opts [\"-Djdk.attach.allowAttachSelf\"], :main-opts [\"-m\" \"nrepl.cmdline\" \"--middleware\" \"[cider.nrepl/cider-middleware]\"]}}}") " -M:test:cider/nrepl") "")) (deps '(("nrepl/nrepl" "0.9.0")))) @@ -527,7 +464,7 @@ :to-equal expected))))) (it "allows for global options" (let ((expected (string-join `("-J-Xverify:none -Sdeps " - ,(shell-quote-argument "{:deps {cider/cider-nrepl {:mvn/version \"0.49.0\"} nrepl/nrepl {:mvn/version \"0.9.0\"}} :aliases {:cider/nrepl {:jvm-opts [\"-Djdk.attach.allowAttachSelf\"], :main-opts [\"-m\" \"nrepl.cmdline\" \"--middleware\" \"[cider.nrepl/cider-middleware]\"]}}}") + ,(shell-quote-argument "{:deps {cider/cider-nrepl {:mvn/version \"0.55.7\"} nrepl/nrepl {:mvn/version \"0.9.0\"}} :aliases {:cider/nrepl {:jvm-opts [\"-Djdk.attach.allowAttachSelf\"], :main-opts [\"-m\" \"nrepl.cmdline\" \"--middleware\" \"[cider.nrepl/cider-middleware]\"]}}}") " -M:test:cider/nrepl") "")) (deps '(("nrepl/nrepl" "0.9.0")))) @@ -538,7 +475,7 @@ (setq-local cider-jack-in-dependencies '(("org.clojure/tools.deps" (("git/sha" . "6ae2b6f71773de7549d7f22759e8b09fec27f0d9") ("git/url" . "https://github.com/clojure/tools.deps/"))))) (let ((expected (string-join `("clojure -Sdeps " - ,(shell-quote-argument "{:deps {nrepl/nrepl {:mvn/version \"0.9.0\"} cider/cider-nrepl {:mvn/version \"0.49.0\"} org.clojure/tools.deps { :git/sha \"6ae2b6f71773de7549d7f22759e8b09fec27f0d9\" :git/url \"https://github.com/clojure/tools.deps/\" }} :aliases {:cider/nrepl {:jvm-opts [\"-Djdk.attach.allowAttachSelf\"], :main-opts [\"-m\" \"nrepl.cmdline\" \"--middleware\" \"[cider.nrepl/cider-middleware]\"]}}}") + ,(shell-quote-argument "{:deps {nrepl/nrepl {:mvn/version \"0.9.0\"} cider/cider-nrepl {:mvn/version \"0.55.7\"} org.clojure/tools.deps { :git/sha \"6ae2b6f71773de7549d7f22759e8b09fec27f0d9\" :git/url \"https://github.com/clojure/tools.deps/\" }} :aliases {:cider/nrepl {:jvm-opts [\"-Djdk.attach.allowAttachSelf\"], :main-opts [\"-m\" \"nrepl.cmdline\" \"--middleware\" \"[cider.nrepl/cider-middleware]\"]}}}") " -M:cider/nrepl") ""))) (setq-local cider-allow-jack-in-without-project t) diff --git a/test/cider-util-tests.el b/test/cider-util-tests.el index 9c2e612e0..656e7f2db 100644 --- a/test/cider-util-tests.el +++ b/test/cider-util-tests.el @@ -1,6 +1,6 @@ ;;; cider-util-tests.el -*- lexical-binding: t; -*- -;; Copyright © 2012-2024 Tim King, Bozhidar Batsov +;; Copyright © 2012-2025 Tim King, Bozhidar Batsov ;; Author: Tim King ;; Bozhidar Batsov diff --git a/test/clojure-ts-mode/cider-find-ts-tests.el b/test/clojure-ts-mode/cider-find-ts-tests.el new file mode 100644 index 000000000..d3db256f3 --- /dev/null +++ b/test/clojure-ts-mode/cider-find-ts-tests.el @@ -0,0 +1,88 @@ +;;; cider-find-ts-tests.el --- -*- lexical-binding: t; -*- + +;; Copyright (C) 2025 Roman Rudakov + +;; Author: Roman Rudakov + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; This is part of CIDER + +;;; Code: + +(require 'buttercup) +(require 'cider-find) + +(describe "cider--find-keyword-loc (TreeSitter)" + (it "finds the given keyword, discarding false positives" + (with-clojure-ts-buffer "(ns some.ns) +;; ::foo +\"::foo\" +#_::foo +::foobar +\" +::foo +\" +::foo +more +stuff" + (let* ((sample-buffer (current-buffer))) + (spy-on 'cider-ensure-connected :and-return-value t) + (spy-on 'cider-sync-request:ns-path :and-call-fake (lambda (kw-ns _) + kw-ns)) + (spy-on 'cider-resolve-alias :and-call-fake (lambda (_ns ns-qualifier) + ns-qualifier)) + (spy-on 'cider-find-file :and-call-fake (lambda (kw-ns) + (when (equal kw-ns "some.ns") + sample-buffer))) + + (nrepl-dbind-response (cider--find-keyword-loc "::some.ns/foo") (dest dest-point) + (expect dest-point :to-equal 63) + (with-current-buffer dest + (goto-char dest-point) + ;; important - ensure that we're looking at ::foo and not ::foobar: + (expect (cider-symbol-at-point 'look-back) :to-equal "::foo"))) + + (nrepl-dbind-response (cider--find-keyword-loc "::foo") (dest dest-point) + (expect dest-point :to-equal 63) + (with-current-buffer dest + (goto-char dest-point) + ;; important - ensure that we're looking at ::foo and not ::foobar: + (expect (cider-symbol-at-point 'look-back) :to-equal "::foo"))) + + (nrepl-dbind-response (cider--find-keyword-loc ":some.ns/foo") (dest dest-point) + (expect dest-point :to-equal 63) + (with-current-buffer dest + (goto-char dest-point) + ;; important - ensure that we're looking at ::foo and not ::foobar: + (expect (cider-symbol-at-point 'look-back) :to-equal "::foo"))) + + (nrepl-dbind-response (cider--find-keyword-loc "::some.ns/bar") (dest dest-point) + (expect dest-point :to-equal nil)) + + (nrepl-dbind-response (cider--find-keyword-loc ":some.ns/bar") (dest dest-point) + (expect dest-point :to-equal nil)) + + (expect (cider--find-keyword-loc ":foo") :to-throw 'user-error) + + (nrepl-dbind-response (cider--find-keyword-loc ":unrelated/foo") (dest dest-point) + (expect dest-point :to-equal nil)) + + (nrepl-dbind-response (cider--find-keyword-loc "::unrelated/foo") (dest dest-point) + (expect dest-point :to-equal nil)))))) + +(provide 'cider-find-ts-tests) +;;; cider-find-ts-tests.el ends here diff --git a/test/clojure-ts-mode/cider-util-ts-tests.el b/test/clojure-ts-mode/cider-util-ts-tests.el index 186eb6aaa..3eeede016 100644 --- a/test/clojure-ts-mode/cider-util-ts-tests.el +++ b/test/clojure-ts-mode/cider-util-ts-tests.el @@ -32,6 +32,24 @@ (require 'clojure-ts-mode) (require 'cider-util) +(defun with-clojure-ts-buffer--go-to-point () + (when (search-forward "|" nil 'noerror) + (delete-char -1))) + +(defmacro with-clojure-ts-buffer (contents &rest body) + "Execute BODY in a clojure-ts-mode buffer with CONTENTS + +CONTENTS is a string containing an optional character `|' indicating the +cursor position. If not present, the cursor is placed at the end of the +buffer." + (declare (indent 1)) + `(with-temp-buffer + (delay-mode-hooks (clojure-ts-mode)) + (insert ,contents) + (goto-char (point-min)) + (with-clojure-ts-buffer--go-to-point) + ,@body)) + (describe "clojure-ts-mode activation" (it "test suite installs the tree-sitter-clojure grammar" (with-temp-buffer @@ -56,4 +74,20 @@ (expect (cider-clojurescript-major-mode-p) :not :to-be-truthy) (expect (cider-clojurec-major-mode-p) :to-be-truthy)))) +(describe "cider-keyword-at-p" + (it "returns `t' if in keyword" + (with-clojure-ts-buffer ":he|llo" + (expect (cider-keyword-at-point-p) :to-be-truthy) + (expect (cider-keyword-at-point-p (point)) :to-be-truthy)) + (with-clojure-ts-buffer "::he|llo" + (expect (cider-keyword-at-point-p) :to-be-truthy) + (expect (cider-keyword-at-point-p (point)) :to-be-truthy)) + (with-clojure-ts-buffer ":some.names|pace/hello" + (expect (cider-keyword-at-point-p) :to-be-truthy) + (expect (cider-keyword-at-point-p (point)) :to-be-truthy))) + (it "returns `nil' if not in keyword" + (with-clojure-ts-buffer ":hello \"|World\"" + (expect (cider-keyword-at-point-p) :not :to-be-truthy) + (expect (cider-keyword-at-point-p (point)) :not :to-be-truthy)))) + (provide 'cider-ts-util-tests) diff --git a/test/enrich/cider-docstring-tests.el b/test/enrich/cider-docstring-tests.el index fa5de1709..ab5dcc14d 100644 --- a/test/enrich/cider-docstring-tests.el +++ b/test/enrich/cider-docstring-tests.el @@ -1,7 +1,7 @@ ;; -*- lexical-binding: t; -*- ;;; cider-docstring-tests.el -;; Copyright © 2012-2024 Bozhidar Batsov +;; Copyright © 2012-2025 Bozhidar Batsov ;; Author: Bozhidar Batsov diff --git a/test/integration/integration-test-utils.el b/test/integration/integration-test-utils.el index e31c757d3..bdefeba83 100644 --- a/test/integration/integration-test-utils.el +++ b/test/integration/integration-test-utils.el @@ -1,6 +1,6 @@ ;;; integration-test-utils.el -*- lexical-binding: t; -*- -;; Copyright © 2022-2024 Ioannis Kappas +;; Copyright © 2022-2025 Ioannis Kappas ;; This file is NOT part of GNU Emacs. diff --git a/test/integration/integration-tests.el b/test/integration/integration-tests.el index 1bfa2baaf..71081a995 100644 --- a/test/integration/integration-tests.el +++ b/test/integration/integration-tests.el @@ -1,6 +1,6 @@ ;;; integration-tests.el -*- lexical-binding: t; -*- -;; Copyright © 2022-2024 Ioannis Kappas +;; Copyright © 2022-2025 Ioannis Kappas ;; This file is NOT part of GNU Emacs. diff --git a/test/nrepl-bencode-tests.el b/test/nrepl-bencode-tests.el index cb7b7d2ac..410c5f186 100644 --- a/test/nrepl-bencode-tests.el +++ b/test/nrepl-bencode-tests.el @@ -1,6 +1,6 @@ ;;; nrepl-bencode-tests.el -*- lexical-binding: t; -*- -;; Copyright © 2012-2024 Tim King, Bozhidar Batsov +;; Copyright © 2012-2025 Tim King, Bozhidar Batsov ;; Author: Tim King ;; Bozhidar Batsov @@ -327,10 +327,48 @@ If object is incomplete, return a decoded path." "int" 1 "int-list" (1 2 3 4 5) "string" "f30dbd69-7095-40c1-8e98-7873ae71a07f" - "dict" (dict "k1" 1 "k2" 2 "k3" "333333") + "unordered-dict" (dict "k3" "333333" "k2" 2 "k1" 1) "status" ("eval-error"))) - (expect (car (nrepl-bdecode-string (nrepl-bencode obj))) - :to-equal obj)))) + ;; Bencoded dicts may change the order of the keys of original + ;; dict, as bencoding a dict MUST encode the keys in sorted + ;; order. We need to compare objects taking this into account. + (expect (bencodable-obj-equal? + obj + (car (nrepl-bdecode-string (nrepl-bencode obj)))) + :to-be t)))) + +(describe "nrepl--bencode" + (it "encodes strings" + (expect (nrepl-bencode "spam") :to-equal "4:spam") + (expect (nrepl-bencode "") :to-equal "0:") + ;; Assuming we use UTF-8 encoded strings, which + ;; Clojure/Clojurescript do. + (expect (nrepl-bencode "Божидар") :to-equal "14:Божидар")) + + (it "encodes integers" + (expect (nrepl-bencode 3) :to-equal "i3e") + (expect (nrepl-bencode -3) :to-equal "i-3e")) + + (it "encodes lists" + (expect (nrepl-bencode '("spam" "eggs")) + :to-equal "l4:spam4:eggse") + (expect (nrepl-bencode '("spam" ("eggs" "salt"))) + :to-equal "l4:spaml4:eggs4:saltee") + (expect (nrepl-bencode '(1 2 3 (4 5 (6)) 7 8)) + :to-equal "li1ei2ei3eli4ei5eli6eeei7ei8ee")) + + (it "encodes dicts" + (expect (nrepl-bencode '(dict "spam" "eggs" "cow" "moo")) + :to-equal "d3:cow3:moo4:spam4:eggse") + (expect (nrepl-bencode '(dict "spam" "eggs" + "cow" (dict "foo" "foobar" "bar" "baz"))) + :to-equal "d3:cowd3:bar3:baz3:foo6:foobare4:spam4:eggse")) + + (it "handles nils" + (expect (nrepl-bencode '("" nil (dict "" nil))) + :to-equal "l0:led0:leee") + (expect (nrepl-bencode '("" nil (dict "cow" nil "" 6))) + :to-equal "l0:led0:i6e3:cowleee"))) ;; benchmarks diff --git a/test/nrepl-client-tests.el b/test/nrepl-client-tests.el index 43c776e95..3df845181 100644 --- a/test/nrepl-client-tests.el +++ b/test/nrepl-client-tests.el @@ -1,6 +1,6 @@ ;;; nrepl-client-tests.el -*- lexical-binding: t; -*- -;; Copyright © 2012-2024 Tim King, Bozhidar Batsov +;; Copyright © 2012-2025 Tim King, Bozhidar Batsov ;; Author: Tim King ;; Bozhidar Batsov diff --git a/test/nrepl-dict-tests.el b/test/nrepl-dict-tests.el index 5337eec0d..b84c7cd42 100644 --- a/test/nrepl-dict-tests.el +++ b/test/nrepl-dict-tests.el @@ -1,6 +1,6 @@ ;;; nrepl-dict-tests.el -*- lexical-binding: t; -*- -;; Copyright © 2012-2024 Tim King, Bozhidar Batsov +;; Copyright © 2012-2025 Tim King, Bozhidar Batsov ;; Author: Tim King ;; Bozhidar Batsov diff --git a/test/nrepl-server-mock.el b/test/nrepl-server-mock.el index 4f5c96d66..4b3eb98d0 100644 --- a/test/nrepl-server-mock.el +++ b/test/nrepl-server-mock.el @@ -1,6 +1,6 @@ -;; nrepl-server-mock.el -*- lexical-binding: t; -*- +;; nrepl-server-mock.el --- Mock nREPL server -*- lexical-binding: t; -*- -;; Copyright © 2021-2024 Ioannis Kappas +;; Copyright © 2021-2025 Ioannis Kappas ;; This file is NOT part of GNU Emacs. @@ -24,7 +24,7 @@ ;; A mock nREPL server that sends dummy replies back to clients with just enough ;; information onboard to accommodate testing requirements. ;; -;; Meant to be invoked as the top-level fn of an emacs subprocess. +;; Meant to be invoked as the top-level fn of an Emacs subprocess. ;;; Code: @@ -33,35 +33,73 @@ (require 'queue) (require 'cl) +(defun nrepl-server-mock--get-keys (dict keys) + "Get the values for KEYS from nrepl-dict DICT. +Get them as a list, so they can be easily consumed by +`cl-destructuring-bind`." + (mapcar (lambda (k) (nrepl-dict-get dict k)) keys)) + (defun nrepl-server-mock-filter (proc output) - "Handle the nREPL message found in OUTPUT sent by the client -PROC. Minimal implementation, just enough for fulfilling clients' testing -requirements." + "Handle the nREPL message found in OUTPUT sent by the client PROC. +Minimal implementation, just enough for fulfilling clients' testing +requirements. + +Additional complexity is added by the fact that bencoded dictionaries +must have their keys in sorted order. But we don't want to have to +remember to write them down as such in the test values here (because +there is ample room for mistakes that are harder to debug)." ;; (mock/log! ":mock.filter/output %s :msg %s" proc output) (condition-case error-details (let* ((msg (queue-dequeue (cdr (nrepl-bdecode output)))) (_ (mock/log! ":mock.filter/msg :in %S" msg)) + ;; Message id and session are needed for all request + ;; messages and responses. Get them once here. + (msg-id (nrepl-dict-get msg "id")) + (msg-session (nrepl-dict-get msg "session")) (response (pcase msg - (`(dict "op" "clone" "id" ,id) - `(dict "id" ,id + ((pred (lambda (msg) + (let ((keys '("client-version"))) + (cl-destructuring-bind (client-version) (nrepl-server-mock--get-keys msg keys) + (bencodable-obj-equal? msg + `(dict "op" "clone" + "client-name" "CIDER" + "client-version" ,client-version + "id" ,msg-id)))))) + `(dict "id" ,msg-id "session" "a-session" "status" ("done") - "new-session" "a-new-session")) + "new-session" "a-new-session")) + + ((pred (bencodable-obj-equal? `(dict "op" "describe" + "id" ,msg-id + "session" ,msg-session))) + `(dict "id" ,msg-id + "session" ,msg-session + "status" ("done"))) - (`(dict "op" "describe" "session" ,session "id" ,id) - `(dict "id" ,id "session" ,session "status" - ("done"))) ;; Eval op can include other fields in addition to the ;; code, we only need the signature and the session and - ;; id fields at the end. - (`(dict "op" "eval" "code" ,_code . ,rest) - (cl-destructuring-bind (_ session _ id) (seq-drop rest (- (seq-length rest) 4)) - `(dict "id" ,id "session" ,session "status" - ("done")))) - (`(dict "op" "close" "session" ,session "id" ,id) - `(dict "id" ,id "session" ,session "status" - ("done")))))) + ;; id fields. + ((pred (lambda (msg) + (let ((keys '("op"))) + (cl-destructuring-bind (op) (nrepl-server-mock--get-keys msg keys) + (bencodable-obj-equal? `(dict "op" ,op + "id" ,msg-id + "session" ,msg-session) + `(dict "op" "eval" + "id" ,msg-id + "session" ,msg-session)))))) + `(dict "id" ,msg-id + "session" ,msg-session + "status" ("done"))) + + ((pred (bencodable-obj-equal? `(dict "op" "close" + "id" ,msg-id + "session" ,msg-session))) + `(dict "id" ,msg-id + "session" ,msg-session + "status" ("done")))))) (mock/log! ":mock.filter/msg :out %S" response) (if (not response) @@ -82,25 +120,26 @@ requirements." )) (defun nrepl-server-mock-start () - "Start a mock nREPL server process. Prints out nREPL welcome message of -the port and host it is started on. Exits after a 10 secs" + "Start a mock nREPL server process. +Prints out nREPL welcome message of the port and host it is started +on. Exits after a 10 secs" ;; change first argument to non-nil to enable logging to file (nrepl-tests-log/init! nil mock "./nrepl-server-mock.log" 'new) (mock/log! ":mock/starting...") (let* ((server-process (make-network-process - :name "server-mock/process" - :server 't - :host 'local + :name "server-mock/process" + :server 't + :host 'local ;; listen to an unoccupied port - :service 't - :buffer "server-mock/buffer" - :filter #'nrepl-server-mock-filter - :sentinel - (lambda (_proc status-change-descr) - (mock/log! ":mock/process-status %s" status-change-descr)))) - (contact (process-contact server-process 't)) + :service 't + :buffer "server-mock/buffer" + :filter #'nrepl-server-mock-filter + :sentinel + (lambda (_proc status-change-descr) + (mock/log! ":mock/process-status %s" status-change-descr)))) + (contact (process-contact server-process 't)) (mock-message (format "nREPL server started on port %d on host %s" (plist-get contact :service) (plist-get contact :host)))) @@ -115,3 +154,5 @@ the port and host it is started on. Exits after a 10 secs" (make-string (- 4096 (length mock-message)) ?*))) (sleep-for 10) (mock/log! ":mock/exiting..."))) + +;;; nrepl-server-mock.el ends here diff --git a/test/utils/cider-connection-test-utils.el b/test/utils/cider-connection-test-utils.el index c6d8a1f7d..b6da58e03 100644 --- a/test/utils/cider-connection-test-utils.el +++ b/test/utils/cider-connection-test-utils.el @@ -1,6 +1,6 @@ ;;; cider-connection-test-utils.el -*- lexical-binding: t; -*- -;; Copyright © 2012-2024 Tim King, Bozhidar Batsov +;; Copyright © 2012-2025 Tim King, Bozhidar Batsov ;; Author: Tim King ;; Bozhidar Batsov diff --git a/test/utils/nrepl-tests-utils.el b/test/utils/nrepl-tests-utils.el index 956fd91ab..64b7e4af3 100644 --- a/test/utils/nrepl-tests-utils.el +++ b/test/utils/nrepl-tests-utils.el @@ -1,6 +1,6 @@ ;;; nrepl-test-utils.el -*- lexical-binding: t; -*- -;; Copyright © 2021-2024 Ioannis Kappas +;; Copyright © 2021-2025 Ioannis Kappas ;; This file is NOT part of GNU Emacs. @@ -113,6 +113,50 @@ calling process." (message ":nrepl-mock-server-process-started..."))))) server-process)) +(defun bencodable-obj-equal? (obj1 obj2) + "Compare bencodable objects OBJ1 and OBJ2 for equality. +They are considered equal if they have the same content. Dicts are +considered equal if they have the same key-value pairs, even if the keys +appear in different order." + (cond + ((nrepl-dict-p obj1) + (if (not (nrepl-dict-p obj2)) + nil + (let ((obj1-keys (sort (nrepl-dict-keys obj1) + (lambda (a b) + (string< a b)))) + (obj2-keys (sort (nrepl-dict-keys obj2) + (lambda (a b) + (string< a b))))) + (if (not (equal obj1-keys obj2-keys)) + nil + (seq-every-p #'identity + (mapcar (lambda (key) + (bencodable-obj-equal? + (nrepl-dict-get obj1 key) + (nrepl-dict-get obj2 key))) + obj1-keys)))))) + ((listp obj1) + (if (not (and (listp obj2) + (= (length obj1) + (length obj2)))) + nil + (seq-every-p #'identity + (cl-mapcar (lambda (obj1 obj2) + (bencodable-obj-equal? obj1 obj2)) + obj1 + obj2)))) + ((integerp obj1) + (if (not (integerp obj2)) + nil + (= obj1 obj2))) + ((stringp obj1) + (if (not (stringp obj2)) + nil + (string= obj1 obj2))) + ;; Any other kind of value is not a bencodable value. + nil)) + (provide 'nrepl-tests-utils) ;;; nrepl-tests-utils.el ends here