├── .circleci
├── config.yml
└── download-jdk-sources.sh
├── .clj-kondo
├── config.edn
└── hooks
│ └── core.clj
├── .dir-locals.el
├── .gitattributes
├── .github
├── CONTRIBUTING.md
├── ISSUE_TEMPLATE.md
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ └── github_release.yml
├── .gitignore
├── CHANGELOG.md
├── Makefile
├── README.md
├── doc
├── antora.yml
├── cljdoc.edn
└── modules
│ └── ROOT
│ ├── nav.adoc
│ └── pages
│ ├── about
│ ├── changelog.adoc
│ ├── history.adoc
│ └── license.adoc
│ ├── compatibility.adoc
│ ├── faq.adoc
│ ├── hacking.adoc
│ ├── index.adoc
│ ├── internals.adoc
│ ├── nrepl-api
│ ├── ops.adoc
│ └── supplied_middleware.adoc
│ ├── release_policy.adoc
│ └── usage.adoc
├── eastwood.clj
├── maint
└── cider
│ └── nrepl
│ └── impl
│ └── docs.clj
├── project.clj
├── resources
└── cider
│ └── nrepl
│ ├── content-types.edn
│ └── version.edn
├── src
├── cider
│ ├── nrepl.clj
│ └── nrepl
│ │ ├── middleware.clj
│ │ ├── middleware
│ │ ├── DebugSupport.java
│ │ ├── apropos.clj
│ │ ├── classpath.clj
│ │ ├── clojuredocs.clj
│ │ ├── complete.clj
│ │ ├── content_type.clj
│ │ ├── debug.clj
│ │ ├── enlighten.clj
│ │ ├── format.clj
│ │ ├── info.clj
│ │ ├── inspect.clj
│ │ ├── log.clj
│ │ ├── macroexpand.clj
│ │ ├── ns.clj
│ │ ├── out.clj
│ │ ├── profile.clj
│ │ ├── refresh.clj
│ │ ├── reload.clj
│ │ ├── resource.clj
│ │ ├── slurp.clj
│ │ ├── spec.clj
│ │ ├── stacktrace.clj
│ │ ├── test.clj
│ │ ├── test
│ │ │ └── extensions.clj
│ │ ├── trace.clj
│ │ ├── track_state.clj
│ │ ├── undef.clj
│ │ ├── util.clj
│ │ ├── util
│ │ │ ├── cljs.clj
│ │ │ ├── coerce.clj
│ │ │ ├── error_handling.clj
│ │ │ ├── instrument.clj
│ │ │ ├── meta.clj
│ │ │ ├── nrepl.clj
│ │ │ └── reload.clj
│ │ ├── version.clj
│ │ └── xref.clj
│ │ ├── pprint.clj
│ │ ├── print_method.clj
│ │ └── version.clj
├── cider_nrepl
│ └── plugin.clj
└── data_readers.clj
└── test
├── clj
└── cider
│ ├── nrepl
│ ├── middleware
│ │ ├── apropos_test.clj
│ │ ├── classpath_test.clj
│ │ ├── clojuredocs_test.clj
│ │ ├── complete_test.clj
│ │ ├── content_type_test.clj
│ │ ├── debug_integration_test.clj
│ │ ├── debug_test.clj
│ │ ├── format_test.clj
│ │ ├── info_spec_test.clj
│ │ ├── info_test.clj
│ │ ├── inspect_test.clj
│ │ ├── log_test.clj
│ │ ├── macroexpand_test.clj
│ │ ├── ns_test.clj
│ │ ├── out_test.clj
│ │ ├── profile_test.clj
│ │ ├── refresh_test.clj
│ │ ├── reload_test.clj
│ │ ├── resource_test.clj
│ │ ├── slurp_test.clj
│ │ ├── spec_test.clj
│ │ ├── stacktrace_test.clj
│ │ ├── test
│ │ │ └── extensions_test.clj
│ │ ├── test_filter_tests.clj
│ │ ├── test_test.clj
│ │ ├── trace_test.clj
│ │ ├── track_state_test.clj
│ │ ├── undef_test.clj
│ │ ├── util
│ │ │ ├── error_handling_test.clj
│ │ │ ├── instrument_test.clj
│ │ │ └── meta_test.clj
│ │ ├── version_test.clj
│ │ └── xref_test.clj
│ ├── plugin_test.clj
│ ├── print_method_test.clj
│ └── version_test.clj
│ ├── nrepl_test.clj
│ └── test_helpers.clj
├── cljs
└── cider
│ └── nrepl
│ ├── middleware
│ ├── cljs_complete_test.clj
│ ├── cljs_info_test.clj
│ ├── cljs_inspect_test.clj
│ ├── cljs_macroexpand_test.clj
│ ├── cljs_ns_test.clj
│ └── cljs_stacktrace_test.clj
│ └── piggieback_test.clj
├── common
├── cider
│ ├── nrepl
│ │ ├── test_session.clj
│ │ └── test_transport.clj
│ └── test_ns
│ │ ├── first_test_ns.clj
│ │ ├── second_test_ns.clj
│ │ └── third_test_ns.clj
└── cider_nrepl
│ └── plugin_test.clj
├── java
└── cider
│ └── nrepl
│ └── test
│ ├── AnotherTestClass.java
│ ├── TestClass.java
│ └── YetAnotherTest.java
├── resources
├── cider
│ └── nrepl
│ │ └── middleware
│ │ └── test_with_throwing_fixtures.clj
├── failing_test_ns.clj
├── failing_test_ns_2.clj
├── logback-test.xml
├── logging.properties
├── sum-types-are-cool.jpg
├── test.txt
└── unknown.unknown
├── smoketest
├── .gitignore
├── project.clj
└── src
│ └── smoketest
│ └── core.clj
└── src
└── cider
└── nrepl
└── middleware
└── debug_integration_test
└── fn.clj
/.circleci/download-jdk-sources.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | URL="$1"
3 | QUALIFIER=$2
4 | DEST=$3
5 |
6 | # Download JDK sources from Github and repackage in the same fashion as src.zip
7 | # that is normally distributed with JDK.
8 | wget "$URL" -O full-src.zip
9 | unzip -q full-src.zip
10 | cp -r jdk*/src/java.base/share/classes java.base
11 | cp -r jdk*/src/java.desktop/share/classes java.desktop
12 | zip -qr $DEST java.base java.desktop
13 | rm -rf java.base java.desktop jdk* full-src.zip
14 |
--------------------------------------------------------------------------------
/.clj-kondo/config.edn:
--------------------------------------------------------------------------------
1 | {:hooks {:analyze-call {cider.nrepl.middleware.out/with-out-binding
2 | hooks.core/with-out-binding}}
3 | :lint-as {cider.nrepl.middleware.log-test/with-each-framework clojure.core/let}
4 | :exclude-files "debug_integration_test.clj$" ;; TODO: remove once flaky debugger tests are addressed
5 | :linters {:unresolved-symbol {:exclude [(cider.nrepl/def-wrapper)
6 | (cider.nrepl.middleware.util.instrument/definstrumenter)
7 | (cider.nrepl.middleware.util.instrument/with-break)
8 | (cider.nrepl.middleware.util.instrument/instrument-special-form)
9 | (cider.nrepl.middleware.util.instrument/instrument-coll)
10 | (cider.nrepl.print-method/def-print-method)
11 | (cljs.test/is [match? thrown-match?])
12 | (clojure.test/is [match? thrown-match?])]}
13 | :consistent-alias {:aliases {clojure.string str}}
14 | :unused-import {:level :off}
15 | :unresolved-var {:level :off}
16 | :unused-binding {:level :off}
17 | :refer-all {:level :off}
18 | :unused-namespace {:level :off}
19 | :missing-test-assertion {:level :off}
20 | :use {:level :off}
21 | :redundant-let {:level :off}
22 | :unused-private-var {:level :off}
23 | :missing-else-branch {:level :off}
24 | :unused-referred-var {:level :off}
25 | :type-mismatch {:level :off}
26 | :unresolved-namespace {:exclude [clojure.main nrepl.transport js]}}
27 | :output {:progress true
28 | :exclude-files ["data_readers" "tasks"]}}
29 |
--------------------------------------------------------------------------------
/.clj-kondo/hooks/core.clj:
--------------------------------------------------------------------------------
1 | (ns hooks.core
2 | (:require
3 | [clj-kondo.hooks-api :as api]))
4 |
5 | (defn with-out-binding
6 | "Rewrite with-out-binding as a let on the first symbol in the arg vector.
7 | The remaining args in the arg vector are evaluated."
8 | [{:keys [node]}]
9 | (let [[_ binding-vec & body] (:children node)
10 | [binding & others] (:children binding-vec)
11 | new-node (api/list-node
12 | (list*
13 | (api/token-node 'let)
14 | (api/vector-node
15 | (reduce
16 | into
17 | [binding (api/token-node 'nil)]
18 | (mapv
19 | #(vector (api/token-node '_) %)
20 | others)))
21 | body))]
22 | ;; un-comment below to debug changes
23 | ;; (prn :with-binding (api/sexpr new-node))
24 | {:node (with-meta new-node (meta node))}))
25 |
--------------------------------------------------------------------------------
/.dir-locals.el:
--------------------------------------------------------------------------------
1 | ;;; Directory Local Variables
2 | ;;; For more information see (info "(emacs) Directory Variables")
3 |
4 | ((clojure-mode
5 | (clojure-indent-style . :always-align)
6 | (indent-tabs-mode . nil)
7 | (fill-column . 80)))
8 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | CHANGELOG.md merge=union
2 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | If you discover issues, have ideas for improvements or new features, or
4 | want to contribute a new module, please report them to the
5 | [issue tracker][1] of the repository or submit a pull request. Please,
6 | try to follow these guidelines when you do so.
7 |
8 | ## Issue reporting
9 |
10 | * Check that the issue has not already been reported.
11 | * Check that the issue has not already been fixed in the latest code
12 | (a.k.a. `master`).
13 | * Be clear, concise and precise in your description of the problem.
14 | * Open an issue with a descriptive title and a summary in grammatically correct,
15 | complete sentences.
16 | * Include any relevant code to the issue summary.
17 |
18 | ## Pull requests
19 |
20 | * Read [how to properly contribute to open source projects on Github][2].
21 | * Use a topic branch to easily amend a pull request later, if necessary.
22 | * Write [good commit messages][3].
23 | * Squash related commits together.
24 | * Use the same coding conventions as the rest of the project.
25 | * Include tests for the code you've submitted.
26 | * Make sure the existing tests pass.
27 | * Open a [pull request][4] that relates to *only* one subject with a clear title
28 | and description in grammatically correct, complete sentences.
29 |
30 | [1]: https://github.com/clojure-emacs/cider-nrepl/issues
31 | [2]: http://gun.io/blog/how-to-github-fork-branch-and-pull-request
32 | [3]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
33 | [4]: https://help.github.com/articles/using-pull-requests
34 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | *Use the template below when reporting bugs. Please, make sure that you're
2 | running the latest stable release or the latest snapshot/pre-release of
3 | `cider-nrepl` and that the problem you're reporting hasn't been reported (and
4 | potentially fixed) already.*
5 |
6 | ## Expected behavior
7 |
8 | ## Actual behavior
9 |
10 | ## Steps to reproduce the problem
11 |
12 | *This is extremely important! Providing us with a reliable way to reproduce
13 | a problem will expedite its solution.*
14 |
15 | ## Environment & Version information
16 |
17 | ### cider-nrepl version
18 |
19 | E.g. 0.11.2
20 |
21 | ### Java version
22 |
23 | E.g. 1.8
24 |
25 | ### Operating system
26 |
27 | E.g. Windows 10
28 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | Before submitting a PR make sure the following things have been done:
2 |
3 | - [ ] The commits are consistent with our [contribution guidelines](../blob/master/.github/CONTRIBUTING.md)
4 | - [ ] You've added tests to cover your change(s)
5 | - [ ] All tests are passing
6 | - [ ] You've updated the README
7 | - [ ] Middleware documentation is up to date
8 | * Please check out and modify the `cider.nrepl` ns which has all middleware documentation.
9 | * Run `lein docs` afterwards, and commit the results.
10 |
11 | **Note:** If you're just starting out to hack on `cider-nrepl` you might find
12 | [nREPL's documentation](https://nrepl.org) and the
13 | "Design" section of the README extremely useful.*
14 |
15 | Thanks!
16 |
--------------------------------------------------------------------------------
/.github/workflows/github_release.yml:
--------------------------------------------------------------------------------
1 | name: Create GitHub Release
2 |
3 | on:
4 | push:
5 | tags:
6 | - "v*" # Trigger when a version tag is pushed (e.g., v1.0.0)
7 |
8 | jobs:
9 | create-release:
10 | runs-on: ubuntu-latest
11 |
12 | permissions:
13 | contents: write
14 |
15 | steps:
16 | - name: Checkout Code
17 | uses: actions/checkout@v4
18 |
19 | - name: Create GitHub Release with Auto-Generated Notes
20 | uses: ncipollo/release-action@v1
21 | with:
22 | tag: ${{ github.ref_name }}
23 | name: cider-nrepl ${{ github.ref_name }}
24 | prerelease: ${{ contains(github.ref, '-rc') || contains(github.ref, '-alpha') || contains(github.ref, '-beta') }}
25 | generateReleaseNotes: true # Auto-generate release notes based on PRs and commits
26 | # TODO: Use bodyFile to get the contents from changelog
27 | token: ${{ secrets.GITHUB_TOKEN }}
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /lib
3 | /classes
4 | /checkouts
5 | pom.xml
6 | pom.xml.asc
7 | nashorn_code_cache
8 | .cljs_node_repl
9 | out
10 | *.jar
11 | *.class
12 | .cljs_nashorn_repl
13 | .inline-deps
14 | .no-pedantic
15 | .lein-deps-sum
16 | .lein-failures
17 | .lein-plugins
18 | .lein-repl-history
19 | .nrepl-port
20 | .source-deps
21 | .clj-kondo/.cache
22 | /.no-mranderson
23 | /test-runner
24 | /.test-classpath
25 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: test quick-test eastwood cljfmt cljfmt-fix install fast-install smoketest deploy clean lint docs test_impl
2 | .DEFAULT_GOAL := quick-test
3 |
4 | # Set bash instead of sh for the @if [[ conditions,
5 | # and use the usual safety flags:
6 | SHELL = /bin/bash -Ee
7 |
8 | CLOJURE_VERSION ?= 1.12
9 | TEST_PROFILES ?= "-user,-dev,+test"
10 |
11 | # We need Java sources to test Java parsing functionality, but the Docker images
12 | # we use on CircleCI doesn't include src.zip. So we have to download them from
13 | # Github and repackage in a form that is resemblant to src.zip from normal
14 | # distributions.
15 | base-src-jdk8.zip:
16 | # echo 'Placeholder. We dont parse sources on JDK8.'
17 | touch $@
18 |
19 | base-src-jdk11.zip:
20 | bash .circleci/download-jdk-sources.sh https://github.com/adoptium/jdk11u/archive/refs/tags/jdk-11.0.28+0.zip jdk11 $@
21 |
22 | base-src-jdk17.zip:
23 | bash .circleci/download-jdk-sources.sh https://github.com/adoptium/jdk17u/archive/refs/tags/jdk-17.0.15+5.zip jdk17 $@
24 |
25 | base-src-jdk21.zip:
26 | bash .circleci/download-jdk-sources.sh https://github.com/adoptium/jdk21u/archive/refs/tags/jdk-21.0.7+5.zip jdk21 $@
27 |
28 | base-src-jdk24.zip:
29 | bash .circleci/download-jdk-sources.sh https://github.com/adoptium/jdk/archive/refs/tags/jdk-24+36.zip jdk24 $@
30 |
31 | copy-sources-to-jdk: base-src-$(JDK_SRC_VERSION).zip
32 | mkdir -p $(JAVA_HOME)/lib && cp base-src-$(JDK_SRC_VERSION).zip $(JAVA_HOME)/lib/src.zip
33 |
34 | dump-version:
35 | echo '"$(PROJECT_VERSION)"' > resources/cider/nrepl/version.edn
36 |
37 | target/srcdeps: project.clj
38 | lein with-profile -user,-dev inline-deps
39 | # Remove cljfmt.main because it depends on tools.cli which we explicitly removed.
40 | rm -f target/srcdeps/cider/nrepl/inlined/deps/cljfmt/*/cljfmt/main.clj
41 |
42 | test_impl: copy-sources-to-jdk
43 | lein with-profile $(TEST_PROFILES),+$(CLOJURE_VERSION) test
44 |
45 | test: target/srcdeps
46 | @make test_impl TEST_PROFILES="$(TEST_PROFILES),+plugin.mranderson/config"
47 |
48 | quick-test: test_impl
49 |
50 | eastwood:
51 | lein with-profile -user,-dev,+$(CLOJURE_VERSION),+deploy,+eastwood eastwood
52 |
53 | cljfmt:
54 | lein with-profile -user,-dev,+$(CLOJURE_VERSION),+cljfmt cljfmt check
55 |
56 | cljfmt-fix:
57 | lein with-profile -user,-dev,+$(CLOJURE_VERSION),+cljfmt cljfmt fix
58 |
59 | .make_kondo_prep: project.clj .clj-kondo/config.edn
60 | CIDER_NO_MRANDERSON="true" CIDER_NO_PEDANTIC="true" lein with-profile -user,-dev,+test,+clj-kondo,+deploy,+$(CLOJURE_VERSION) clj-kondo --copy-configs --dependencies --lint '$$classpath' > $@
61 |
62 | kondo: .make_kondo_prep clean
63 | CIDER_NO_MRANDERSON="true" CIDER_NO_PEDANTIC="true" lein with-profile -user,-dev,+test,+clj-kondo,+deploy,+$(CLOJURE_VERSION) clj-kondo
64 |
65 | lint: kondo cljfmt eastwood
66 |
67 | # PROJECT_VERSION=x.y.z make install
68 | install: dump-version check-install-env target/srcdeps
69 | CIDER_NO_PEDANTIC="true" lein with-profile -user,-dev,+$(CLOJURE_VERSION),+plugin.mranderson/config install
70 | git checkout resources/cider/nrepl/version.edn
71 |
72 | # PROJECT_VERSION=x.y.z make fast-install
73 | fast-install: dump-version check-install-env
74 | lein with-profile -user,-dev,+$(CLOJURE_VERSION) install
75 | git checkout resources/cider/nrepl/version.edn
76 |
77 | smoketest: install
78 | cd test/smoketest && \
79 | lein with-profile -user,-dev,+$(CLOJURE_VERSION) uberjar && \
80 | java -jar target/smoketest-0.1.0-SNAPSHOT-standalone.jar
81 |
82 | # Deployment is performed via CI by creating a git tag prefixed with "v".
83 | # Please do not deploy locally as it skips various measures (particularly around mranderson).
84 | deploy: check-env target/srcdeps
85 | @if ! echo "$(CIRCLE_TAG)" | grep -q "^v"; then \
86 | echo "[Error] CIRCLE_TAG $(CIRCLE_TAG) must start with 'v'."; \
87 | exit 1; \
88 | fi
89 | rm -f .no-mranderson
90 | export PROJECT_VERSION=$$(echo "$(CIRCLE_TAG)" | sed 's/^v//'); \
91 | echo "\"$$PROJECT_VERSION\"" > resources/cider/nrepl/version.edn; \
92 | lein with-profile -user,-dev,-provided,+$(CLOJURE_VERSION),+plugin.mranderson/config deploy clojars
93 |
94 | check-env:
95 | ifndef CLOJARS_USERNAME
96 | $(error CLOJARS_USERNAME is undefined)
97 | endif
98 | ifndef CLOJARS_PASSWORD
99 | $(error CLOJARS_PASSWORD is undefined)
100 | endif
101 | ifndef CIRCLE_TAG
102 | $(error CIRCLE_TAG is undefined. Please only perform deployments by publishing git tags. CI will do the rest.)
103 | endif
104 |
105 | check-install-env:
106 | ifndef PROJECT_VERSION
107 | $(error Please set PROJECT_VERSION as an env var beforehand.)
108 | endif
109 |
110 | clean:
111 | lein with-profile -user clean
112 | cd test/smoketest && lein with-profile -user clean
113 |
114 | docs:
115 | lein docs
116 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://circleci.com/gh/clojure-emacs/cider-nrepl/tree/master)
2 | [](https://codecov.io/gh/clojure-emacs/cider-nrepl/)
3 | [](https://clojars.org/cider/cider-nrepl)
4 | [](https://cljdoc.org/d/cider/cider-nrepl/CURRENT)
5 | [](https://clojars.org/cider/cider-nrepl)
6 |
7 | # CIDER nREPL
8 |
9 | A collection of [nREPL](https://github.com/nrepl/nrepl)
10 | middleware, originally designed to enhance
11 | [CIDER](https://github.com/clojure-emacs/cider).
12 | `cider-nrepl` is also used:
13 |
14 | - [fireplace.vim](https://github.com/tpope/vim-fireplace)
15 | - [vim-iced](https://github.com/liquidz/vim-iced)
16 | - [Calva](https://calva.io/)
17 | - [Conjure](https://github.com/Olical/conjure)
18 |
19 | and other Clojure development tools based on nREPL.
20 |
21 | If you're just a user trying to get started with CIDER, then you
22 | probably don't want to read this. You should follow the steps in
23 | [CIDER's user manual](https://docs.cider.mx) instead.
24 |
25 | > [!TIP]
26 | >
27 | > People who are new to nREPL might benefit from reading [its
28 | > user manual](https://nrepl.org).
29 |
30 | ## Documentation
31 |
32 | Please refer to the [documentation site](https://docs.cider.mx/cider-nrepl) for
33 | information on how to setup `cider-nrepl` or how to leverage its nREPL API.
34 |
35 | ## Release policy
36 |
37 | We're following [SemVer](http://semver.org/) (as much as one can be
38 | following it when the major version is 0). At this point bumps of the
39 | minor (second) version number are considered major releases and always
40 | include new features or significant changes to existing features. API
41 | compatibility between major releases is not a (big) concern (although we try
42 | to break the API rarely and only for a good reason).
43 |
44 | The development cycle for the next major
45 | release starts immediately after the previous one has been
46 | shipped. Bugfix/point releases (if any) address only serious bugs and
47 | never contain new features.
48 |
49 | ## Contributing
50 |
51 | ### Issues
52 |
53 | Report issues and suggest features and improvements on the
54 | [GitHub issue tracker](https://github.com/clojure-emacs/cider-nrepl/issues). Don't
55 | ask questions on the issue tracker - the mailing list and the IRC
56 | channel are the places for questions.
57 |
58 | ### Patches
59 |
60 | Patches in any form are always welcome! GitHub pull requests are even better! :-)
61 |
62 | Before submitting a patch or a pull request make sure all tests are
63 | passing and that your patch is in line with the [contribution
64 | guidelines](.github/CONTRIBUTING.md).
65 |
66 | ### Local development
67 |
68 | Local development tasks, like running the tests or locally installing
69 | cider-nrepl are offered by our Makefile. We recommend using it, since some
70 | aspects (e.g. the use of [mranderson][]) can be intricate to newcomers.
71 |
72 | These are its main tasks for local development:
73 |
74 | ```shell
75 | # Run tests, using mranderson (slower but more realistic)
76 | make test
77 |
78 | # Run tests, without using mranderson (considerably faster)
79 | make quick-test
80 |
81 | # Install the project in your local ~/.m2 directory, using mranderson (recommended)
82 | # The JVM flag is a temporary workaround.
83 | export LEIN_JVM_OPTS="-Dmranderson.internal.no-parallelism=true"
84 | PROJECT_VERSION=X.Y.Z make install
85 |
86 | # Install the project in your local ~/.m2 directory, without using mranderson
87 | # (it's faster, but please only use when you repeatedly need to install cider-nrepl)
88 | # The JVM flag is a temporary workaround.
89 | export LEIN_JVM_OPTS="-Dmranderson.internal.no-parallelism=true"
90 | PROJECT_VERSION=X.Y.Z make fast-install
91 |
92 | # Runs clj-kondo, cljfmt and Eastwood (in that order, with fail-fast).
93 | # Please try to run this before pushing commits.
94 | make lint
95 |
96 | # Regenerates our user manual.
97 | # When you modify our middleware such that its schema changes, please reflect so in the `cider.nrepl` namespace
98 | # and run:
99 | make docs
100 | ```
101 |
102 | ## Releasing to Clojars
103 |
104 | > [!IMPORTANT]
105 | >
106 | > Make sure you've updated the changelog, `docs/antora.yml`, etc
107 | > before cutting a new release.
108 |
109 | Release to [clojars](https://clojars.org/) by tagging a new release:
110 |
111 | ```shell
112 | git tag -a vX.Y.Z -m "Release X.Y.Z"
113 | git push --tags
114 | ```
115 |
116 | The CI will take it from there.
117 |
118 | ## Hall of Fame
119 |
120 | Special credit goes to the following people for their contributions:
121 |
122 | - Gary Trakhman ([@gtrak](https://github.com/gtrak))
123 | - Jeff Valk ([@jeffvalk](https://github.com/jeffvalk))
124 | - Hugo Duncan ([@hugoduncan](https://github.com/hugoduncan))
125 | - Michael Griffiths ([@cichli](https://github.com/cichli))
126 | - Artur Malabarba ([@malabarba](https://github.com/malabarba))
127 | - Lars Andersen ([@expez](https://github.com/expez))
128 | - Benedek Fazekas ([@benedekfazekas](https://github.com/benedekfazekas))
129 |
130 | And a big thanks to
131 | [all other contributors](https://github.com/clojure-emacs/cider-nrepl/graphs/contributors)
132 | who have helped so far.
133 |
134 | Let's also acknowledge some of the projects leveraged by cider-nrepl:
135 |
136 | - [orchard][] - extracted from `cider-nrepl`, so that non-nREPL clients can leverage the generic tooling functionality (like `inspect`, `apropos`, `var-info`, etc
137 | - [compliment][] - for Clojure code completion
138 | - [clj-suitable][] - for ClojureScript code completion using runtime inspection
139 | - [tools.namespace][] - for namespace reloading
140 | - [clj-reload][] - for namespace reloading
141 | - [cljfmt][] - for code formatting
142 |
143 | ## License
144 |
145 | Copyright © 2013-2025 Bozhidar Batsov
146 |
147 | Distributed under the Eclipse Public License, the same as Clojure.
148 |
149 | [orchard]: https://github.com/clojure-emacs/orchard
150 | [compliment]: https://github.com/alexander-yakushev/compliment
151 | [clj-suitable]: https://github.com/clojure-emacs/clj-suitable
152 | [tools.namespace]: https://github.com/clojure/tools.namespace
153 | [clj-reload]: https://github.com/tonsky/clj-reload
154 | [cljfmt]: https://github.com/weavejester/cljfmt
155 | [mranderson]: https://github.com/benedekfazekas/mranderson
156 |
--------------------------------------------------------------------------------
/doc/antora.yml:
--------------------------------------------------------------------------------
1 | name: cider-nrepl
2 | title: CIDER nREPL
3 | # The version should be in the format major.minor (e.g. 0.28).
4 | # Don't include patch in the version (e.g. 0.28.7).
5 | version: ~
6 | nav:
7 | - modules/ROOT/nav.adoc
8 |
--------------------------------------------------------------------------------
/doc/cljdoc.edn:
--------------------------------------------------------------------------------
1 | {:cljdoc.doc/tree
2 | [["Readme" {:file "README.md"}]
3 | ["Changelog" {:file "CHANGELOG.md"}]
4 | ["Home" {:file "doc/modules/ROOT/pages/index.adoc"}]
5 | ["Usage" {:file "doc/modules/ROOT/pages/usage.adoc"}]
6 | ["Understanding the Internals" {:file "doc/modules/ROOT/pages/internals.adoc"}]
7 | ["nREPL API" {}
8 | ["Supplied Middleware" {:file "doc/modules/ROOT/pages/nrepl-api/supplied_middleware.adoc"}]
9 | ["Supplied Ops" {:file "doc/modules/ROOT/pages/nrepl-api/ops.adoc"}]]
10 | ["Hacking on CIDER nREPL" {:file "doc/modules/ROOT/pages/hacking.adoc"}]
11 | ["Compatibility" {:file "doc/modules/ROOT/pages/compatibility.adoc"}]
12 | ["Release Policy" {:file "doc/modules/ROOT/pages/release_policy.adoc"}]
13 | ["FAQ" {:file "doc/modules/ROOT/pages/faq.adoc"}]
14 | ["About" {}
15 | ["History" {:file "doc/modules/ROOT/pages/about/history.adoc"}]
16 | ["Changelog" {:file "doc/modules/ROOT/pages/about/changelog.adoc"}]
17 | ["License" {:file "doc/modules/ROOT/pages/about/license.adoc"}]]]}
18 |
--------------------------------------------------------------------------------
/doc/modules/ROOT/nav.adoc:
--------------------------------------------------------------------------------
1 | // Don't forget to update cljdoc.edn when making changes here
2 | * xref:index.adoc[Home]
3 | * xref:usage.adoc[Usage]
4 | * xref:internals.adoc[Understanding the Internals]
5 | * nREPL API
6 | ** xref:nrepl-api/supplied_middleware.adoc[Supplied Middleware]
7 | ** xref:nrepl-api/ops.adoc[Supplied Ops]
8 | * xref:hacking.adoc[Hacking on CIDER nREPL]
9 | * xref:compatibility.adoc[Compatibility]
10 | * xref:release_policy.adoc[Release Policy]
11 | * xref:faq.adoc[FAQ]
12 | * About
13 | ** xref:about/history.adoc[History]
14 | ** xref:about/changelog.adoc[Changelog]
15 | ** xref:about/license.adoc[License]
16 |
--------------------------------------------------------------------------------
/doc/modules/ROOT/pages/about/changelog.adoc:
--------------------------------------------------------------------------------
1 | = Changelog
2 |
3 | An extensive changelog is available https://github.com/clojure-emacs/cider-nrepl/blob/master/CHANGELOG.md[here].
4 |
5 | NOTE: Only user-visible changes are documented.
6 |
--------------------------------------------------------------------------------
/doc/modules/ROOT/pages/about/history.adoc:
--------------------------------------------------------------------------------
1 | = History
2 |
3 | `cider-nrepl` was mostly inspired by https://github.com/technomancy/swank-clojure[swank-clojure].
4 |
5 | It started life as an attempt to push as much of CIDER's functionality as possible to the nREPL layer,
6 | with the assumption that this would make it easier to develop CIDER and would open up the possibility
7 | to build complex features like an interactive debugger. Fortunately, the assumption turned out to be correct.
8 | Today `cider-nrepl` provides pretty much everything that a Clojure development environment might need (and more).
9 |
10 | We quickly realized that there was nothing CIDER specific in those middleware
11 | and we've started to encourage more tool authors to leverage them. Today
12 | `cider-nrepl` is developed and released independently from CIDER and is used by
13 | most of the nREPL-based editors and IDEs out there, which is a great example of
14 | team work. Eventually `cider-nrepl` served as the inspiration for more similar
15 | middleware libraries - e.g. `refactor-nrepl`, `iced-nrepl` and `sayid`.
16 |
17 | In 2019, the core functionality has been extracted out of `cider-nrepl` into
18 | https://github.com/clojure-emacs/orchard[Orchard], a REPL-agnostic library.
19 |
20 | You can check out https://www.youtube.com/watch?v=4X-1fJm25Ww[this talk], which explores the birth of
21 | `cider-nrepl`.
22 |
--------------------------------------------------------------------------------
/doc/modules/ROOT/pages/about/license.adoc:
--------------------------------------------------------------------------------
1 | = License
2 |
3 | `cider-nrepl` is distributed under the Eclipse Public License, the same as Clojure.
4 |
--------------------------------------------------------------------------------
/doc/modules/ROOT/pages/compatibility.adoc:
--------------------------------------------------------------------------------
1 | = Compatibility
2 |
3 | == Java
4 |
5 | `cider-nrepl` officially targets Java 8, 11, 17, 21, and the most recent release
6 | version. More generally speaking - we aim to support all Java releases that are
7 | currently officially supported by Oracle.
8 |
9 | == Clojure
10 |
11 | `cider-nrepl` targets Clojure 1.10+. As Clojure doesn't have the concept of supported releases
12 | we have to get a bit creative to determine the minimum version to target.
13 |
14 | The minimum required Clojure version is currently derived using data
15 | from the
16 | https://clojure.org/news/2019/02/04/state-of-clojure-2019[State of
17 | Clojure] survey. In general we consider a Clojure release eligible for
18 | dropping once its usage drops bellow 5%, but we'd not drop support for
19 | any release just for the sake of doing it. We'd do it only if
20 | this would lessen the maintenance burden or open up the possibility for
21 | big nREPL improvements.
22 |
23 | == ClojureScript
24 |
25 | Currently we apply the same policy for Clojure and ClojureScript support.
26 |
27 | NOTE: ClojureScript support is contingent on the Piggieback middleware.
28 | Currently `cider-nrepl` requires Piggieback 0.4+ to work properly.
29 |
30 | == nREPL
31 |
32 | `cider-nrepl` supports nREPL 0.6+.
33 |
34 | NOTE: We pay special attention to supporting whatever nREPL is bundled with the
35 | current stable Leiningen release.
36 |
37 | == Compatibility Matrix
38 |
39 | Below you can find the official compatibility matrix for `cider-nrepl`.
40 |
41 | NOTE: The matrix lists only the last versions of `cider-nrepl` that supports the
42 | given compatibility tuple.
43 |
44 | .Compatibility Matrix
45 | |===
46 | | cider-nrepl | Required JDK | Required Clojure | Required nREPL
47 |
48 | | 0.19
49 | | 8
50 | | 1.8
51 | | 0.2.13
52 |
53 | | 0.20
54 | | 8
55 | | 1.8
56 | | 0.4.x
57 |
58 | | 0.25
59 | | 8
60 | | 1.8
61 | | 0.6
62 |
63 | | 0.47.0
64 | | 8
65 | | 1.9
66 | | 1.0.0
67 |
68 | | 0.55.7
69 | | 8
70 | | 1.10
71 | | 1.0.0
72 |
73 | |===
74 |
75 | == Backwards Compatibility
76 |
77 | `cider-nrepl` is under active development and breaking changes might happen from
78 | time to time. That being said, we're well aware that these days many editors and
79 | other Clojure development tools rely on `cider-nrepl` and we're fully committed
80 | to avoid introducing painful breakages there.
81 |
82 | Most tools authors using `cider-nrepl` are involved with its development in
83 | some capacity, so we're always considering carefully any proposed breaking change
84 | and the impact it would have.
85 |
86 | It's extremely unlikely that we're going to break compatibility on the
87 | protocol level ever (with the rare cases of changing/removing things
88 | that existed, but we knew for a fact weren't used). Most often
89 | backwards compatibility would be broken on the implementation level -
90 | usually extracting something out of `cider-nrepl` to the `orchard` library.
91 |
--------------------------------------------------------------------------------
/doc/modules/ROOT/pages/faq.adoc:
--------------------------------------------------------------------------------
1 | = Frequently Asked Questions
2 |
3 | == Is `cider-nrepl` specific to CIDER?
4 |
5 | No, the name is a bit of a misnomer. All of the functionality in
6 | `cider-nrepl` can be leveraged by any nREPL client. Many Clojure editors
7 | are built on top of `cider-nrepl`.
8 |
9 | If we were naming the project today we would have gone with `orchard-nrepl`. Naming is hard!
10 |
11 | == Does `cider-nrepl` support ClojureScript?
12 |
13 | Yes, it does. The support, however, is partial and not all of the functionality if available
14 | for ClojureScript.
15 |
16 | == Does `cider-nrepl` support ClojureCLR?
17 |
18 | No, it does not. There are two reasons for this:
19 |
20 | - The lack of a compatible nREPL implementation for .NET
21 | - The usage of Java APIs in the current implementation of `cider-nrepl`
22 |
23 | Both problems are solvable, but they will require significant amount of work and knowledge
24 | of .NET that the current `cider-nrepl` team does not have.
25 |
26 | == Are there any other nREPL middleware libraries similar to `cider-nrepl`?
27 |
28 | Yeah, there are. Most notably there's `refactor-nrepl` which provides a lot of
29 | refactoring functionality for Clojure editors.
30 |
--------------------------------------------------------------------------------
/doc/modules/ROOT/pages/hacking.adoc:
--------------------------------------------------------------------------------
1 | = Hacking on cider-nrepl
2 |
3 | Hacking on cider-nrepl requires nothing but a bit of knowledge of Clojure and nREPL.
4 | In this section we'll take a look at some practical tips to make you more productive
5 | while working on the project.
6 |
7 | == Makefile
8 |
9 | cider-nrepl has some pretty complicated Lein profiles, as it has to deal with multiple versions of
10 | Clojure and ClojureScript, plus dependency inlining with Mr. Anderson. That's why we've
11 | added a good old `Makefile` to save you the trouble of having to think about the profiles
12 | and just focus on the tasks at hand.
13 |
14 | Obviously you can still work with Lein directly, but you won't have to do this most of the time.
15 |
16 | == Testing your changes
17 |
18 | You've got several options for doing this:
19 |
20 | * Installing a snapshot of your work recently and doing tests against it (e.g. with `make install`).
21 | * Relying solely on the unit tests. You better write good unit tests, though!
22 | * Spinning new versions of nREPL from the REPL, and connecting some client to them to test your changes.
23 | * If you're already using a client that depends on cider-nrepl (e.g. CIDER) making changes to the cider-nrepl
24 | code will normally result in those changes becoming immediately available to your client.
25 |
26 | == Running the tests
27 |
28 | Just do:
29 |
30 | $ make test
31 |
32 | That's going to handle the dependency inlining behind the scenes.
33 | By default the tests are going to be run against the most recent
34 | Clojure version that's supported.
35 |
36 | == Linting
37 |
38 | cider-nrepl uses eastwood and cljfmt. Make sure your changes conform to the project's baseline by doing:
39 |
40 | $ make eastwood
41 | $ make cljfmt
42 |
43 | == Deploying
44 |
45 | Just do:
46 |
47 | $ make deploy
48 |
49 | This is going to handle the dependency inlining behind the scenes.
50 |
--------------------------------------------------------------------------------
/doc/modules/ROOT/pages/index.adoc:
--------------------------------------------------------------------------------
1 | = CIDER nREPL
2 |
3 | == Overview
4 |
5 | `cider-nrepl` aims to extend the base functionality of an nREPL server to cater
6 | to the needs of Clojure(Script) programming environments. It provides nREPL ops for common
7 | operations like:
8 |
9 | * code completion
10 | * source and documentation lookup
11 | * profiling
12 | * debugging
13 | * code reloading
14 | * find references
15 | * running tests
16 | * filtering stacktraces
17 |
18 | The ultimate goal of `cider-nrepl` is to provide a solid foundation for nREPL clients,
19 | so they don't have to reinvent the wheel all the time.
20 |
21 | Despite its name, `cider-nrepl` is editor-agnostic and is leveraged by several other
22 | Clojure editors, besides CIDER (e.g. `vim-fireplace`, `iced-vim`, Calva, CCW).
23 | While the project is officially a part of CIDER, its development is a joint
24 | venture between all interested tool authors.
25 |
26 | == Design
27 |
28 | This section documents some of the major design decisions in cider-nrepl.
29 |
30 | While in essence it's just a collection of nREPL middleware we had to
31 | make a few important design decision here and there that influenced
32 | the code base and the usability of cider-nrepl in various ways.
33 |
34 | === REPL-powered
35 |
36 | All of the middleware that are currently part of `cider-nrepl` are relying on
37 | REPL state introspection to perform their work. While we might leverage
38 | static code analysis down the road for some tasks, `cider-nrepl` will always
39 | be a REPL-first tool.
40 |
41 | === Editor Agnostic
42 |
43 | Although those middleware were created for use with CIDER almost all
44 | of them are extremely generic and can be leveraged from other editors.
45 |
46 | Projects like https://github.com/tpope/vim-fireplace[vim-fireplace] and https://github.com/SevereOverfl0w/vim-replant[vim-replant] are making use of
47 | cider-nrepl already.
48 |
49 | === Reusable Core Logic
50 |
51 | cider-nrepl tries to have as little logic as possible and mostly
52 | provides thin wrappers over existing libraries (e.g. https://github.com/alexander-yakushev/compliment[compliment],
53 | https://github.com/weavejester/cljfmt[cljfmt], etc). Much of its core functionality lives in
54 | https://github.com/clojure-emacs/orchard[orchard], so that
55 | eventually it can be used by non-nREPL clients (e.g. Socket REPL
56 | clients).
57 |
58 | Very simply put - there's very little code in cider-nrepl that's not
59 | simply wrapping code from other libraries in nREPL operations.
60 |
61 | The primary reason for this is our desire to eventually provide
62 | support for non-nREPL REPLs in CIDER, but this also means that other
63 | editors can directly leverage the work we've done so far.
64 |
65 | === ClojureScript Support
66 |
67 | We want cider-nrepl to offer feature parity between Clojure and
68 | ClojureScript, but we're not quite there yet and many features right
69 | now are Clojure-only.
70 |
71 | We'd really appreciate all the help we can get from ClojureScript
72 | hackers to make this a reality.
73 |
74 | === Isolated Runtime Dependencies
75 |
76 | Most of cider-nrepl's dependencies are processed with
77 | https://github.com/benedekfazekas/mranderson[mranderson], so that
78 | they won't collide with the dependencies of your own projects. This
79 | basically means that cider-nrepl has almost no runtime dependencies in
80 | the production artifact - just copies of the deps inlined with changed
81 | namespaces/packages. It's a bit ugly and painful, but it gets the job
82 | done.
83 |
84 | The exclusion to this rule are the artifacts that are themselves part of the
85 | CIDER ecosystem and have to runtime dependencies of their own:
86 |
87 | - https://github.com/clojure-emacs/orchard[Orchard]
88 | - https://github.com/clojure-emacs/logjam[Logjam]
89 |
90 | If someone has better ideas how to isolate our runtime dependencies -
91 | we're all ears!
92 |
93 | === Deferred Middleware Loading
94 |
95 | To improve the startup time of the nREPL server all of cider-nrepl's
96 | middleware are loaded for real only when needed.
97 |
98 | You can read more about this
99 | https://github.com/clojure-emacs/cider-nrepl/pull/438[here].
100 |
101 | We'd love to bring the support for deferred middleware loading
102 | straight to nREPL down the road.
103 |
104 | === Middleware Errors Never Hang Requests
105 |
106 | See https://github.com/clojure-emacs/cider-nrepl/pull/327[here].
107 |
--------------------------------------------------------------------------------
/doc/modules/ROOT/pages/internals.adoc:
--------------------------------------------------------------------------------
1 | = CIDER nREPL Internals
2 |
3 | == Lazy middleware loading
4 |
5 | Eager loading of all of cider-nrepl's middleware resulted in a significant impact to the
6 | startup time of the nREPL server. To mitigate this we've devised a strategy to postpone
7 | the actual initialization of some middleware until the first time it's actually used
8 | by a client.
9 |
10 | That's the reason why middleware description in `cider-nrepl` are not following the common
11 | nREPL convention to be supplied in the same file where the middleware is defined.
12 |
13 | == Dealing with unhandled exception
14 |
15 | Even though nREPL requests are asynchronous in their nature, some editors might be forced
16 | to wait for the response of a request. This is obviously going to result in lock-up in
17 | case of an unhandled exception, that's why `cider-nrepl` introduced the concept of
18 | a "safe transport" that does some reasonable handling of such errors.
19 |
20 | == Operating on unresolved symbols
21 |
22 | All middleware ops operating on some symbol that needs to be resolved would normally do the
23 | resolution themselves. They typically take as parameters `ns` and `sym`, which are current
24 | namespace and a symbol in it, and would resolve `sym` in the context of `ns`.
25 |
26 | This spares users from having to invoke `info` on every symbol before passing the resolved result
27 | to another op. It also maps well to the typical editor workflow - normally you'd be asking
28 | for some operation to be performed for some unresolved symbol in the current namespace.
29 |
30 | == ClojureScript Support
31 |
32 | Currently the ClojureScript support relies on inspecting the ClojureScript compiler
33 | state. `cider-nrepl` would fetch the compiler state from Piggieback and pass it
34 | to the underlying libraries (e.g. `orchard` and `clj-suitable`) that do something useful with it.
35 |
36 | Unfortunately the majority of the middleware don't support ClojureScript
37 | currently, as they are implemented in terms of Clojure-only libraries. Another
38 | roadblock is that `cider-nrepl` runs in a Clojure context and you have to jump
39 | through some hoops to evaluate ClojureScript code from it (e.g. pipe it to
40 | Piggieback's `eval` or evaluate it against the ClojureScript runtime directly as
41 | a string). Hopefully this will change down the road.
42 |
43 | == Dependency obfuscation
44 |
45 | cider-nrepl's dependency would conflict with the dependencies of the application using it,
46 | so we have to take some care to avoid such situation.
47 |
48 | Most of cider-nrepl's dependencies are processed with
49 | https://github.com/benedekfazekas/mranderson[mranderson], so that
50 | they won't collide with the dependencies of your own projects. This
51 | basically means that cider-nrepl doesn't have any runtime dependencies in
52 | the production artifact - just copies of the deps inlined with changed
53 | namespaces/packages.
54 |
55 | This means that `cider-nrepl` has to also take some steps to hide the inlined namespaces,
56 | so they won't pollute the results users would be interested in. Pretty much all of cider-nrepl's
57 | ops would filter out the inlined namespaces.
58 |
--------------------------------------------------------------------------------
/doc/modules/ROOT/pages/nrepl-api/supplied_middleware.adoc:
--------------------------------------------------------------------------------
1 | = Supplied nREPL middleware
2 |
3 | |===
4 | | Middleware | Version Added | Supports ClojureScript | Op(s) | Description
5 |
6 | | `wrap-apropos`
7 | | -
8 | | No
9 | | `apropos`
10 | | Pattern search for symbols and documentation.
11 |
12 | | `wrap-classpath`
13 | | -
14 | | No
15 | | `classpath`
16 | | Java classpath.
17 |
18 | | `wrap-clojuredocs`
19 | | 0.22.0
20 | | No
21 | | `clojuredocs-lookup/clojuredocs-refresh-cache`
22 | | Look up ClojureDocs.
23 |
24 | | `wrap-complete`
25 | | -
26 | | Yes
27 | | `complete`
28 | | Code completion.
29 |
30 | | `wrap-content-type`
31 | | -
32 | | No
33 | | `eval`
34 | | Rich content handling, return multimedia results beyond plain text from `eval`.
35 |
36 | | `wrap-debug`
37 | | -
38 | | No
39 | | `init-debugger/debug-input`
40 | | Establish a channel for `cider-debug` commands, use it to get debug input, and also wrap the eval op.
41 |
42 | | `wrap-format`
43 | | -
44 | | Yes
45 | | `format-(code/edn)`
46 | | Code and data formatting.
47 |
48 | | `wrap-info`
49 | | -
50 | | Yes
51 | | `info/eldoc`
52 | | File/line, arglists, docstrings and other metadata for vars.
53 |
54 | | `wrap-inspect`
55 | | -
56 | | No
57 | | `inspect-(start/refresh/pop/push)`
58 | | Inspect a Clojure expression.
59 |
60 | | `wrap-log`
61 | | 0.30.1
62 | | No
63 | | `cider/log-add-appender`, `cider/log-add-consumer`, `cider/log-analyze-stacktrace`, `cider/log-clear-appender`, `cider/log-exceptions`, `cider/log-format-event`, `cider/log-frameworks`, `cider/log-inspect-event`, `cider/log-levels`, `cider/log-loggers`, `cider/log-remove-appender`, `cider/log-remove-consumer`, `cider/log-search`, `cider/log-update-appender`, `cider/log-update-consumer`, `cider/log-threads`
64 | | Capture, debug, inspect and view log events emitted by Java logging frameworks.
65 |
66 | | `wrap-macroexpand`
67 | | -
68 | | Yes
69 | | `macroexpand/macroexpand-1/macroexpand-all/macroexpand-step`
70 | | Macroexpand a Clojure form.
71 |
72 | | `wrap-ns`
73 | | -
74 | | No
75 | | `ns-list/ns-vars/ns-path/ns-load-all/ns-aliases`
76 | | Namespace browsing & loading.
77 |
78 | | `wrap-spec`
79 | | -
80 | | No
81 | | `spec-list/spec-form/spec-example`
82 | | Spec browsing.
83 |
84 | | `wrap-profile`
85 | | -
86 | | No
87 | | `cider/profile-toggle-var`, `cider/profile-toggle-ns`, `cider/profile-summary`, `cider/profile-clear`
88 | | Middleware for manual profiling.
89 |
90 | | `wrap-refresh`
91 | | -
92 | | No
93 | | `refresh/refresh-all/refresh-clear`
94 | | Code reloading.
95 |
96 | | `wrap-resource`
97 | | 0.22
98 | | No
99 | | `resource`
100 | | Return resource path.
101 |
102 | | `wrap-stacktrace`
103 | | -
104 | | No
105 | | `stacktrace`
106 | | Cause and stacktrace analysis for exceptions.
107 |
108 | | `wrap-test`
109 | | -
110 | | No
111 | | `test-var-query/retest/test-stacktrace`
112 | | Test execution, reporting, and inspection.
113 |
114 | | `wrap-trace`
115 | | -
116 | | No
117 | | `toggle-trace-var`/`toggle-trace-ns`
118 | | Toggle tracing of a given var or ns.
119 |
120 | | `wrap-out`
121 | | -
122 | | Yes
123 | | `out-subscribe`/`out-unsubscribe`
124 | | Echo the server's output stream to client sessions.
125 |
126 | | `wrap-undef`
127 | | -
128 | | No
129 | | `undef`
130 | | Undefine a var.
131 |
132 | | `wrap-version`
133 | | -
134 | | Yes
135 | | `cider-version`
136 | | The CIDER-nREPL version map.
137 |
138 | | `wrap-xref`
139 | | 0.22
140 | | No
141 | | `fn-refs`/`fn-deps`
142 | | Function references and dependencies (other functions).
143 | |===
144 |
--------------------------------------------------------------------------------
/doc/modules/ROOT/pages/release_policy.adoc:
--------------------------------------------------------------------------------
1 | = Release Policy
2 |
3 | We're following http://semver.org/[SemVer] (as much as one can be
4 | following it when the major version is 0). At this point bumps of the
5 | minor (second) version number are considered major releases and always
6 | include new features or significant changes to existing features. Clojure API
7 | compatibility between major releases is not a (big) concern (although we try
8 | to break the API rarely and only for a good reason). nREPL API compatibility, however,
9 | is a big concern and we're very careful to limit the breaking changes there (especially
10 | when it comes to middleware that are widely used).
11 |
12 | The development cycle for the next major
13 | release starts immediately after the previous one has been
14 | shipped. Bugfix/point releases (if any) address only serious bugs and
15 | never contain new features.
16 |
17 | Here are a few examples:
18 |
19 | * 0.5.0 - Feature release
20 | * 0.5.1 - Bug-fix release
21 | * 0.5.2 - Bug-fix release
22 | * 0.6.0 - Feature release
23 |
24 | CIDER and `cider-nrepl` are released independently, but follow the same release policy overall.
25 | `cider-nrepl` is used by many other projects besides CIDER, therefore the need for it to have
26 | a release schedule that's independent from CIDER's.
27 |
--------------------------------------------------------------------------------
/doc/modules/ROOT/pages/usage.adoc:
--------------------------------------------------------------------------------
1 | = Usage
2 |
3 | == Prerequisites
4 |
5 | `cider-nrepl` supports only Clojure(Script) 1.10+ and Java 8+.
6 |
7 | Leiningen users will need to have version 2.8.3 or newer installed.
8 |
9 | == Via Leiningen
10 |
11 | Use the convenient plugin for defaults, either in your project's
12 | `project.clj` file or in the `:user` profile in
13 | `~/.lein/profiles.clj`.
14 |
15 | [source,clojure]
16 | ----
17 | :plugins [[cider/cider-nrepl "0.55.7"]]
18 | ----
19 |
20 | A minimal `profiles.clj` for CIDER would be:
21 |
22 | [source,clojure]
23 | ----
24 | {:user {:plugins [[cider/cider-nrepl "0.55.7"]]}}
25 | ----
26 |
27 | Or (if you know what you're doing) add `cider-nrepl` to your `:dev
28 | :dependencies` vector plus specific middleware to `:nrepl-middleware`
29 | under `:repl-options`.
30 |
31 | [source,clojure]
32 | ----
33 | :dependencies [[cider/cider-nrepl "0.55.7"]]
34 | :repl-options {:nrepl-middleware
35 | [cider.nrepl/wrap-apropos
36 | cider.nrepl/wrap-classpath
37 | cider.nrepl/wrap-clojuredocs
38 | cider.nrepl/wrap-complete
39 | cider.nrepl/wrap-debug
40 | cider.nrepl/wrap-format
41 | cider.nrepl/wrap-info
42 | cider.nrepl/wrap-inspect
43 | cider.nrepl/wrap-log
44 | cider.nrepl/wrap-macroexpand
45 | cider.nrepl/wrap-ns
46 | cider.nrepl/wrap-spec
47 | cider.nrepl/wrap-profile
48 | cider.nrepl/wrap-refresh
49 | cider.nrepl/wrap-reload
50 | cider.nrepl/wrap-resource
51 | cider.nrepl/wrap-stacktrace
52 | cider.nrepl/wrap-test
53 | cider.nrepl/wrap-trace
54 | cider.nrepl/wrap-out
55 | cider.nrepl/wrap-undef
56 | cider.nrepl/wrap-version
57 | cider.nrepl/wrap-xref]}
58 | ----
59 |
60 | == Via clj
61 |
62 | You can easily boot an nREPL server with the CIDER middleware loaded
63 | with the following "magic" incantation:
64 |
65 | ----
66 | clj -Sdeps '{:deps {cider/cider-nrepl {:mvn/version "0.55.7"} }}' -M -m nrepl.cmdline --middleware "[cider.nrepl/cider-middleware]"
67 | ----
68 |
69 | There are also two convenient aliases you can employ:
70 |
71 | [source,clojure]
72 | ----
73 | {...
74 | :aliases
75 | {:cider-clj {:extra-deps {org.clojure/clojure {:mvn/version "1.10.3"}
76 | cider/cider-nrepl {:mvn/version "0.55.7"}}
77 | :main-opts ["-m" "nrepl.cmdline" "--middleware" "[cider.nrepl/cider-middleware]"]}
78 |
79 | :cider-cljs {:extra-deps {org.clojure/clojure {:mvn/version "1.10.3"}
80 | org.clojure/clojurescript {:mvn/version "1.10.339"}
81 | cider/cider-nrepl {:mvn/version "0.55.7"}
82 | cider/piggieback {:mvn/version "0.5.2"}}
83 | :main-opts ["-m" "nrepl.cmdline" "--middleware"
84 | "[cider.nrepl/cider-middleware,cider.piggieback/wrap-cljs-repl]"]}}}
85 | ----
86 |
87 | Which then allow you to simply run:
88 |
89 | ----
90 | clj -M:cider-clj
91 | ----
92 |
93 | == Via embedding nREPL in your application
94 |
95 | If you're embedding nREPL in your application you'll have to start the
96 | server with CIDER's own nREPL handler.
97 |
98 | [source,clojure]
99 | ----
100 | (ns my-app
101 | (:require [nrepl.server :as nrepl-server]
102 | [cider.nrepl :refer (cider-nrepl-handler)]))
103 |
104 | (defn -main []
105 | (nrepl-server/start-server :port 7888 :handler cider-nrepl-handler))
106 | ----
107 |
108 | There's nothing magical about the `cider-nrepl-handler` - all it does is
109 | to add all middleware defined in `cider-nrepl` to nREPL's default handler.
110 | That's how CIDER's nREPL handler is created:
111 |
112 | [source,clojure]
113 | ----
114 | (def cider-middleware
115 | '[cider.nrepl/wrap-apropos
116 | cider.nrepl/wrap-classpath
117 | cider.nrepl/wrap-clojuredocs
118 | cider.nrepl/wrap-complete
119 | cider.nrepl/wrap-content-type
120 | cider.nrepl/wrap-debug
121 | cider.nrepl/wrap-enlighten
122 | cider.nrepl/wrap-format
123 | cider.nrepl/wrap-info
124 | cider.nrepl/wrap-inspect
125 | cider.nrepl/wrap-log
126 | cider.nrepl/wrap-macroexpand
127 | cider.nrepl/wrap-ns
128 | cider.nrepl/wrap-out
129 | cider.nrepl/wrap-slurp
130 | cider.nrepl/wrap-profile
131 | cider.nrepl/wrap-refresh
132 | cider.nrepl/wrap-reload
133 | cider.nrepl/wrap-resource
134 | cider.nrepl/wrap-spec
135 | cider.nrepl/wrap-stacktrace
136 | cider.nrepl/wrap-test
137 | cider.nrepl/wrap-trace
138 | cider.nrepl/wrap-tracker
139 | cider.nrepl/wrap-undef
140 | cider.nrepl/wrap-version
141 | cider.nrepl/wrap-xref])
142 |
143 | (def cider-nrepl-handler
144 | "CIDER's nREPL handler."
145 | (apply nrepl-server/default-handler (map resolve-or-fail cider-middleware)))
146 | ----
147 |
148 | TIP: You can build your own custom nREPL handler with the middleware you need by
149 | adjusting the code above.
150 |
151 | == With JBoss AS/JBoss EAP/WildFly
152 |
153 | Using the advanced features of the `info` middleware with one of the
154 | JBoss application servers requires a tweak, since JBoss modules
155 | prevent modifications to `AppClassLoader` (usually the highest
156 | modifiable classloader) from being seen by application code. To work
157 | around this, run the following code from within your application
158 | to mark that classloader as unmodifiable, and cause the lower level
159 | `clojure.lang.DynamicClassLoader` to be used instead. This code must
160 | execute prior to loading the `cider-nrepl` middleware. Note that this
161 | is only if you are deploying a standard WAR file to the application
162 | server directly. If you are using http://immutant.org/[Immutant]
163 | (1.x or 2.x), you won't need to do this.
164 |
165 | [source,clj]
166 | ----
167 | (require '[dynapath.dynamic-classpath :as cp])
168 |
169 | (extend sun.misc.Launcher$AppClassLoader
170 | cp/DynamicClasspath
171 | (assoc cp/base-readable-addable-classpath
172 | :classpath-urls #(seq (.getURLs %))
173 | :can-add? (constantly false)))
174 | ----
175 |
--------------------------------------------------------------------------------
/eastwood.clj:
--------------------------------------------------------------------------------
1 | (disable-warning
2 | {:linter :deprecations
3 | :symbol-matches #{#"^public final void java\.lang\.Thread\.stop\(\)$"}})
4 |
--------------------------------------------------------------------------------
/maint/cider/nrepl/impl/docs.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.impl.docs
2 | "Doc generation utilities"
3 | (:require
4 | [cider.nrepl :as cider-nrepl]
5 | [cider.nrepl.version :as version]
6 | [clojure.tools.cli :as cli]
7 | [clojure.java.io :as io]
8 | [clojure.string :as str]
9 | [nrepl.core :as nrepl]
10 | [nrepl.server :as server]
11 | [nrepl.transport :as transport])
12 | (:import
13 | (java.io File)))
14 |
15 | (declare -main)
16 |
17 | (def cli-options
18 | [["-f" "--file FILE" "File to write output into (defaults to standard output)"
19 | :parse-fn #(File. %) :default *out*]
20 | ["-h" "--help" "Show this help message"]
21 | ["-o" "--output OUT" "One of: raw, adoc, md" :default "adoc"
22 | :validate [#(contains? #{"raw" "adoc" "md"} %)]]])
23 |
24 | (defn- exit [status msg]
25 | (println msg)
26 | (System/exit status))
27 |
28 | (defn- usage [options-summary]
29 | (->> [(:doc (meta #'-main))
30 | ""
31 | "Usage: lein -m +maint run cider.nrepl.impl.docs [options]"
32 | ""
33 | "Options:"
34 | options-summary]
35 | (str/join \newline)))
36 |
37 | (defn- error-msg [errors]
38 | (str "The following errors occurred while parsing your command:\n\n"
39 | (str/join \newline errors)))
40 |
41 | (def built-in-ops
42 | "A list of nREPL built-in ops.
43 | We use it to filter those out in the docs generation process."
44 | #{:add-middleware :clone :close :completions :describe
45 | :eval :interrupt :load-file :lookup :ls-middleware :ls-sessions
46 | :sideloader-start :sideloader-provide :stdin :swap-middleware})
47 |
48 | (defn relevant-ops
49 | "Filter out the nREPL built-in ops."
50 | [ops]
51 | (remove (fn [[k _]] (built-in-ops k)) ops))
52 |
53 | ;; oh, kill me now
54 | (defn- markdown-escape
55 | [^String s]
56 | (.replaceAll s "([*_])" "\\\\$1"))
57 |
58 | (defn- message-slot-markdown
59 | [msg-slot-docs]
60 | (apply str (for [[k v] msg-slot-docs]
61 | (format "* `%s` %s\n" (pr-str k) (markdown-escape v)))))
62 |
63 | (defn- describe-markdown
64 | "Given a message containing the response to a verbose :describe message,
65 | generates a markdown string conveying the information therein, suitable for
66 | use in e.g. wiki pages, github, etc."
67 | [{:keys [ops versions]}]
68 | (apply str "# Supported nREPL operations
69 |
70 | generated from a verbose 'describe' response (nREPL v"
71 | (:version-string version/version)
72 | ")\n\n## Operations"
73 | (for [[op {:keys [doc optional requires returns]}] (sort (relevant-ops ops))]
74 | (str "\n\n### `" (pr-str op) "`\n\n"
75 | (markdown-escape doc) "\n\n"
76 | "###### Required parameters\n\n"
77 | (message-slot-markdown (sort requires))
78 | "\n\n###### Optional parameters\n\n"
79 | (message-slot-markdown (sort optional))
80 | "\n\n###### Returns\n\n"
81 | (message-slot-markdown (sort returns))))))
82 |
83 | ;; because `` is expected to work, this only escapes certain characters. As opposed to using + to properly escape everything.
84 | (defn- adoc-escape
85 | [s]
86 | (some-> s
87 | (str/replace #"\*(.+?)\*" "\\\\*$1*")
88 | (str/replace #"\_(.+?)\_" "\\\\_$1_")
89 | (str/escape {\` "``"})))
90 |
91 | (defn- message-slot-adoc
92 | [msg-slot-docs]
93 | (if (seq msg-slot-docs)
94 | (apply str (for [[k v] msg-slot-docs]
95 | (format "* `%s` %s\n" (pr-str k) (adoc-escape v))))
96 | "{blank}"))
97 |
98 | (defn op-name [op]
99 | (let [op-ns (namespace op)
100 | op-name (name op)]
101 | (cond->> op-name
102 | op-ns (str op-ns "/"))))
103 |
104 | (defn- describe-adoc
105 | "Given a message containing the response to a verbose :describe message,
106 | generates a asciidoc string conveying the information therein, suitable for
107 | use in e.g. wiki pages, github, etc."
108 | [{:keys [ops versions]}]
109 | (apply str "= Supported nREPL operations\n\n"
110 | "[small]#generated from a verbose 'describe' response (cider-nrepl v"
111 | (:version-string version/version)
112 | ")#\n\n== Operations"
113 | (for [[op {:keys [doc optional requires returns]}] (sort (relevant-ops ops))]
114 | (str "\n\n=== `" (op-name op) "`\n\n"
115 | (adoc-escape doc) "\n\n"
116 | "Required parameters::\n"
117 | (message-slot-adoc (sort requires))
118 | "\n\nOptional parameters::\n"
119 | (message-slot-adoc (sort optional))
120 | "\n\nReturns::\n"
121 | (message-slot-adoc (sort returns))
122 | "\n"))))
123 |
124 | (defn- format-response [format resp]
125 | (cond (= format "raw") (pr-str (select-keys resp [:ops]))
126 | (= format "md") (str "\n"
128 | (describe-markdown resp))
129 | (= format "adoc") (str "////\n"
130 | "This file was _generated_ with `lein docs`"
131 | "\n *Do not edit!*\n"
132 | "////\n"
133 | (describe-adoc resp))))
134 |
135 | (defn generate-ops-info []
136 | (let [[local remote] (transport/piped-transports)
137 | handler cider-nrepl/cider-nrepl-handler
138 | msg {:op "describe"
139 | :verbose? "true"
140 | :id "1"}]
141 | (handler (assoc msg :transport remote))
142 | (-> (nrepl/response-seq local 500)
143 | first
144 | clojure.walk/keywordize-keys)))
145 |
146 | (defn -main
147 | "Regenerate and output the ops documentation to the specified destination in the specified format."
148 | [& args]
149 | (let [{:keys [options arguments errors summary]} (cli/parse-opts args cli-options)]
150 | (cond
151 | (:help options) (exit 0 (usage summary))
152 | errors (exit 1 (error-msg errors))
153 | :else
154 | (let [file (:file options)
155 | format (:output options)
156 | resp (generate-ops-info)
157 | docs (format-response format resp)]
158 | (if (= *out* file) (println docs)
159 | (do (spit file docs)
160 | (println (str "Regenerated " (.getAbsolutePath file)))))))
161 | (shutdown-agents)))
162 |
--------------------------------------------------------------------------------
/resources/cider/nrepl/content-types.edn:
--------------------------------------------------------------------------------
1 | {"text/edn" ["edn"]
2 | "text/clojure" ["clj" "cljc"]
3 | "text/clojurescript" ["cljs"]
4 | "text/yaml" ["yml" "yaml"]}
5 |
--------------------------------------------------------------------------------
/resources/cider/nrepl/version.edn:
--------------------------------------------------------------------------------
1 | ;; This file is automatically overwriten by `make deploy` job
2 | ;; whenever we perform a deployment.
3 | "0.0.0"
4 |
--------------------------------------------------------------------------------
/src/cider/nrepl/middleware.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware
2 | "This namespace mostly exists, so that
3 | it's easy to share the list of provided middleware.
4 |
5 | Eventually the deferred middleware loading code will be probably moved here as well."
6 | {:added "0.25"})
7 |
8 | (def cider-middleware
9 | '[cider.nrepl/wrap-apropos
10 | cider.nrepl/wrap-classpath
11 | cider.nrepl/wrap-clojuredocs
12 | cider.nrepl/wrap-complete
13 | cider.nrepl/wrap-content-type
14 | cider.nrepl/wrap-debug
15 | cider.nrepl/wrap-enlighten
16 | cider.nrepl/wrap-format
17 | cider.nrepl/wrap-info
18 | cider.nrepl/wrap-inspect
19 | cider.nrepl/wrap-log
20 | cider.nrepl/wrap-macroexpand
21 | cider.nrepl/wrap-ns
22 | cider.nrepl/wrap-out
23 | cider.nrepl/wrap-slurp
24 | cider.nrepl/wrap-profile
25 | cider.nrepl/wrap-refresh
26 | cider.nrepl/wrap-reload
27 | cider.nrepl/wrap-resource
28 | cider.nrepl/wrap-spec
29 | cider.nrepl/wrap-stacktrace
30 | cider.nrepl/wrap-test
31 | cider.nrepl/wrap-trace
32 | cider.nrepl/wrap-tracker
33 | cider.nrepl/wrap-undef
34 | cider.nrepl/wrap-version
35 | cider.nrepl/wrap-xref])
36 |
37 | (def ops-that-can-eval
38 | "Set of nREPL ops that can lead to code being evaluated."
39 | #{"eval" "load-file"
40 | "refresh" "refresh-all" "refresh-clear"
41 | "cider.clj-reload/reload" "cider.clj-reload/reload-all" "cider.clj-reload/reload-clear"
42 | "toggle-trace-var" "toggle-trace-ns"
43 | "undef" "undef-all"
44 | "cider/profile-toggle-var" "cider/profile-toggle-ns"})
45 |
--------------------------------------------------------------------------------
/src/cider/nrepl/middleware/DebugSupport.java:
--------------------------------------------------------------------------------
1 | package cider.nrepl.middleware;
2 |
3 | import clojure.lang.*;
4 |
5 | /**
6 | * Contains instrumentation helpers for cider.nrepl.middleware.debug. The main
7 | * purpose of having these helpers in Java is reducing the instrumentation
8 | * footprint (measured in bytecode size). Invoking Java methods usually takes
9 | * fewer bytecode instructions than a corresponding Clojure function. Java also
10 | * allows us to have primitive overrides which further reduces overhead when
11 | * instrumenting code that contains primitives, but also preserves type hints
12 | * (so instrumented code behaves closer to original code).
13 | *
14 | * The reason we care about bytecode size is 65KB method limit that JVM imposes.
15 | */
16 | public class DebugSupport {
17 |
18 | private static volatile IFn breakFn = null;
19 |
20 | public static Object doBreak(Object coor, Object val, Object locals, Object STATE__) {
21 | if (breakFn == null)
22 | breakFn = (IFn)RT.var("cider.nrepl.middleware.debug", "break");
23 | return breakFn.invoke(coor, val, locals, STATE__);
24 | }
25 |
26 | public static long doBreak(Object coor, long val, Object locals, Object STATE__) {
27 | return (long)doBreak(coor, Numbers.num(val), locals, STATE__);
28 | }
29 |
30 | public static double doBreak(Object coor, double val, Object locals, Object STATE__) {
31 | return (double)doBreak(coor, Numbers.num(val), locals, STATE__);
32 | }
33 |
34 | // The purpose of the following assoc methods is to build a locals map.
35 | // Chaining such assoc calls is more bytecode-compact than a single
36 | // RT.mapUniqueKeys(...) because the latter constructs an array (load each
37 | // key and value onto the stack, save them into the array) and boxes
38 | // primitives (invocations of Numbers.num). Additionally, in this custom
39 | // method we turn string keys into symbols, which means we don't have to
40 | // generate symbols at the callsite (in the instrumented method). This saves
41 | // bytecode because LDC of a constant string is more compact than
42 | // ALOAD+GETFIELD of an interned symbol.
43 |
44 | public static IPersistentMap assoc(Object map, Object key, Object value) {
45 | return ((IPersistentMap)map).assoc(Symbol.intern(null, (String)key), value);
46 | }
47 |
48 | public static IPersistentMap assoc(Object map, Object key, long value) {
49 | return assoc(map, key, Numbers.num(value));
50 | }
51 |
52 | public static IPersistentMap assoc(Object map, Object key, double value) {
53 | return assoc(map, key, Numbers.num(value));
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/cider/nrepl/middleware/apropos.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.apropos
2 | "Search symbols and docs matching a regular expression"
3 | {:author "Jeff Valk"}
4 | (:require
5 | [cider.nrepl.middleware.util.coerce :as util.coerce]
6 | [cider.nrepl.middleware.util.error-handling :refer [with-safe-transport]]
7 | [orchard.apropos :as apropos]))
8 |
9 | ;;; ## Middleware
10 |
11 | (defn- msg->var-query-map [msg]
12 | (let [ns-query (select-keys msg [:exactly :project? :load-project-ns? :has-tests?
13 | :include-regexps :exclude-regexps])]
14 | (cond-> msg
15 | ;; Compatibility for the pre-var-query API
16 | (:privates? msg)
17 | (assoc-in [:var-query :private?] true)
18 |
19 | (:query msg)
20 | (assoc-in [:var-query :search] (:query msg))
21 |
22 | (and (:query msg)
23 | (not (:case-sensitive? msg)))
24 | (update-in [:var-query :search] #(format "(?i:%s)" %))
25 |
26 | (:docs? msg)
27 | (assoc-in [:var-query :search-property] :doc)
28 |
29 | (:docs? msg)
30 | (assoc :full-doc? true)
31 |
32 | true
33 | (assoc-in [:var-query :ns-query] ns-query)
34 |
35 | true
36 | (update :var-query util.coerce/var-query)
37 |
38 | (:ns msg)
39 | (update :ns (comp find-ns symbol)))))
40 |
41 | (defn apropos [msg]
42 | {:apropos-matches (-> msg
43 | msg->var-query-map
44 | apropos/find-symbols)})
45 |
46 | (defn handle-apropos [handler msg]
47 | (with-safe-transport handler msg
48 | "apropos" apropos))
49 |
--------------------------------------------------------------------------------
/src/cider/nrepl/middleware/classpath.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.classpath
2 | (:require
3 | [cider.nrepl.middleware.util.error-handling :refer [with-safe-transport]]
4 | [clojure.java.io :as io]
5 | [orchard.java.classpath :as cp]
6 | [orchard.misc :as misc]))
7 |
8 | (defn file-url?
9 | [u]
10 | (and (misc/url? u)
11 | (= (.getProtocol ^java.net.URL u) "file")))
12 |
13 | (defn classpath-reply [_msg]
14 | {:classpath (->> (cp/classpath)
15 | (filter file-url?)
16 | (map io/as-file)
17 | (map str))})
18 |
19 | (defn handle-classpath [handler msg]
20 | (with-safe-transport handler msg
21 | "classpath" classpath-reply))
22 |
--------------------------------------------------------------------------------
/src/cider/nrepl/middleware/clojuredocs.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.clojuredocs
2 | "This middleware allows you to query for data ClojureDocs.
3 | It's a very simple wrapper around `orchard.clojuredocs`."
4 | {:added "0.22"}
5 | (:require
6 | [cider.nrepl.middleware.util :as util]
7 | [cider.nrepl.middleware.util.error-handling :refer [with-safe-transport]]
8 | [orchard.clojuredocs :as docs]))
9 |
10 | (defn- clojuredocs-lookup-reply [{:keys [ns sym]}]
11 | (try
12 | ;; TODO: change this to `resolve-doc` once I've added the extra arity there
13 | (if-let [doc (docs/resolve-and-find-doc (symbol ns) (symbol sym))]
14 | {:clojuredocs (util/transform-value doc)}
15 | {:status :no-doc})
16 | ;; TODO: Handle a missing ns directly in Orchard
17 | (catch Exception _
18 | {:status :no-doc})))
19 |
20 | (defn clojuredocs-refresh-cache-reply [{:keys [export-edn-url]}]
21 | (docs/clean-cache!)
22 | (if export-edn-url
23 | (docs/update-cache! export-edn-url)
24 | (docs/update-cache!))
25 | {:status :ok})
26 |
27 | (defn handle-clojuredocs [handler msg]
28 | (with-safe-transport handler msg
29 | "clojuredocs-refresh-cache" clojuredocs-refresh-cache-reply
30 | "clojuredocs-lookup" clojuredocs-lookup-reply))
31 |
--------------------------------------------------------------------------------
/src/cider/nrepl/middleware/complete.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.complete
2 | "Code completion middleware.
3 | Delegates to the compliment library for the heavy lifting.
4 | Uses clj-suitable for ClojureScript completion."
5 | (:require
6 | [cider.nrepl.middleware.util.cljs :as cljs]
7 | [cider.nrepl.middleware.util.error-handling :refer [with-safe-transport]]
8 | [compliment.core :as complete]
9 | [compliment.utils :as complete-utils]
10 | [orchard.misc :as misc]
11 | [suitable.compliment.sources.cljs :as suitable-sources]))
12 |
13 | (def shadow-cljs-present?
14 | (try (require 'shadow.cljs.devtools.api) true
15 | (catch Throwable _ false)))
16 |
17 | ;; controls if dynamic cljs code completions are active
18 | (def suitable-enabled? true)
19 |
20 | (def suitable-complete-for-nrepl
21 | (when suitable-enabled?
22 | (requiring-resolve 'suitable.complete-for-nrepl/complete-for-nrepl)))
23 |
24 | (def clj-sources
25 | "A list of Clojure completion sources for compliment."
26 | [:compliment.sources.vars/vars
27 | :compliment.sources.namespaces/namespaces
28 | :compliment.sources.classes/classes
29 | :compliment.sources.class-members/members
30 | :compliment.sources.class-members/static-members
31 | :compliment.sources.keywords/keywords
32 | :compliment.sources.local-bindings/local-bindings
33 | :compliment.sources.resources/resources
34 | :compliment.sources.special-forms/special-forms
35 | :compliment.sources.special-forms/literals])
36 |
37 | (def cljs-sources
38 | "A list of ClojureScript completion sources for compliment."
39 | [::suitable-sources/cljs-source
40 | ;; The local binding analysis done by
41 | ;; :compliment.sources.local-bindings/local-bindings doesn't perform any
42 | ;; evaluation or execution of the context form. Thus, it is independent of
43 | ;; the actual host platform differences. Given that, we can use that same
44 | ;; source for ClojureScript completion.
45 | :compliment.sources.local-bindings/local-bindings])
46 |
47 | (defn complete
48 | [{:keys [ns prefix symbol context extra-metadata enhanced-cljs-completion? sort-order]
49 | :as msg}]
50 | ;; TODO: Drop legacy symbol param in version 1.0
51 | (let [prefix (str (or prefix symbol))
52 | completion-opts {:ns (misc/as-sym ns)
53 | :context context
54 | :sort-order (or (some-> sort-order keyword) :by-length)
55 | :extra-metadata (set (map keyword extra-metadata))}]
56 | (if-let [cljs-env (cljs/grab-cljs-env msg)]
57 | ;; ClojureScript completion
58 | (binding [suitable-sources/*compiler-env* cljs-env]
59 | ;; First we get whatever candidates we can from the ClojureScript compiler source
60 | (cond-> (complete/completions prefix (merge completion-opts {:sources cljs-sources}))
61 | ;; and we optionally append to them dynamically obtained candidates
62 | ;; See https://github.com/clojure-emacs/clj-suitable#how-does-it-work for details
63 | (and suitable-enabled? enhanced-cljs-completion?)
64 | (concat (suitable-complete-for-nrepl (assoc msg :symbol prefix)))))
65 | ;; Clojure completion
66 | (complete/completions prefix (merge completion-opts {:sources clj-sources})))))
67 |
68 | (defn completion-doc
69 | [{:keys [ns sym symbol] :as msg}]
70 | ;; TODO: Drop legacy symbol param in version 1.0
71 | (let [sym (str (or sym symbol))
72 | ns (misc/as-sym ns)]
73 | (if-let [cljs-env (cljs/grab-cljs-env msg)]
74 | (binding [suitable-sources/*compiler-env* cljs-env]
75 | (complete/documentation sym ns {:sources cljs-sources}))
76 | (complete/documentation sym ns {:sources clj-sources}))))
77 |
78 | (defn complete-reply [msg]
79 | {:completions (complete msg)})
80 |
81 | (defn doc-reply
82 | [msg]
83 | {:completion-doc (completion-doc msg)})
84 |
85 | (defn flush-caches-reply
86 | [_msg]
87 | (complete-utils/flush-caches)
88 | {})
89 |
90 | (defn handle-complete [handler msg]
91 | (with-safe-transport handler msg
92 | "complete" complete-reply
93 | "complete-doc" doc-reply
94 | "complete-flush-caches" flush-caches-reply))
95 |
--------------------------------------------------------------------------------
/src/cider/nrepl/middleware/content_type.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.content-type
2 | "Rich content handling for CIDER.
3 | Mostly derived from the pprint middleware.
4 |
5 | ---
6 |
7 | In the long ago, @technomancy [1] talked about his vision for using
8 | nREPL to support multimedia results beyond plain text, ala DrRacket
9 | and other \"rich\" REPLs. There was an initial cut at this [2],
10 | which never became part of the mainline Emacs tooling.
11 |
12 | The goal of this module is to provide some support for recognizing
13 | multimedia objects (images and URIs thereto) as the result of
14 | evaluation, so that they can be rendered by a REPL.
15 |
16 | The design of this module is based heavily on RFC-2045 [3] which
17 | describes messages packaged with `Content-Type`,
18 | `Content-Transfer-Encoding` and of course a body in that it seeks to
19 | provide decorated responses which contain metadata which a client
20 | can use to provide a rich interpretation.
21 |
22 | There's also RFC-2017 [4] which defines the `message/external-body`
23 | MIME type for defining messages which don't contain their own
24 | bodies.
25 |
26 | The basic architecture of this changeset is that eval results are
27 | inspected, and matched against two fundamental supported cases. One
28 | is that the value is actually a binary Java image, which can be MIME
29 | encoded and transmitted back directly. The other is that the object
30 | is some variant of a URI (such as a file naming an image or other
31 | content) which cannot be directly serialized. In this second case we
32 | send an RFC-2017 response which provides the URL from which a client
33 | could request the nREPL server slurp the desired content.
34 |
35 | Hence the slurp middleware which slurps URLs and produces MIME coded
36 | data.
37 |
38 | ---
39 |
40 | [1] https://groups.google.com/forum/#!topic/clojure-tools/rkmJ-5086RY
41 | [2] https://github.com/technomancy/nrepl-discover/blob/master/src/nrepl/discover/samples.clj#L135
42 | [3] https://tools.ietf.org/html/rfc2045
43 | [4] https://tools.ietf.org/html/rfc2017"
44 | {:authors ["Reid 'arrdem' McKenzie "
45 | "Arne 'plexus' Brasseur "]}
46 | (:require
47 | [cider.nrepl.middleware.slurp :refer [slurp-reply]])
48 | (:import
49 | [java.awt.image RenderedImage]
50 | [java.io ByteArrayOutputStream File OutputStream]
51 | [java.net URI URL]
52 | java.nio.file.Path
53 | javax.imageio.ImageIO
54 | nrepl.transport.Transport))
55 |
56 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
57 |
58 | (defprotocol URLCoercable
59 | (as-url-string [o]))
60 |
61 | (extend-protocol URLCoercable
62 | Path
63 | (as-url-string [^Path p]
64 | (.. p normalize toUri toString))
65 |
66 | File
67 | (as-url-string [^File f]
68 | (.. f getCanonicalFile toURI toString))
69 |
70 | URI
71 | (as-url-string [^URI u]
72 | (.toString u))
73 |
74 | URL
75 | (as-url-string [^URL u]
76 | (.toString u)))
77 |
78 | (defn external-body-response
79 | "Partial response map having an external-body content-type referring to the given URL.
80 |
81 | See RFC-2017: Definition of the URL MIME External-Body Access-Type."
82 | [value]
83 | {:content-type ["message/external-body"
84 | {"access-type" "URL"
85 | "URL" (as-url-string value)}]
86 | :body ""})
87 |
88 | (defmulti content-type-response
89 | "Consumes an nREPL response, having a `:value`. If the `:value` is of a
90 | recognized type, then rewrite the response to have a `:content-type` being a
91 | MIME type of the content, and a `:body` to re-use the RFC term for the message
92 | payload.
93 |
94 | Dispatches on the [[clojure.core/type]] of the value, i.e. the metadata
95 | `:type` value, or the class."
96 | (comp type :value))
97 |
98 | (defmethod content-type-response :default [response]
99 | response)
100 |
101 | (defmethod content-type-response URI [{:keys [value] :as response}]
102 | (merge response (external-body-response value)))
103 |
104 | (defmethod content-type-response URL [{:keys [value] :as response}]
105 | (merge response (external-body-response value)))
106 |
107 | (defmethod content-type-response File [{^File file :value :as response}]
108 | (if (.exists file)
109 | (merge response (external-body-response file))
110 | response))
111 |
112 | (defmethod content-type-response java.awt.image.RenderedImage [{^java.awt.image.RenderedImage image :value :as response}]
113 | (with-open [bos (ByteArrayOutputStream.)]
114 | (merge response (when (ImageIO/write image "png" ^OutputStream bos)
115 | (slurp-reply "" ["image/png" {}] (.toByteArray bos))))))
116 |
117 | (defn content-type-transport
118 | "Transport proxy which allows this middleware to intercept responses
119 | and inspect / alter them."
120 | [^Transport transport]
121 | (reify Transport
122 | (recv [_this]
123 | (.recv transport))
124 |
125 | (recv [_this timeout]
126 | (.recv transport timeout))
127 |
128 | (send [_this response]
129 | (.send transport (content-type-response response)))))
130 |
131 | (defn handle-content-type
132 | "Handler for inspecting the results of the `eval` op, attempting to
133 | detect content types and generate richer responses when content
134 | information is available.
135 |
136 | Requires that the user opt-in by providing the `content-type` key in
137 | nREPL requests, same as the pprint middleware.
138 |
139 | Note that this middleware makes no attempt to prevent
140 | pretty-printing of the eval result, which could lead to double
141 | output in some REPL clients."
142 | [handler msg]
143 | (let [{:keys [op transport content-type]} msg]
144 | (handler (if (and (= "eval" op) content-type)
145 | (assoc msg :transport (content-type-transport transport))
146 | msg))))
147 |
--------------------------------------------------------------------------------
/src/cider/nrepl/middleware/enlighten.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.enlighten
2 | "Instrument user code to \"light up\" when it runs.
3 | The instrumented code will report the value of local variables and
4 | report its return value.
5 | Implemented as an extension of the debugger."
6 | {:author "Artur Malabarba"}
7 | (:require
8 | [cider.nrepl.middleware.debug :as d]
9 | [cider.nrepl.middleware.util.instrument :as ins]))
10 |
11 | (defn pr-very-short [val]
12 | (binding [*print-length* 3, *print-level* 2]
13 | (pr-str val)))
14 |
15 | (defn send-if-local
16 | "If locals contains sym, send its value over the debug channel.
17 | The value is added to `extras` under :debug-value, and `extras` is
18 | sent over the debug channel with the :enlighten status."
19 | [sym extras locals]
20 | (when (contains? locals sym)
21 | ;; Enlightened values are inlined, so let's keep them short.
22 | (->> (locals sym) pr-very-short
23 | (assoc extras :status :enlighten
24 | :erase-previous :true
25 | :debug-value)
26 | d/debugger-send)))
27 |
28 | (defn wrap-function-form
29 | "Wrap a form representing a function/macro/special-form call.
30 | Return an equivalent form, instrumented to work with enlighten.
31 |
32 | Currently this only instruments forms that could run several times
33 | in a single evaluation. This is necessary so that the client can
34 | clean-up overlays from previous evaluations."
35 | [[head & args :as form] {:keys [coor]}]
36 | (let [erase `(d/debugger-send (assoc (:msg ~'STATE__)
37 | :coor ~coor
38 | :status :enlighten
39 | :erase-previous :true))]
40 | (case head
41 | ;; This is still compile-time, so return a form, not a function.
42 | fn* `#(do ~erase (apply ~form %&))
43 | ;; `defn` expands to `(def name (fn ...))`.
44 | def (let [[name val] args]
45 | (if (and (seq? val) (= 'fn* (first val)))
46 | (list head name
47 | `#(do ~erase
48 | (let [out# (apply ~val %&)]
49 | ;; `defn` is the only non-symbol form that we enlighten.
50 | (->> (assoc (:msg ~'STATE__)
51 | :coor ~coor
52 | :status :enlighten
53 | :debug-value (pr-very-short out#))
54 | d/debugger-send)
55 | out#)))
56 | form))
57 | ;; Ensure that any `recur`s remain in the tail position.
58 | loop* (list* head (first args) erase (rest args))
59 | ;; Else.
60 | form)))
61 |
62 | (defmacro light-form
63 | "Return the result of form, and maybe enlighten it."
64 | [form {:keys [coor] :as extras} original-form]
65 | (cond
66 | (symbol? original-form) `(do
67 | (send-if-local '~original-form
68 | (assoc (:msg ~'STATE__) :coor ~coor)
69 | ~(d/locals-capturer &env))
70 | ~form)
71 | (seq? form) (wrap-function-form form extras)
72 | :else form))
73 |
74 | (defn light-reader [form]
75 | (ins/tag-form-recursively form #'light-form))
76 |
77 | ;;; Middleware
78 | (defn eval-with-enlighten
79 | "Like `eval`, but also enlighten code."
80 | [form]
81 | (let [form1 `(d/with-initial-debug-bindings
82 | ~(ins/instrument-tagged-code (light-reader form)))]
83 | ;; (ins/print-form form1 true)
84 | (eval form1)))
85 |
86 | (defn handle-enlighten
87 | [h {:keys [op enlighten] :as msg}]
88 | (if (and (= op "eval") enlighten)
89 | (h (assoc msg :eval "cider.nrepl.middleware.enlighten/eval-with-enlighten"))
90 | (h msg)))
91 |
--------------------------------------------------------------------------------
/src/cider/nrepl/middleware/format.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.format
2 | "Code and EDN formatting functionality."
3 | (:refer-clojure :exclude [read-string])
4 | (:require
5 | [cider.nrepl.middleware.util.error-handling :refer [with-safe-transport]]
6 | [cljfmt.core :as fmt]
7 | [clojure.string :as str]
8 | [clojure.tools.reader.edn :as edn]
9 | [clojure.tools.reader.reader-types :as readers]
10 | [clojure.walk :as walk]
11 | [nrepl.middleware.print :as print])
12 | (:import
13 | (java.io StringWriter)))
14 |
15 | ;;; Code formatting
16 | (defn- keyword->symbol [kw]
17 | (.sym ^clojure.lang.Keyword kw))
18 |
19 | (defn- generate-user-indents [indents]
20 | (reduce-kv
21 | (fn [acc kw rule]
22 | (assoc acc
23 | (keyword->symbol kw)
24 | (walk/postwalk #(cond-> % (string? %) keyword) rule)))
25 | fmt/default-indents
26 | indents))
27 |
28 | (defn format-code-reply
29 | [{:keys [code options]}]
30 | (let [opts (some-> options
31 | (select-keys [:indents :alias-map])
32 | (update :indents generate-user-indents)
33 | (update :alias-map #(reduce-kv (fn [m k v] (assoc m (name k) v)) {} %)))]
34 | {:formatted-code (fmt/reformat-string code opts)}))
35 |
36 | ;;; EDN formatting
37 | (defn- read-edn
38 | "Returns a vector of EDN forms, read from the string s."
39 | [s]
40 | (let [reader (readers/string-push-back-reader s)
41 | sentinel (Object.)]
42 | (loop [forms []]
43 | (let [form (edn/read {:eof sentinel
44 | :default (fn [_tag value]
45 | (pr-str value))}
46 | reader)]
47 | (if (= sentinel form)
48 | forms
49 | (recur (conj forms form)))))))
50 |
51 | (defn- format-edn
52 | [edn print-fn]
53 | (->> (read-edn edn)
54 | (map (fn [value]
55 | (let [writer (StringWriter.)]
56 | (print-fn value writer)
57 | (str writer))))
58 | (str/join "\n")
59 | (str/trim)))
60 |
61 | (defn format-edn-reply
62 | [{:keys [edn ::print/print-fn]}]
63 | {:formatted-edn (format-edn edn print-fn)})
64 |
65 | ;;; Middleware op handling
66 | (defn handle-format [handler msg]
67 | (with-safe-transport handler msg
68 | "format-code" format-code-reply
69 | "format-edn" format-edn-reply))
70 |
--------------------------------------------------------------------------------
/src/cider/nrepl/middleware/ns.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.ns
2 | (:refer-clojure :exclude [ns-aliases])
3 | (:require
4 | [cider.nrepl.middleware.util :as util]
5 | [cider.nrepl.middleware.util.cljs :as cljs]
6 | [cider.nrepl.middleware.util.coerce :as util.coerce]
7 | [cider.nrepl.middleware.util.error-handling :refer [with-safe-transport]]
8 | [cider.nrepl.middleware.util.meta :as um]
9 | [orchard.cljs.analysis :as cljs-analysis]
10 | [clojure.java.io :as io]
11 | [orchard.info :as info]
12 | [orchard.misc :as misc]
13 | [orchard.namespace :as ns]
14 | [orchard.query :as query]))
15 |
16 | (defn ns-list-vars-by-name
17 | "Return a list of vars named `name` amongst all namespaces.
18 | `name` is a symbol."
19 | [name]
20 | (->> (mapcat ns-interns (all-ns))
21 | (filter #(= (first %) name))
22 | (map second)))
23 |
24 | (defn ns-vars-clj [ns & [var-query]]
25 | (->> {:ns-query {:exactly [ns]}}
26 | (merge var-query)
27 | util.coerce/var-query
28 | query/vars
29 | (map (comp str :name meta))
30 | sort))
31 |
32 | (defn ns-vars-with-meta-clj [ns & [var-query]]
33 | (->> {:ns-query {:exactly [ns]}}
34 | (merge var-query)
35 | util.coerce/var-query
36 | query/vars
37 | (map meta)
38 | (map (juxt (comp str :name) um/relevant-meta))
39 | (into (sorted-map))))
40 |
41 | (defn ns-list-cljs [env]
42 | (->> (cljs-analysis/all-ns env)
43 | keys
44 | (map name)
45 | sort))
46 |
47 | (defn ns-vars-cljs [env ns & [var-query]]
48 | (let [fetch-vars (if (:private? var-query)
49 | (partial cljs-analysis/ns-interns-from-env env)
50 | (partial cljs-analysis/public-vars env))]
51 | (->> (symbol ns)
52 | fetch-vars
53 | keys
54 | (map name)
55 | sort)))
56 |
57 | (defn ns-vars-with-meta-cljs [env ns & [var-query]]
58 | (let [fetch-vars (if (:private? var-query)
59 | (partial cljs-analysis/ns-interns-from-env env)
60 | (partial cljs-analysis/public-vars env))]
61 | (->> (symbol ns)
62 | fetch-vars
63 | (misc/update-vals (comp um/relevant-meta :meta))
64 | (misc/update-keys name)
65 | (into (sorted-map)))))
66 |
67 | (defn ns-list [{:keys [filter-regexps] :as msg}]
68 | (if-let [cljs-env (cljs/grab-cljs-env msg)]
69 | (ns-list-cljs cljs-env)
70 | (ns/loaded-namespaces filter-regexps)))
71 |
72 | (defn ns-vars [{:keys [ns var-query] :as msg}]
73 | (if-let [cljs-env (cljs/grab-cljs-env msg)]
74 | (ns-vars-cljs cljs-env ns var-query)
75 | (ns-vars-clj ns var-query)))
76 |
77 | (defn ns-vars-with-meta [{:keys [ns var-query] :as msg}]
78 | (if-let [cljs-env (cljs/grab-cljs-env msg)]
79 | (ns-vars-with-meta-cljs cljs-env ns var-query)
80 | (ns-vars-with-meta-clj ns var-query)))
81 |
82 | (defn ns-path [{:keys [ns] :as msg}]
83 | (if-let [cljs-env (cljs/grab-cljs-env msg)]
84 | (:file (info/info* {:dialect :cljs
85 | :env cljs-env
86 | :sym (symbol ns)}))
87 | (str (ns/canonical-source ns))))
88 |
89 | (defn ns-list-reply [msg]
90 | {:ns-list (ns-list msg)})
91 |
92 | (defn ns-list-vars-by-name-reply [{:keys [name]}]
93 | {:var-list (pr-str (ns-list-vars-by-name (symbol name)))})
94 |
95 | (defn ns-vars-reply
96 | [msg]
97 | {:ns-vars (ns-vars msg)})
98 |
99 | (defn ns-vars-with-meta-reply
100 | [msg]
101 | {:ns-vars-with-meta (ns-vars-with-meta msg)})
102 |
103 | (defn- ns-path-reply [msg]
104 | (let [v (ns-path msg)
105 | cljs? (cljs/grab-cljs-env msg)]
106 | {:path v
107 | :url (if cljs?
108 | (or (some-> v io/resource str)
109 | v)
110 | v)}))
111 |
112 | (defn- ns-load-all-reply
113 | [_msg]
114 | {:loaded-ns (ns/load-project-namespaces)})
115 |
116 | (defn- ns-aliases-clj [ns]
117 | (->> (symbol ns)
118 | (clojure.core/ns-aliases)
119 | (misc/update-vals ns-name)
120 | (util/transform-value)))
121 |
122 | (defn- ns-aliases-cljs [env ns]
123 | (->> (cljs-analysis/ns-aliases env ns)
124 | (remove (fn [[k v]] (= k v)))
125 | (into {})
126 | (util/transform-value)))
127 |
128 | (defn ns-aliases [{:keys [ns] :as msg}]
129 | (if-let [cljs-env (cljs/grab-cljs-env msg)]
130 | (ns-aliases-cljs cljs-env ns)
131 | (ns-aliases-clj ns)))
132 |
133 | (defn- ns-aliases-reply [msg]
134 | {:ns-aliases (ns-aliases msg)})
135 |
136 | (defn handle-ns [handler msg]
137 | (with-safe-transport handler msg
138 | "ns-list" ns-list-reply
139 | "ns-list-vars-by-name" ns-list-vars-by-name-reply
140 | "ns-vars" ns-vars-reply
141 | "ns-vars-with-meta" ns-vars-with-meta-reply
142 | "ns-path" ns-path-reply
143 | "ns-load-all" ns-load-all-reply
144 | "ns-aliases" ns-aliases-reply))
145 |
--------------------------------------------------------------------------------
/src/cider/nrepl/middleware/profile.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.profile
2 | "Simplistic manual tracing profiler for coarse usecases where the accuracy
3 | doesn't matter much and you already know which functions to measure."
4 | (:require
5 | [cider.nrepl.middleware.util.error-handling :refer [with-safe-transport]]
6 | [cider.nrepl.middleware.inspect :as inspect-mw]
7 | [nrepl.misc :refer [response-for]]
8 | [orchard.profile :as profile]))
9 |
10 | (defn toggle-var-reply [{:keys [ns sym] :as msg}]
11 | (if-let [v (ns-resolve (symbol ns) (symbol sym))]
12 | (if (profile/profiled? v)
13 | (do (profile/unprofile-var v)
14 | (response-for msg :status :done :value "unprofiled"))
15 | (if (profile/profilable? v)
16 | (do (profile/profile-var v)
17 | (response-for msg :status :done :value "profiled"))
18 | (response-for msg :status [:done :profile-invalid-var])))
19 | (response-for msg :status #{:profile-no-such-var :done})))
20 |
21 | (defn toggle-ns-reply [{:keys [ns transport] :as msg}]
22 | (let [profiled? (profile/toggle-profile-ns (symbol ns))]
23 | (response-for msg
24 | :status :done
25 | :value (if profiled? "profiled" "unprofiled"))))
26 |
27 | (defn summary-reply
28 | "Return profiling summary optimized for viewing through CIDER inspector."
29 | [{:keys [transport] :as msg}]
30 | (inspect-mw/inspect-reply*
31 | (assoc msg
32 | :max-coll-size 1 ;; To narrow :samples column.
33 | :view-mode :table)
34 | (profile/summary-for-inspector)))
35 |
36 | (defn clear-reply [msg]
37 | (profile/clear)
38 | (response-for msg :status [:done :profile-cleared]))
39 |
40 | (defn handle-profile [handler msg]
41 | (with-safe-transport handler msg
42 | "cider/profile-toggle-var" toggle-var-reply
43 | "cider/profile-toggle-ns" toggle-ns-reply
44 | "cider/profile-summary" summary-reply
45 | "cider/profile-clear" clear-reply))
46 |
--------------------------------------------------------------------------------
/src/cider/nrepl/middleware/refresh.clj:
--------------------------------------------------------------------------------
1 | (ns ^{:clojure.tools.namespace.repl/load false
2 | :clojure.tools.namespace.repl/unload false} cider.nrepl.middleware.refresh
3 | ;; The above metadata prevents reloading of this namespace - otherwise,
4 | ;; `refresh-tracker` is reset with every refresh. This only has any effect
5 | ;; when developing cider-nrepl itself, or when cider-nrepl is used as a
6 | ;; checkout dependency - tools.namespace doesn't reload source in JARs.
7 | (:require
8 | [cider.nrepl.middleware.util :refer [respond-to]]
9 | [cider.nrepl.middleware.util.reload :as reload-utils]
10 | [clojure.main :refer [repl-caught]]
11 | [clojure.tools.namespace.dir :as dir]
12 | [clojure.tools.namespace.find :as find]
13 | [clojure.tools.namespace.reload :as reload]
14 | [clojure.tools.namespace.track :as track]))
15 |
16 | (defonce ^:private refresh-tracker (volatile! (track/tracker)))
17 |
18 | (defn- user-refresh-dirs
19 | "Directories to watch and reload, as configured by the user.
20 |
21 | See `clojure.tools.namespace.repl/set-refresh-dirs`.
22 |
23 | The var is resolved at runtime to get the \"real\" clojure.tools.namespace,
24 | not the mranderson-ized version bundled with CIDER. Returns `nil` if c.t.n.r
25 | isn't loaded. Returns `[]` if c.t.n.r is loaded but no custom dirs have been
26 | set."
27 | []
28 | (some-> (symbol "clojure.tools.namespace.repl" "refresh-dirs")
29 | resolve
30 | deref))
31 |
32 | ;; We construct the keyword at runtime here because namespaced keyword literals
33 | ;; in clojure.tools.namespace.repl itself might be rewritten by mranderson - in
34 | ;; this case, we still want to disable reloading of namespaces that a user has
35 | ;; added the (non-rewritten) metadata to.
36 | (defn- load-disabled?
37 | [sym]
38 | (false? (get (meta (find-ns sym))
39 | (keyword "clojure.tools.namespace.repl" "load"))))
40 |
41 | ;; As documented in clojure.tools.namespace.repl/disable-reload!,
42 | ;; ^{:c.t.n.r/load false} implies ^{:c.t.n.r/unload false}
43 | (defn- unload-disabled?
44 | [sym]
45 | (or (load-disabled? sym)
46 | (false? (get (meta (find-ns sym))
47 | (keyword "clojure.tools.namespace.repl" "unload")))))
48 |
49 | (defn- remove-disabled
50 | [tracker]
51 | (-> tracker
52 | (update-in [::track/load] #(remove load-disabled? %))
53 | (update-in [::track/unload] #(remove unload-disabled? %))))
54 |
55 | (defn- reloading-reply
56 | [{reloading ::track/load}
57 | msg]
58 | (respond-to msg :reloading reloading))
59 |
60 | (defn- result-reply
61 | [{error ::reload/error
62 | error-ns ::reload/error-ns}
63 | msg]
64 |
65 | (if error
66 | (reload-utils/error-reply {:error error :error-ns error-ns} msg)
67 | (respond-to msg :status :ok)))
68 |
69 | (defn after-reply
70 | [{error ::reload/error}
71 | msg]
72 | (reload-utils/after-reply error msg))
73 |
74 | (def ^{:added "0.48.0"} client-requested-clear?
75 | "Whether the nREPL client indicated that a `clear` is desired."
76 | (atom false))
77 |
78 | (defn- refresh-reply
79 | [{:keys [dirs session id] :as msg}]
80 | (let [{:keys [exec]} (meta session)]
81 | (exec id
82 | (fn []
83 | (locking refresh-tracker
84 | (when @client-requested-clear?
85 | (vreset! refresh-tracker (track/tracker)))
86 | (vswap! refresh-tracker
87 | (fn [tracker]
88 | (try
89 | (reload-utils/before-reply msg)
90 |
91 | (-> tracker
92 | (dir/scan-dirs (or (seq dirs) (user-refresh-dirs))
93 | (select-keys msg [:platform :add-all?]))
94 | (remove-disabled)
95 | (doto (reloading-reply msg))
96 | (reload/track-reload)
97 | (doto (result-reply msg))
98 | (doto (after-reply msg)))
99 |
100 | (catch Throwable e
101 | (reload-utils/error-reply {:error e} msg)
102 | tracker)
103 |
104 | (finally
105 | (reset! client-requested-clear? false)))))))
106 | (fn []
107 | (respond-to msg :status :done)))))
108 |
109 | (defn- clear-reply
110 | [{:keys [session id] :as msg}]
111 | (let [{:keys [exec]} (meta session)]
112 | (exec id
113 | (fn []
114 | ;; This used to be a `locking`-based call to our `clear` implementation.
115 | ;; Now it merely "enqueues" a clearing,
116 | ;; because that `locking` could cause unnecessary nREPL timeouts (https://github.com/clojure-emacs/cider/issues/3652 ).
117 | (reset! client-requested-clear? true))
118 | (fn []
119 | (respond-to msg :status :done)))))
120 |
121 | (defn handle-refresh [handler msg]
122 | (case (:op msg)
123 | "refresh" (refresh-reply (assoc msg :platform find/clj))
124 | "refresh-all" (refresh-reply (assoc msg :platform find/clj :add-all? true))
125 | "refresh-clear" (clear-reply msg)
126 | (handler msg)))
127 |
--------------------------------------------------------------------------------
/src/cider/nrepl/middleware/reload.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.reload
2 | "Reload changed namespaces.
3 | Alternative to cider.nrepl.middleware.refresh, using clj-reload instead
4 | of tools.namespace."
5 | (:require
6 | [cider.nrepl.middleware.util.reload :as reload-utils]
7 | [cider.nrepl.middleware.util :refer [respond-to]]
8 | [clj-reload.core :as reload]
9 | [clojure.main :refer [repl-caught]]
10 | [clojure.string :as str]
11 | [nrepl.middleware.interruptible-eval :refer [*msg*]]
12 | [nrepl.middleware.print :as print]
13 | [orchard.stacktrace :as stacktrace]))
14 |
15 | (defn- user-reload
16 | "Resolve clj-reload.core/ from the user project or return fallback."
17 | [sym fallback]
18 | (or (some-> (symbol "clj-reload.core" (str sym)) ;; Don't use mrandorsenized version
19 | resolve)
20 | fallback))
21 |
22 | (defn- init
23 | "Initialize clj-reload with dirs.
24 | Only used for test, but necessary because of mranderson."
25 | [dirs]
26 | (reload/init {:dirs dirs}))
27 |
28 | (defn operation
29 | [msg]
30 | (let [opts {:log-fn (fn [& args]
31 | (respond-to msg {:progress (str/join " " args)}))
32 | :throw false} ;; mimic the tools.namespace behavior so that we can use `reload-utils/after-reply` uniformly
33 | reload (user-reload 'reload reload/reload)
34 | unload (user-reload 'unload reload/unload)]
35 | (cond
36 | (:all msg) (reload (assoc opts :only :all))
37 | (:clear msg) (unload opts)
38 | :else (reload opts))))
39 |
40 | (defn- reload-reply
41 | [{:keys [session id] :as msg}]
42 | (let [{:keys [exec]} (meta session)]
43 | (exec id
44 | (fn []
45 | (try
46 | (reload-utils/before-reply msg)
47 | (let [{:keys [exception]} (operation msg)]
48 | (reload-utils/after-reply exception msg)
49 | (when exception
50 | (throw exception))
51 | (respond-to msg {:status :ok}))
52 | (catch Throwable error
53 | (respond-to msg {:status :error
54 | :error (stacktrace/analyze error)})
55 | (binding [*msg* msg
56 | *err* (print/replying-PrintWriter :err msg {})]
57 | (repl-caught error)))))
58 |
59 | (fn [] (respond-to msg {:status :done})))))
60 |
61 | (defn handle-reload [handler msg]
62 | (case (:op msg)
63 | "cider.clj-reload/reload" (reload-reply msg)
64 | "cider.clj-reload/reload-all" (reload-reply (assoc msg :all true))
65 | "cider.clj-reload/reload-clear" (reload-reply (assoc msg :clear true))
66 | (handler msg)))
67 |
--------------------------------------------------------------------------------
/src/cider/nrepl/middleware/resource.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.resource
2 | (:require
3 | [cider.nrepl.middleware.util :as util]
4 | [cider.nrepl.middleware.util.error-handling :refer [with-safe-transport]]
5 | [orchard.java.resource :as resource]))
6 |
7 | (defn handle-resource [handler msg]
8 | (with-safe-transport handler msg
9 | "resource" {:resource-path (resource/resource-path (:name msg))}
10 | "resources-list" {:resources-list (util/transform-value (resource/resource-maps))}))
11 |
--------------------------------------------------------------------------------
/src/cider/nrepl/middleware/slurp.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.slurp
2 | "Rich reading & handling for CIDER.
3 |
4 | Goes with middleware.content-types, providing the capability to
5 | convert URLs to values which can be handled nicely."
6 | {:authors ["Reid 'arrdem' McKenzie "]}
7 | (:require
8 | [cider.nrepl.middleware.util :refer [respond-to]]
9 | [clojure.edn :as edn]
10 | [clojure.java.io :as io]
11 | [clojure.string :as str])
12 | (:import
13 | (java.io ByteArrayOutputStream FileNotFoundException InputStream)
14 | (java.net MalformedURLException URI URL URLConnection)
15 | (java.nio.file Files Path Paths)
16 | (java.util Base64)))
17 |
18 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
19 |
20 | (def known-content-types
21 | (->> (io/resource "cider/nrepl/content-types.edn")
22 | (io/reader)
23 | (java.io.PushbackReader.)
24 | (edn/read)
25 | (mapcat (fn [[content-type exts]]
26 | (for [ext exts]
27 | [ext content-type])))
28 | (into {})))
29 |
30 | (defn- split-last
31 | [^String to-split ^String where]
32 | (let [idx (.lastIndexOf to-split where)]
33 | (if (not= idx -1)
34 | (.substring to-split (+ (count where) idx) (count to-split))
35 | to-split)))
36 |
37 | (def content-type-pattern
38 | #"(?[^;]+)(;(?.*?))?$")
39 |
40 | (defn normalize-content-type
41 | "nREPL's content-type headers are structured as a pair
42 | `[type {:as attrs}]`. This method normalizes RFC
43 | compliant content-types to this form."
44 | [^String content-type]
45 | (if-let [match (re-find content-type-pattern content-type)]
46 | (let [[_ type _ parameters] match]
47 | [type
48 | (into {}
49 | (when parameters
50 | (map #(str/split % #"=")
51 | (str/split parameters #";"))))])
52 | [content-type {}]))
53 |
54 | (defn get-file-content-type [^Path p]
55 | (or (get known-content-types (split-last (.toString p) "."))
56 | (Files/probeContentType p)
57 | "application/octet-stream"))
58 |
59 | (defn base64-bytes
60 | [^bytes buff]
61 | (.encodeToString (Base64/getEncoder) buff))
62 |
63 | (defn slurp-reply [location content-type ^bytes buff]
64 | (let [^String real-type (first content-type)
65 | binary? (= "application/octet-stream" real-type)
66 | text? (.contains real-type "text")]
67 | (cond
68 | binary?
69 | {:content-type content-type
70 | :body (str "#binary[location=" location ",size=" (count buff) "]")}
71 |
72 | text?
73 | {:content-type content-type
74 | :body (String. buff "utf-8")}
75 |
76 | :else
77 | {:content-type content-type
78 | :content-transfer-encoding "base64"
79 | :body (base64-bytes buff)})))
80 |
81 | (defn slurp-url-to-content+body
82 | "Attempts to parse and then to slurp a URL, producing a content-typed response."
83 | [url-str]
84 | (when-let [^URL url (try (.toURL (URI. url-str))
85 | (catch MalformedURLException _e nil))]
86 | (if (= (.getProtocol url) "file") ;; expected common case
87 | (let [^Path p (Paths/get (.toURI url))
88 | content-type (normalize-content-type (get-file-content-type p))
89 | buff (when-not (.isDirectory (io/as-file url)) (Files/readAllBytes p))]
90 | (slurp-reply p content-type buff))
91 |
92 | ;; It's not a file, so just try to open it on up
93 | (let [^URLConnection conn (.openConnection url)
94 | content-type (normalize-content-type
95 | (or (try
96 | (.getContentType conn)
97 | (catch FileNotFoundException _))
98 | "application/octet-stream"))
99 | ;; FIXME (arrdem 2018-04-03):
100 | ;; There's gotta be a better way here
101 | ^InputStream is (try
102 | (.getInputStream conn)
103 | (catch FileNotFoundException _
104 | (proxy [InputStream] []
105 | (read []
106 | -1))))
107 | os (ByteArrayOutputStream.)]
108 | (loop []
109 | (let [b (.read is)]
110 | (when (<= 0 b)
111 | (.write os b)
112 | (recur))))
113 | (slurp-reply url content-type (.toByteArray os))))))
114 |
115 | (defn handle-slurp
116 | "Message handler which just responds to slurp ops.
117 |
118 | If the slurp is malformed, or fails, lets the rest of the stack keep going."
119 | [handler msg]
120 | (let [{:keys [op url transport]} msg]
121 | (if (and (= "slurp" op) url)
122 | (do (respond-to msg (slurp-url-to-content+body url))
123 | (respond-to msg :status :done))
124 | (handler msg))))
125 |
--------------------------------------------------------------------------------
/src/cider/nrepl/middleware/spec.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.spec
2 | (:require
3 | [cider.nrepl.middleware.util.error-handling :refer [with-safe-transport]]
4 | [orchard.spec :as spec-utils]))
5 |
6 | ;; Replies
7 |
8 | (defn spec-list-reply [msg]
9 | {:spec-list (spec-utils/spec-list (:filter-regex msg))})
10 |
11 | (defn spec-form-reply [msg]
12 | {:spec-form (spec-utils/spec-form (:spec-name msg))})
13 |
14 | (defn spec-example-reply [msg]
15 | {:spec-example (spec-utils/spec-example (:spec-name msg))})
16 |
17 | (defn handle-spec [handler msg]
18 | (with-safe-transport handler msg
19 | "spec-list" spec-list-reply
20 | "spec-form" spec-form-reply
21 | "spec-example" spec-example-reply))
22 |
--------------------------------------------------------------------------------
/src/cider/nrepl/middleware/stacktrace.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.stacktrace
2 | "Cause and stacktrace analysis for exceptions"
3 | {:author "Jeff Valk"}
4 | (:require
5 | [cider.nrepl.middleware.inspect :as middleware.inspect]
6 | [cider.nrepl.middleware.util :refer [respond-to]]
7 | [cider.nrepl.middleware.util.nrepl :refer [notify-client]]
8 | [nrepl.middleware.print :as print]
9 | [nrepl.transport :as t]
10 | [orchard.stacktrace :as stacktrace]))
11 |
12 | (defn- send-analysis
13 | "Send the stacktrace analysis response to the client."
14 | [msg analysis]
15 | (doseq [cause analysis]
16 | (respond-to msg cause)))
17 |
18 | ;; Analyze the last stacktrace
19 |
20 | (defn- analyze-last-stacktrace
21 | "Analyze the last exception."
22 | [{:keys [session] :as msg}]
23 | (let [last-exception (@session #'*e)]
24 | ;; We need to remember the analyzed exception separately because we need to
25 | ;; provide a way to inspect it while *e can change.
26 | (alter-meta! session assoc ::analyzed-exception last-exception)
27 | (send-analysis msg (stacktrace/analyze last-exception))))
28 |
29 | (defn- handle-analyze-last-stacktrace-op
30 | "Handle the analyze last stacktrace op."
31 | [{:keys [session] :as msg}]
32 | (if (@session #'*e)
33 | (analyze-last-stacktrace msg)
34 | (respond-to msg :status :no-error))
35 | (respond-to msg :status :done))
36 |
37 | ;; Stacktrace
38 |
39 | (defn- handle-stacktrace-op
40 | "Handle the stacktrace op."
41 | [msg]
42 | (handle-analyze-last-stacktrace-op msg)
43 | (notify-client msg "The `stacktrace` op is deprecated, please use `analyze-last-stacktrace` instead." :warning))
44 |
45 | (defn- get-last-exception-cause [{:keys [session index] :as msg}]
46 | (when index
47 | (let [last-exception (::analyzed-exception (meta session))
48 | causes (when last-exception
49 | (->> (iterate #(.getCause ^Throwable %) last-exception)
50 | (take-while some?)))]
51 | (nth causes index nil))))
52 |
53 | (defn handle-inspect-last-exception-op [{:keys [session index transport] :as msg}]
54 | (let [inspect-ex-data? (= (:ex-data msg) "true")
55 | cause (get-last-exception-cause msg)
56 | object (if inspect-ex-data?
57 | (ex-data cause)
58 | cause)]
59 | (if object
60 | (t/send transport (middleware.inspect/inspect-reply* msg object))
61 | (respond-to msg :status :no-error))
62 | (respond-to msg :status :done)))
63 |
64 | (defn handle-stacktrace
65 | "Handle stacktrace ops."
66 | [_ {:keys [op] :as msg}]
67 | (case op
68 | "analyze-last-stacktrace" (handle-analyze-last-stacktrace-op msg)
69 | "inspect-last-exception" (handle-inspect-last-exception-op msg)
70 | "stacktrace" (handle-stacktrace-op msg)))
71 |
--------------------------------------------------------------------------------
/src/cider/nrepl/middleware/test/extensions.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.test.extensions
2 | "Extensions to `clojure.test` functionality.
3 |
4 | These are kept in a separate namespace because they are, by definition,
5 | opinionated."
6 | (:require
7 | [clojure.data :as data]
8 | [clojure.pprint :as pp]
9 | [clojure.test :as test :refer [assert-expr]]))
10 |
11 | (defn- diffable-objects?
12 | "Is a diff between `a` and `b` worth displaying to the user?
13 |
14 | Typically, two different scalar values don't have a useful diff.
15 |
16 | Also, collections of different types are not meaningfully diffable."
17 | [a b]
18 | (and (or (and (map? a)
19 | (map? b))
20 |
21 | (and (sequential? a)
22 | (sequential? b))
23 |
24 | (and (set? a)
25 | (set? b)))
26 | (not= a b)))
27 |
28 | (defn maybe-assoc-diffs
29 | "Computes and assocs data diffs when the at least expected/actual pair is deemed worth diffing."
30 | [m expected actual]
31 | (let [diffable? (volatile! false)
32 | diffs (mapv (fn [actual-value]
33 | (when (diffable-objects? expected actual-value)
34 | (vreset! diffable? true))
35 | [actual-value (data/diff expected actual-value)])
36 | actual)]
37 | (cond-> m
38 | ;; If at least one actual value is diffable, then show diffs for all actual values,
39 | ;; even if some of those diffs may not be so useful.
40 | ;; Else one the client side could easily get confused.
41 | @diffable?
42 | (assoc :diffs diffs))))
43 |
44 | ;; From pjstadig/humane-test-output
45 | ;; https://github.com/pjstadig/humane-test-output
46 | (defn =-body
47 | [msg expected more]
48 | (if (seq more)
49 | `(let [expected# ~expected
50 | more# (list ~@more)
51 | result# (apply = expected# more#)]
52 | (->> (if result#
53 | {:type :pass}
54 | (maybe-assoc-diffs {:type :fail}
55 | expected#
56 | (remove #(= expected# %) more#)))
57 | (merge {:message ~msg
58 | :expected expected#
59 | :actual
60 | (if (= 1 (count more#))
61 | ;; most times,` more` has a count of 1. For this case, we unwrap `more`,
62 | ;; which has been the traditional behavior of this feature:
63 | (first more#)
64 | ;; if `more` has 2+ arguments, our :actual will closely resemble clojure.test's own:
65 | (list ~''not (apply list ~(list 'quote '=) expected# more#)))})
66 | test/do-report)
67 | result#)
68 | `(throw (Exception. "= expects more than one argument"))))
69 |
70 | (defmethod assert-expr '= [msg [_ expected & more]]
71 | (=-body msg expected more))
72 |
73 | ;; In cases where an is form is part of a macro expansion assert-expr will get
74 | ;; called with the fully qualified name for = (clojure.core/=)
75 | ;; See: https://github.com/clojure-emacs/cider-nrepl/pull/478#pullrequestreview-90616379
76 | (defmethod assert-expr 'clojure.core/= [msg [_ expected & more]]
77 | (=-body msg expected more))
78 |
79 | (defn diffs-result
80 | "Convert diffs data to form appropriate for transport."
81 | [diffs]
82 | (let [pprint-str #(with-out-str (pp/pprint %))]
83 | (map (fn [[a [removed added]]]
84 | [(pprint-str a)
85 | [(pprint-str removed) (pprint-str added)]])
86 | diffs)))
87 |
--------------------------------------------------------------------------------
/src/cider/nrepl/middleware/trace.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.trace
2 | (:require
3 | [cider.nrepl.middleware.util.error-handling :refer [with-safe-transport]]
4 | [orchard.trace :as trace]))
5 |
6 | (defn toggle-trace-var
7 | [{:keys [ns sym]}]
8 | (if-let [v (ns-resolve (symbol ns) (symbol sym))]
9 | (if (trace/traceable? v)
10 | (if (trace/traced? v)
11 | (do (trace/untrace-var* v)
12 | {:var-name (str v) :var-status "untraced"})
13 | (do (trace/trace-var* v)
14 | {:var-name (str v) :var-status "traced"}))
15 | {:var-name (str v) :var-status "not-traceable"})
16 | {:status #{:toggle-trace-error :done} :var-status "not-found"}))
17 |
18 | (def traced-ns (atom #{}))
19 |
20 | (defn toggle-trace-ns
21 | [{:keys [ns]}]
22 | (if-let [ns (find-ns (symbol ns))]
23 | (if (contains? @@#'trace/traced-nses ns)
24 | (do (trace/untrace-ns* ns)
25 | {:ns-status "untraced"})
26 | (do (trace/trace-ns* ns)
27 | {:ns-status "traced"}))
28 | {:ns-status "not-found"}))
29 |
30 | (defn handle-trace [handler msg]
31 | (with-safe-transport handler msg
32 | "toggle-trace-var" [toggle-trace-var :toggle-trace-error]
33 | "toggle-trace-ns" [toggle-trace-ns :toggle-trace-error]))
34 |
--------------------------------------------------------------------------------
/src/cider/nrepl/middleware/undef.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.undef
2 | "Middleware for undefining symbols.
3 | Fully qualified symbols are interpreted as a var to be unmapped in its
4 | original namespace, whereas unqualified symbols are interpreted as both a var
5 | and ns alias to be unmapped from the current namespace."
6 | (:require
7 | [cider.nrepl.middleware.util.error-handling :refer [with-safe-transport]]
8 | [orchard.misc :as misc]))
9 |
10 | (defn undef
11 | "Undefines a symbol.
12 | When `sym` is unqualified, it is interpreted as both an alias and var to be
13 | unmapped from the namespace `ns`.
14 | When qualified (eg. `foo/bar`), it is interpreted as a var to be unmapped in
15 | the namespace `foo`, which may be an alias in `ns`."
16 | [{:keys [ns sym symbol]}]
17 | (let [ns (misc/as-sym ns)
18 | sym (or sym symbol) ;; for backwards compatibility
19 | [sym-ns sym-name] ((juxt (comp misc/as-sym namespace) misc/name-sym)
20 | (misc/as-sym sym))]
21 | (if sym-ns
22 | ;; fully qualified => var in other namespace
23 | (let [other-ns (get (ns-aliases ns) sym-ns sym-ns)]
24 | (ns-unmap other-ns sym-name))
25 | ;; unqualified => alias or var in current ns
26 | (do (ns-unalias ns sym-name)
27 | (ns-unmap ns sym-name)))
28 | sym))
29 |
30 | (defn undef-all
31 | "Undefines all symbol mappings and aliases in the namespace."
32 | [{:keys [ns]}]
33 | (let [ns (misc/as-sym ns)]
34 | ;; Do not remove the default java.lang imports, as they are not relinked on the next load
35 | ;; see https://github.com/clojure-emacs/cider/issues/3194
36 | (doseq [[sym ref] (ns-map ns)]
37 | (when-not (identical? ref (get clojure.lang.RT/DEFAULT_IMPORTS sym))
38 | (ns-unmap ns sym)))
39 | (doseq [[sym _] (ns-aliases ns)]
40 | (ns-unalias ns sym))
41 | ns))
42 |
43 | (defn undef-reply
44 | [msg]
45 | {:undef (undef msg)})
46 |
47 | (defn undef-all-reply
48 | [msg]
49 | {:undef-all (undef-all msg)})
50 |
51 | (defn handle-undef [handler msg]
52 | (with-safe-transport handler msg
53 | "undef" undef-reply
54 | "undef-all" undef-all-reply))
55 |
--------------------------------------------------------------------------------
/src/cider/nrepl/middleware/util.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.util
2 | "Utility functions that might be useful in middleware."
3 | (:require [nrepl.transport :as transport]
4 | [nrepl.misc :refer [response-for]]))
5 |
6 | (defmulti transform-value "Transform a value for output" type)
7 |
8 | (defmethod transform-value :default [v] (str v))
9 |
10 | (defmethod transform-value Number
11 | [v]
12 | ;; bencode supports only integers, so we convert
13 | ;; other numbers (e.g. floats) to string
14 | (if (integer? v)
15 | v
16 | (str v)))
17 |
18 | (defmethod transform-value nil [_v] nil)
19 |
20 | (defmethod transform-value java.io.File
21 | [v]
22 | (.getAbsolutePath ^java.io.File v))
23 |
24 | (defmethod transform-value clojure.lang.Sequential
25 | [v]
26 | (list* (map transform-value v)))
27 |
28 | (defmethod transform-value clojure.lang.Symbol
29 | [v]
30 | (let [[the-ns the-name] [(namespace v) (name v)]]
31 | (if the-ns
32 | (str the-ns "/" the-name)
33 | the-name)))
34 |
35 | (defmethod transform-value clojure.lang.Keyword
36 | [v]
37 | (transform-value (.sym ^clojure.lang.Keyword v)))
38 |
39 | (defmethod transform-value clojure.lang.Associative
40 | [m]
41 | (->> (for [[k v] m] ; bencode keys must be strings
42 | [(str (transform-value k)) (transform-value v)])
43 | (into {})))
44 |
45 | ;; handles vectors
46 | (prefer-method transform-value clojure.lang.Sequential clojure.lang.Associative)
47 |
48 | (defn respond-to
49 | "Send a response for `msg` with `response-data` using message's transport."
50 | [msg & response-data]
51 | (transport/send (:transport msg) (apply response-for msg response-data)))
52 |
--------------------------------------------------------------------------------
/src/cider/nrepl/middleware/util/cljs.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.util.cljs)
2 |
3 | ;; there's a plan to rename the main namespace of
4 | ;; piggieback to piggieback.core and the following code
5 | ;; simply paves the way for this
6 | (def cider-piggieback?
7 | (try (require 'cider.piggieback) true
8 | (catch Throwable _ false)))
9 |
10 | (def nrepl-piggieback?
11 | (try (require 'piggieback.core) true
12 | (catch Throwable _ false)))
13 |
14 | (defn try-piggieback
15 | "If piggieback is loaded, returns `#'cider.piggieback/wrap-cljs-repl`, or
16 | false otherwise."
17 | []
18 | (cond
19 | cider-piggieback? (resolve 'cider.piggieback/wrap-cljs-repl)
20 | nrepl-piggieback? (resolve 'piggieback.core/wrap-cljs-repl)
21 | :else false))
22 |
23 | (defn- maybe-piggieback
24 | [descriptor descriptor-key]
25 | (if-let [piggieback (try-piggieback)]
26 | (update-in descriptor [descriptor-key] #(set (conj % piggieback)))
27 | descriptor))
28 |
29 | (defn expects-piggieback
30 | "If piggieback is loaded, returns the descriptor with piggieback's
31 | `wrap-cljs-repl` handler assoc'd into its `:expects` set."
32 | [descriptor]
33 | (maybe-piggieback descriptor :expects))
34 |
35 | (defn requires-piggieback
36 | "If piggieback is loaded, returns the descriptor with piggieback's
37 | `wrap-cljs-repl` handler assoc'd into its `:requires` set."
38 | [descriptor]
39 | (maybe-piggieback descriptor :requires))
40 |
41 | (defn- cljs-env-path
42 | "Returns the path in the session map for the ClojureScript compiler
43 | environment used by piggieback."
44 | []
45 | [(if nrepl-piggieback?
46 | (resolve 'piggieback.core/*cljs-compiler-env*)
47 | (resolve 'cider.piggieback/*cljs-compiler-env*))])
48 |
49 | (defn- maybe-deref
50 | [x]
51 | (if (instance? clojure.lang.IDeref x) @x x))
52 |
53 | (defn grab-cljs-env*
54 | [msg]
55 | (let [path (cljs-env-path)]
56 | (some-> msg
57 | :session
58 | maybe-deref
59 | (get-in path))))
60 |
61 | (defn grab-cljs-env
62 | "If piggieback is active, returns the ClojureScript compiler environment for
63 | the running REPL."
64 | [msg]
65 | (maybe-deref (grab-cljs-env* msg)))
66 |
67 | (defn cljs-response-value
68 | "Returns the :value slot of an eval response from piggieback as a Clojure
69 | value."
70 | [response]
71 | (let [value (:value response)]
72 | (try
73 | (read-string value)
74 | (catch Exception _
75 | value))))
76 |
77 | (defn response-value
78 | "Returns the :value slot of an eval response as a Clojure value, reading the
79 | slot if necessary (piggieback 0.2.x)."
80 | [msg response]
81 | (if (grab-cljs-env msg)
82 | (cljs-response-value response)
83 | (:value response)))
84 |
85 | (defmacro with-cljs-env [msg & body]
86 | (try
87 | (require 'cljs.env)
88 | `(binding [cljs.env/*compiler* (grab-cljs-env* ~msg)]
89 | ~@body)
90 | (catch Exception _)))
91 |
92 | (defmacro with-cljs-ns [ns-sym & body]
93 | (try
94 | (require 'cljs.analyzer)
95 | `(binding [cljs.analyzer/*cljs-ns* ~ns-sym]
96 | ~@body)
97 | (catch Exception _)))
98 |
--------------------------------------------------------------------------------
/src/cider/nrepl/middleware/util/coerce.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.util.coerce
2 | "Coercion utilities for coercing bencoded maps.")
3 |
4 | (defn- update-some
5 | [m k & args]
6 | (if (get m k)
7 | (apply update m k args)
8 | m))
9 |
10 | (defn ns-query
11 | "Poke and prod at a bencoded ns-query until it is in the form that orchard
12 | expects."
13 | [ns-query]
14 | (-> ns-query
15 | (update-some :exactly
16 | #(seq
17 | (map (fn [ns-string]
18 | (if-let [ns (find-ns (symbol ns-string))]
19 | ns
20 | (throw (ex-info "Namespace not found"
21 | {::id :namespace-not-found
22 | :namespace-string ns-string}))))
23 | %)))
24 | (update :project? some?)
25 | (update :load-project-ns? (fn [x]
26 | (cond
27 | (= x []) false
28 | :else (some? x))))
29 | (update :has-tests? some?)
30 | (update-some :include-regexps #(map re-pattern %))
31 | (update-some :exclude-regexps #(map re-pattern %))))
32 |
33 | (defn var-query
34 | [var-query]
35 | (-> var-query
36 | (update :ns-query ns-query)
37 | (update-some :exactly #(seq (keep (comp find-var symbol) %)))
38 | (update :test? some?)
39 | (update :private? some?)
40 | (update-some :include-meta-key #(map keyword %))
41 | (update-some :exclude-meta-key #(map keyword %))
42 | (update-some :search re-pattern)
43 | (update-some :search-property keyword)
44 | (dissoc :manipulate-vars)))
45 |
--------------------------------------------------------------------------------
/src/cider/nrepl/middleware/util/meta.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.util.meta
2 | "Utility functions for extracting and manipulating metadata."
3 | (:require
4 | [orchard.misc :as misc]))
5 |
6 | (def relevant-meta-keys
7 | "Metadata keys that are useful to us.
8 | This is used so that we don't crowd the ns cache with useless or
9 | redudant information, such as :name and :ns."
10 | [:indent :deprecated :macro :arglists :test :doc :fn
11 | :cider/instrumented :style/indent :orchard.trace/traced
12 | :orchard.profile/profiled])
13 |
14 | (defn relevant-meta
15 | "Filter the entries in map m by `relevant-meta-keys` and non-nil values."
16 | [m]
17 | (into {}
18 | (keep #(when-let [v (get m %)] [% (pr-str v)]))
19 | relevant-meta-keys))
20 |
--------------------------------------------------------------------------------
/src/cider/nrepl/middleware/util/nrepl.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.util.nrepl
2 | "Common utilities for interaction with the client."
3 | (:require
4 | [nrepl.middleware.interruptible-eval :refer [*msg*]]
5 | [nrepl.misc :refer [response-for]]
6 | [nrepl.transport :as transport]))
7 |
8 | (defn notify-client
9 | "Send user level notification to client as a response to request `msg`.
10 | If transport is not provided use (:transport msg). If msg is not provided, use
11 | current *msg* from interruptible-eval middleware. Type is a keyword or string
12 | indicating type of the message (e.g. :message, :warning, :error etc). Type
13 | defaults to :message. See `nrepl-notify` on the Emacs side."
14 | ([notification] (notify-client *msg* notification))
15 | ([msg notification] (notify-client (:transport msg) msg notification nil))
16 | ([msg notification type] (notify-client (:transport msg) msg notification type))
17 | ([tr msg notification type]
18 | (transport/send tr (apply response-for msg
19 | :status :notification
20 | :msg notification
21 | (when type [:type type])))))
22 |
--------------------------------------------------------------------------------
/src/cider/nrepl/middleware/util/reload.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.util.reload
2 | "Common parts for the code-reloading middleware namespaces."
3 | {:added "0.47.0"}
4 | (:require
5 | [cider.nrepl.middleware.util :refer [respond-to]]
6 | [clojure.main :refer [repl-caught]]
7 | [nrepl.middleware.interruptible-eval :refer [*msg*]]
8 | [nrepl.middleware.print :as print]
9 | [orchard.misc :as misc]
10 | [orchard.stacktrace :as stacktrace]))
11 |
12 | (defn error-reply [{:keys [error error-ns]} msg]
13 | (respond-to msg (cond-> {:status :error}
14 | error (assoc :error (stacktrace/analyze error))
15 | error-ns (assoc :error-ns error-ns)))
16 |
17 | (binding [*msg* msg
18 | *err* (print/replying-PrintWriter :err msg {})]
19 | (repl-caught error)))
20 |
21 | (defn- zero-arity-callable? [func]
22 | (and (fn? (if (var? func) @func func))
23 | (->> (:arglists (meta func))
24 | (some #(or (= [] %) (= '& (first %)))))))
25 |
26 | (defn- resolve-and-invoke
27 | "Takes a string and tries to coerce a function from it. If that
28 | function is a function of possible zero arity (ie, truly a thunk or
29 | has optional parameters and can be called with zero args, it is
30 | called. Returns whether the function was resolved."
31 | [sym {:keys [_session] :as msg}]
32 | (let [the-var (some-> sym misc/as-sym resolve)]
33 |
34 | (when (and (var? the-var)
35 | (not (zero-arity-callable? the-var)))
36 | (throw (IllegalArgumentException.
37 | (format "%s is not a function of no arguments" sym))))
38 |
39 | (binding [*msg* msg
40 | *out* (print/replying-PrintWriter :out msg {})
41 | *err* (print/replying-PrintWriter :err msg {})]
42 | (when (var? the-var)
43 | (@the-var))
44 | (var? the-var))))
45 |
46 | (defn before-reply [{:keys [before] :as msg}]
47 | (when before
48 | (respond-to msg {:status :invoking-before
49 | :before before})
50 |
51 | (let [resolved? (resolve-and-invoke before msg)]
52 | (respond-to msg {:status (if resolved?
53 | :invoked-before
54 | :invoked-not-resolved)
55 | :before before}))))
56 |
57 | (defn after-reply [error
58 | {:keys [after] :as msg}]
59 | (when (and (not error) after)
60 | (try
61 | (respond-to msg {:status :invoking-after
62 | :after after})
63 |
64 | (let [resolved? (resolve-and-invoke after msg)]
65 | (respond-to msg {:status (if resolved?
66 | :invoked-after
67 | :invoked-not-resolved)
68 | :after after}))
69 |
70 | (catch Exception e
71 | (error-reply {:error e} msg)))))
72 |
--------------------------------------------------------------------------------
/src/cider/nrepl/middleware/version.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.version
2 | "Return version info of the CIDER-nREPL middleware itself."
3 | (:require
4 | [cider.nrepl.version :as version]
5 | [cider.nrepl.middleware.util :refer [respond-to]]))
6 |
7 | (defn handle-version [handler msg]
8 | (if (= (:op msg) "cider-version")
9 | (respond-to msg {:status :done, :cider-version version/version})
10 | (handler msg)))
11 |
--------------------------------------------------------------------------------
/src/cider/nrepl/middleware/xref.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.xref
2 | "Find function dependencies and function references."
3 | {:author "Bozhidar Batsov"
4 | :added "0.22"}
5 | (:require
6 | [cider.nrepl.middleware.util.error-handling :refer [with-safe-transport]]
7 | [clojure.java.io :as io]
8 | [orchard.meta :as meta]
9 | [orchard.misc :as misc]
10 | [orchard.xref :as xref]))
11 |
12 | (defn- filename-as-url [filename]
13 | (if-let [resource (io/resource filename)]
14 | (str resource) ;; adds "file:" / "jar:file:" in front of the filename, besides from an absolute path
15 | filename))
16 |
17 | (defn- xref-data [v]
18 | (let [var-meta (meta/var-meta v)]
19 | {:name (meta/var-name v)
20 | :doc (meta/var-doc 1 v)
21 | :file (:file var-meta)
22 | :file-url (or (:file-url var-meta)
23 | (some-> var-meta :file filename-as-url))
24 | :line (:line var-meta)
25 | :column (:column var-meta)}))
26 |
27 | (defn file-line-column [{:keys [file-url file line column]}]
28 | [(or file-url file) (or line 0) (or column 0)])
29 |
30 | (defn fn-refs-reply [{:keys [ns sym]}]
31 | (let [var (ns-resolve (misc/as-sym ns) (misc/as-sym sym))]
32 | {:fn-refs (->> var
33 | xref/fn-refs
34 | (map xref-data)
35 | (sort-by file-line-column))}))
36 |
37 | (defn fn-deps-reply [{:keys [ns sym]}]
38 | (let [var (ns-resolve (misc/as-sym ns) (misc/as-sym sym))]
39 | {:fn-deps (->> var
40 | xref/fn-deps
41 | (map xref-data)
42 | (sort-by file-line-column))}))
43 |
44 | (defn handle-xref [handler msg]
45 | (with-safe-transport handler msg
46 | "fn-refs" fn-refs-reply
47 | "fn-deps" fn-deps-reply))
48 |
--------------------------------------------------------------------------------
/src/cider/nrepl/pprint.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.pprint
2 | "Pretty-print related utilities.
3 | All functions here are simple wrappers compatible with the expectations of
4 | nrepl.middleware.print/wrap-print."
5 | {:added "0.20"}
6 | (:refer-clojure :exclude [pr])
7 | (:require
8 | [clojure.pprint]
9 | [orchard.pp :as pp]
10 | [orchard.misc :as misc]))
11 |
12 | (def ^:private pr-options
13 | [:print-dup
14 | :print-readably
15 | :print-length
16 | :print-level
17 | :print-meta
18 | :print-namespace-maps])
19 |
20 | (defn- option->var
21 | [option]
22 | (resolve (symbol "clojure.core" (str "*" (name option) "*"))))
23 |
24 | (defn- try-resolve [var-symbol pprinter-name]
25 | (or (misc/require-and-resolve var-symbol)
26 | (binding [*out* *err*]
27 | (println (format "Could not load %s namespace. To use %s pretty-printing with CIDER, add it to dependencies explicitly."
28 | (namespace var-symbol) pprinter-name))
29 | nil)))
30 |
31 | (defn- pr-bindings
32 | [options]
33 | (->> (select-keys options pr-options)
34 | (into {} (keep (fn [[option value]]
35 | (when-let [var (option->var option)]
36 | [var value]))))))
37 |
38 | (defn pr
39 | "Equivalent to `clojure.core/pr`. Any options corresponding to dynamic
40 | printing configuration vars in `clojure.core` will, if provided, be bound
41 | accordingly (e.g. `clojure.core/*print-length*` will be used if
42 | `:print-length` is provided)."
43 | ([value writer]
44 | (pr value writer nil))
45 | ([value writer options]
46 | (with-bindings (pr-bindings options)
47 | (if *print-dup*
48 | (print-dup value writer)
49 | (print-method value writer)))))
50 |
51 | (defn pprint
52 | "A simple wrapper around `clojure.pprint/write`."
53 | ([value writer]
54 | (pprint value writer {}))
55 | ([value writer options]
56 | (apply clojure.pprint/write value (mapcat identity (assoc options :stream writer)))))
57 |
58 | (defn orchard-pprint
59 | ([value writer]
60 | (pp/pprint writer value {}))
61 | ([value writer options]
62 | (pp/pprint writer value options)))
63 |
64 | (def ^:private fipp-printer
65 | (delay (try-resolve 'fipp.edn/pprint "Fipp")))
66 |
67 | (defn fipp-pprint
68 | ([value writer]
69 | (fipp-pprint value writer {}))
70 | ([value writer options]
71 | (if-some [fipp @fipp-printer]
72 | (binding [*out* writer]
73 | (fipp value options))
74 | ;; Default to orchard.pp/pprint if Fipp could not be loaded.
75 | (pp/pprint writer value options))))
76 |
77 | (def ^:private puget-printer
78 | (delay (try-resolve 'puget.printer/pprint "Puget")))
79 |
80 | (defn puget-pprint
81 | ([value writer]
82 | (puget-pprint value writer {}))
83 | ([value writer options]
84 | (if-some [puget @puget-printer]
85 | (binding [*out* writer]
86 | (puget value options))
87 | ;; Default to orchard.pp/pprint if Puget could not be loaded.
88 | (pp/pprint writer value options))))
89 |
90 | (def ^:private zprint-printer
91 | (delay (try-resolve 'zprint.core/zprint "zprint")))
92 |
93 | (defn zprint-pprint
94 | ([value writer]
95 | (zprint-pprint value writer {}))
96 | ([value writer options]
97 | (if-some [zprint @zprint-printer]
98 | (binding [*out* writer]
99 | (zprint value options))
100 | ;; Default to orchard.pp/pprint if Puget could not be loaded.
101 | (pp/pprint writer value options))))
102 |
--------------------------------------------------------------------------------
/src/cider/nrepl/print_method.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.print-method
2 | "Extending `print-method` defined in clojure.core, to provide
3 | prettier versions of some objects. This applies to anything that
4 | calls `print-method`, which includes return values, `pr`, `print`
5 | and the likes."
6 | (:require
7 | [orchard.print :as print])
8 | (:import
9 | (clojure.lang AFunction Atom IDeref MultiFn Namespace)))
10 |
11 | (def ^:dynamic *pretty-objects*
12 | "If true, cider prettifies some object descriptions.
13 | For instance, instead of printing functions as
14 | #object[clojure.core$_PLUS_ 0x4e648e99 \"clojure.core$_PLUS_@4e648e99\"]
15 | they are printed as
16 | #function[clojure.core/+]
17 |
18 | To disable this feature, do
19 | (alter-var-root #'cider.nrepl.print-method/*pretty-objects* not)"
20 | true)
21 |
22 | (defn- replace-with-orchard-print
23 | "Replace `clojure.core/print-method` for the given class with
24 | `orchard.print/print` when `*pretty-objects*` is true, otherwise call the
25 | default Clojure implementation."
26 | [klass]
27 | (defmethod print-method klass [x writer]
28 | (if *pretty-objects*
29 | (print/print x writer)
30 | (#'clojure.core/print-object writer))))
31 |
32 | ;; NB: we don't replace all clojure.core/print-method implementations with
33 | ;; orchard.print/print because they arguably have different purpose. Orchard
34 | ;; printer is more human-oriented whereas print-method is a bit more
35 | ;; machine-oriented. So, we only replace it for those types where the visual
36 | ;; improvement is substantial yet we are confident it won't break something.
37 |
38 | ;;; Atoms: #atom[{:foo :bar}]
39 | (replace-with-orchard-print Atom)
40 |
41 | ;;; Function objects: #function[clojure.core/str]
42 | (replace-with-orchard-print AFunction)
43 |
44 | ;;; Multimethods: #multifn[print-method 0x3f0cd5b4]
45 | (replace-with-orchard-print MultiFn)
46 |
47 | ;;; Namespaces: #namespace[clojure.core]
48 | (replace-with-orchard-print Namespace)
49 |
50 | ;;; Various derefables
51 | ;; #agent[1], #agent[ #error[...]]
52 | ;; #delay[], #delay[1], #delay[ #error[...]]
53 | ;; #future[], #future[1], #future[ #error[...]]
54 | ;; #promise[], #promise[1]
55 | (replace-with-orchard-print IDeref)
56 |
--------------------------------------------------------------------------------
/src/cider/nrepl/version.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.version
2 | (:require
3 | [clojure.java.io :as io]))
4 |
5 | (def version-string
6 | "The current version for cider-nrepl as a string."
7 | (-> (io/resource "cider/nrepl/version.edn")
8 | slurp
9 | read-string))
10 |
11 | (assert (seq version-string))
12 |
13 | (assert (string? version-string))
14 |
15 | (def version
16 | "Current version of CIDER nREPL as a map.
17 | Map of :major, :minor, :incremental, :qualifier,
18 | and :version-string."
19 | (assoc (->> version-string
20 | (re-find #"(\d+)\.(\d+)\.(\d+)-?(.*)")
21 | rest
22 | (map #(try (Integer/parseInt %) (catch Exception _e nil)))
23 | (zipmap [:major :minor :incremental :qualifier]))
24 | :version-string version-string))
25 |
--------------------------------------------------------------------------------
/src/cider_nrepl/plugin.clj:
--------------------------------------------------------------------------------
1 | (ns cider-nrepl.plugin
2 | "Provides a simple way to setup the CIDER nREPL middleware in
3 | Leiningen projects."
4 | (:require
5 | [cider.nrepl.middleware :as mw]
6 | [cider.nrepl.version :refer [version-string]]
7 | [leiningen.core.main :as lein]))
8 |
9 | (def minimum-versions {:lein "2.8.3"
10 | :clojure "1.10.0"})
11 |
12 | (defn valid-version? [kind version] (lein/version-satisfies? version (minimum-versions kind)))
13 | (def valid-lein-version? (partial valid-version? :lein))
14 | (def valid-clojure-version? (partial valid-version? :clojure))
15 |
16 | ;; Exists for the sole purpose of modifying the current project's metadata.
17 | ;; See https://github.com/technomancy/leiningen/blob/master/doc/PLUGINS.md#project-middleware
18 | (defn middleware
19 | [{:keys [dependencies exclusions] :as project}]
20 | (let [lein-version-ok? (valid-lein-version? (lein/leiningen-version))
21 | clojure-excluded? (some #(= % 'org.clojure/clojure) exclusions)
22 | clojure-versions (when-not clojure-excluded?
23 | (->> dependencies
24 | (keep (fn [[id version & _]]
25 | (when (and (= id 'org.clojure/clojure)
26 | ;; We do an additional check here to ensure
27 | ;; a version is present. Some lein extensions
28 | ;; such as lein modules or managed dependencies
29 | ;; do not require versions in the dependency list
30 | (string? version))
31 | version)))))
32 | clojure-version-ok? (cond clojure-excluded?
33 | ;; In this case the onus is on the user. A warning will be emitted
34 | ;; later, but we assume that the user will provide an appropriate
35 | ;; implementation.
36 | true
37 |
38 | (empty? clojure-versions)
39 | ;; Lein 2.8.3+ uses Clojure 1.8 by default, which would be OK.
40 | lein-version-ok?
41 |
42 | :else
43 | ;; There is a Clojure version depended on, it must check out.
44 | (some valid-clojure-version? clojure-versions))]
45 |
46 | (when-not lein-version-ok?
47 | (lein/warn "Warning: cider-nrepl requires Leiningen 2.8.3 or greater."))
48 | (when-not clojure-version-ok?
49 | (lein/warn "Warning: cider-nrepl requires Clojure 1.10.0 or greater."))
50 | (when clojure-excluded?
51 | (lein/warn "Warning: Clojure is excluded, assuming an appropriate fork (Clojure 1.10.0 or later) is provided."))
52 | (when-not (and lein-version-ok? clojure-version-ok?)
53 | (lein/warn "Warning: cider-nrepl will not be included in your project."))
54 |
55 | (cond-> project
56 | (and clojure-version-ok? lein-version-ok?)
57 | (-> (update-in [:dependencies]
58 | (fnil into [])
59 | [['cider/cider-nrepl version-string]])
60 | (update-in [:repl-options :nrepl-middleware]
61 | (fnil into [])
62 | mw/cider-middleware)))))
63 |
--------------------------------------------------------------------------------
/src/data_readers.clj:
--------------------------------------------------------------------------------
1 | {dbg cider.nrepl.middleware.debug/debug-reader
2 | break cider.nrepl.middleware.debug/breakpoint-reader
3 | break! cider.nrepl.middleware.debug/break-on-exception-reader
4 | dbg! cider.nrepl.middleware.debug/debug-on-exception-reader
5 | light cider.nrepl.middleware.enlighten/light-reader}
6 |
--------------------------------------------------------------------------------
/test/clj/cider/nrepl/middleware/apropos_test.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.apropos-test
2 | (:require
3 | [cider.nrepl.middleware.apropos :refer [apropos] :as apropos]
4 | [cider.nrepl.test-session :as session]
5 | [clojure.string :as str]
6 | [clojure.test :refer :all]))
7 |
8 | (def ^:private ^{:doc "Can't. See. Me"} my-private-var [:a :b :c])
9 |
10 | (use-fixtures :each session/session-fixture)
11 |
12 | (deftest msg->var-query-map-test
13 | (testing "Constructs the ns-query map correctly"
14 | (let [msg {:exclude-regexps ["^cider.nrepl" "^refactor-nrepl" "^nrepl"]
15 | :query "spelling"}
16 | query-map (#'apropos/msg->var-query-map msg)]
17 | (is (contains? (:var-query query-map) :ns-query))
18 | (is (= 3 (count (-> query-map :var-query :ns-query :exclude-regexps))))))
19 |
20 | (testing "No :search key in the query-map if no :query in message"
21 | (let [msg {:exclude-regexps ["^cider.nrepl" "^refactor-nrepl" "^nrepl"]}
22 | query-map (#'apropos/msg->var-query-map msg)]
23 | (is ((complement contains?) (:var-query query-map) :search)))))
24 |
25 | (deftest integration-test
26 | (testing "Apropos op, typical case"
27 | (let [response (session/message {:op "apropos" :query "handle-apropos"})
28 | match (get-in response [:apropos-matches 0])]
29 | (is (= (:status response) #{"done"}))
30 | (is (= (:type match) "function"))
31 | (is (= (:name match) "cider.nrepl.middleware.apropos/handle-apropos"))))
32 |
33 | (testing "Exclude namespaces typical case"
34 | (let [response (session/message {:op "apropos" :query "handle-apropos"
35 | :exclude-regexps ["cider.nrepl.middleware.apropos"]})
36 | match (get-in response [:apropos-matches 0])]
37 | (is (empty? match))
38 | (is (= (:status response) #{"done"}))))
39 |
40 | (testing "Apropos op, but specialized cases (invoked with prefix argument)"
41 | (testing "Fails to get a private var because private? unset"
42 | (let [response (session/message {:op "apropos" :query "my-private-var"})
43 | match (get-in response [:apropos-matches 0])]
44 | (is (= (:status response) #{"done"}))
45 | (is (empty? match))))
46 |
47 | (testing "Gets a private var using a case insensitive query"
48 | (let [response (session/message {:op "apropos" :query "My-Private-Var" :privates? "t"})
49 | match (get-in response [:apropos-matches 0])]
50 | (is (= (:status response) #{"done"}))
51 | (is (= (:type match) "variable"))
52 | (is (= (:name match) "cider.nrepl.middleware.apropos-test/my-private-var"))
53 | (is (= (:doc match) "Can't."))))
54 |
55 | (testing "Fails to get a private var due to case-mismatch in a case sensitive query"
56 | (let [response (session/message {:op "apropos"
57 | :query "My-Private-Var"
58 | :privates? "t"
59 | :case-sensitive? "t"})
60 | match (get-in response [:apropos-matches 0])]
61 | (is (= (:status response) #{"done"}))
62 | (is (empty? match))))
63 |
64 | (testing "Finds a public macro via a case-insensitive search through the docs"
65 | (let [doc-query "threads the expr through the forms"
66 | response (session/message {:op "apropos" :query doc-query :docs? "t"})
67 | match (get-in response [:apropos-matches 0])]
68 | (is (= (:status response) #{"done"}))
69 | (is (= (:type match) "macro"))
70 | (is (= (:name match) "clojure.core/->"))
71 | (is (-> match ^String (:doc) (.startsWith (str/capitalize doc-query))))))))
72 |
73 | (deftest error-handling-test
74 | (testing "Handles a fake error done via mocked function"
75 | (with-redefs [apropos
76 | (fn [args] (throw (Exception. "boom")))]
77 | (let [response (session/message {:op "apropos" :query "doesn't matter"})]
78 | (is (= (:status response) #{"apropos-error" "done"}))
79 | (is (= (:ex response) "class java.lang.Exception"))
80 | (is (-> response ^String (:err) (.startsWith "java.lang.Exception: boom")))
81 | (is (:pp-stacktrace response)))))
82 |
83 | (testing "Handles a real error caused by an improper regular expression"
84 | (let [response (session/message {:op "apropos" :query "*illegal"})]
85 | (is (= (:status response) #{"apropos-error" "done"}))
86 | (is (-> response ^String (:err) (.startsWith "java.util.regex.PatternSyntaxException: Dangling")))
87 | (is (:pp-stacktrace response)))))
88 |
--------------------------------------------------------------------------------
/test/clj/cider/nrepl/middleware/classpath_test.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.classpath-test
2 | (:require
3 | [cider.nrepl.test-session :as session]
4 | [cider.nrepl.middleware.classpath :refer :all]
5 | [clojure.test :refer :all]))
6 |
7 | (use-fixtures :each session/session-fixture)
8 |
9 | (deftest integration-test
10 | (let [response (session/message {:op "classpath"})
11 | classpaths (:classpath response)]
12 | (is (= (:status response) #{"done"}))
13 | (is (> (count classpaths) 1))
14 | (is (every? string? classpaths))
15 | (is (some #(re-find #".*clojure-.*jar" %) classpaths))))
16 |
17 | (deftest error-handling-test
18 | (with-redefs [classpath-reply (fn [_] (throw (Exception. "cp error")))]
19 | (let [response (session/message {:op "classpath"})]
20 | (is (= (:status response) #{"done" "classpath-error"}))
21 | (is (-> response ^String (:err) (.startsWith "java.lang.Exception: cp error")))
22 | (is (= (:ex response) "class java.lang.Exception"))
23 | (is (:pp-stacktrace response)))))
24 |
25 | (deftest file-url?-test
26 | (is (file-url? (.toURL (.toURI (java.io.File. "")))))
27 | (is (not (file-url? (.toURL (java.net.URI. "jar:file:/tmp/test.jar!/BOOT-INF/classes"))))))
28 |
--------------------------------------------------------------------------------
/test/clj/cider/nrepl/middleware/clojuredocs_test.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.clojuredocs-test
2 | (:require
3 | [cider.nrepl.test-session :as session]
4 | [clojure.java.io :as io]
5 | [clojure.string :as str]
6 | [clojure.test :refer :all])
7 | (:import
8 | java.io.File))
9 |
10 | (def ^:private test-url
11 | (delay
12 | (let [tmp (File/createTempFile "export" ".edn")
13 | ;; Take export.edn for testing from Orchard dependency.
14 | orchard-clojuredocs-export (io/resource "clojuredocs/export.edn")]
15 | (assert orchard-clojuredocs-export)
16 | (io/copy (io/reader orchard-clojuredocs-export) tmp)
17 | (str tmp))))
18 |
19 | (use-fixtures :each session/session-fixture)
20 |
21 | (deftest clojuredocs-refresh-cache-integration-test
22 | (testing "Invalid URL"
23 | (let [response (session/message {:op "clojuredocs-refresh-cache"
24 | :export-edn-url "/non-existing.edn"})]
25 | (is (contains? (:status response) "clojuredocs-refresh-cache-error"))
26 | (is (not (str/blank? (:err response))))))
27 |
28 | (testing "Valid URL"
29 | (let [response (session/message {:op "clojuredocs-refresh-cache"
30 | :export-edn-url @test-url})]
31 | (is (contains? (:status response) "ok")))))
32 |
33 | (deftest clojuredocs-lookup-integration-test
34 | (testing "Searching for non-existing documentation"
35 | (let [response (session/message {:op "clojuredocs-lookup"
36 | :ns "non-existing"
37 | :sym "non-existing"})]
38 | (is (contains? (:status response) "no-doc"))))
39 |
40 | (testing "Searching for existing documentation"
41 | (let [response (session/message {:op "clojuredocs-lookup"
42 | :ns "clojure.core"
43 | :sym "first"})
44 | doc (get response :clojuredocs {})]
45 | (is (contains? (:status response) "done"))
46 | (is (= "clojure.core" (:ns doc)))
47 | (is (= "first" (:name doc)))
48 | (is (every? #(contains? doc %) [:examples :see-alsos]))))
49 |
50 | (testing "Resolves syms in the supplied ns"
51 | (let [response (session/message {:op "clojuredocs-lookup"
52 | :ns "cider.nrepl.middleware.clojuredocs-test"
53 | :sym "map"})
54 | doc (get response :clojuredocs {})]
55 | (is (contains? (:status response) "done"))
56 | (is (= "clojure.core" (:ns doc)))
57 | (is (= "map" (:name doc)))
58 | (is (every? #(contains? doc %) [:examples :see-alsos])))))
59 |
--------------------------------------------------------------------------------
/test/clj/cider/nrepl/middleware/complete_test.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.complete-test
2 | (:require
3 | [cider.nrepl.middleware.complete :as c]
4 | [cider.nrepl.test-session :as session]
5 | [cider.test-helpers :refer :all]
6 | [clojure.test :refer :all]
7 | [matcher-combinators.matchers :as matchers]))
8 |
9 | (use-fixtures :each session/session-fixture)
10 |
11 | (deftest complete
12 | (testing "blank"
13 | (is+ {:status #{"done"}
14 | :completions (matchers/seq-of map?)}
15 | (session/message {:op "complete"
16 | :ns "user"
17 | :prefix ""})))
18 |
19 | (testing "basic usage"
20 | (is+ {:status #{"done"}
21 | :completions [{:candidate "filter" :ns "clojure.core" :priority 31}
22 | {:candidate "filterv" :ns "clojure.core" :priority 31}]}
23 | (session/message {:op "complete"
24 | :ns "user"
25 | :prefix "filt"})))
26 |
27 | (testing "function arglists"
28 | (is+ {:arglists '("[x y]") :ns "clojure.core", :candidate "unchecked-add", :type "function"}
29 | (-> (session/message {:op "complete"
30 | :ns "user"
31 | :prefix "unchecked-a"
32 | :extra-metadata ["arglists"]})
33 | :completions first)))
34 |
35 | (testing "function metadata"
36 | (is+ {:arglists ["[map key val]" "[map key val & kvs]"]
37 | :doc string?}
38 | (first (:completions (session/message {:op "complete"
39 | :ns "user"
40 | :prefix "assoc"
41 | :extra-metadata ["arglists" "doc"]})))))
42 |
43 | (testing "default sorting"
44 | (is+ (matchers/prefix [{:candidate "map"}
45 | {:candidate "map?"}
46 | {:candidate "mapv"}
47 | {:candidate "mapcat"}])
48 | (:completions (session/message {:op "complete"
49 | :ns "user"
50 | :prefix "map"}))))
51 |
52 | (testing "by-name sorting"
53 | (is+ (matchers/prefix [{:candidate "map"}
54 | {:candidate "map-entry?"}
55 | {:candidate "map-indexed"}
56 | {:candidate "map?"}])
57 | (:completions (session/message {:op "complete"
58 | :ns "user"
59 | :prefix "map"
60 | :sort-order "by-name"}))))
61 |
62 | (testing "macro metadata"
63 | (is+ {:arglists ["[name & opts+sigs]"]
64 | :doc string?}
65 | (first (:completions (session/message {:op "complete"
66 | :ns "user"
67 | :prefix "defprot"
68 | :extra-metadata ["arglists" "doc"]})))))
69 |
70 | (testing "Clojure 1.12 qualified methods"
71 | (when (or (> (:major *clojure-version*) 1)
72 | (>= (:minor *clojure-version*) 12))
73 | (is+ {:candidate "Thread/.interrupt", :type "method"}
74 | (first (:completions (session/message {:op "complete"
75 | :ns "user"
76 | :prefix "Thread/.int"})))))))
77 |
78 | (deftest complete-doc-test
79 | (testing "blank"
80 | (is+ {:status #{"done"}
81 | :completion-doc empty?}
82 | (session/message {:op "complete-doc" :sym ""})))
83 |
84 | (testing "basic usage"
85 | (is+ {:status #{"done"}
86 | :completion-doc #"^clojure.core/true\?"}
87 | (session/message {:op "complete-doc" :sym "true?"}))))
88 |
89 | (deftest complete-flush-caches-test
90 | (testing "basic usage"
91 | (let [response (session/message {:op "complete-flush-caches"})]
92 | (is (= (:status response) #{"done"})))))
93 |
94 | (deftest error-handling-test
95 | (testing "complete op error handling"
96 | (with-redefs [c/complete (fn [& _] (throw (Exception. "complete-exc")))]
97 | (is+ {:ex "class java.lang.Exception"
98 | :status #{"complete-error" "done"}
99 | :err #"^java.lang.Exception: complete-exc"
100 | :pp-stacktrace some?}
101 | (session/message {:op "complete" :ns "doesn't matter" :prefix "fake"}))))
102 |
103 | (testing "complete-doc op error handling"
104 | (with-redefs [c/completion-doc (fn [& _] (throw (Exception. "complete-doc-exc")))]
105 | (is+ {:ex "class java.lang.Exception"
106 | :status #{"complete-doc-error" "done"}
107 | :err #"^java.lang.Exception: complete-doc-exc"
108 | :pp-stacktrace some?}
109 | (session/message {:op "complete-doc" :sym "doesn't matter"})))))
110 |
--------------------------------------------------------------------------------
/test/clj/cider/nrepl/middleware/content_type_test.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.content-type-test
2 | (:require
3 | [cider.nrepl.middleware.content-type :as content-type]
4 | [cider.nrepl.test-session :as session]
5 | [clojure.string :as str]
6 | [clojure.test :refer :all])
7 | (:import
8 | [org.apache.commons.lang3 SystemUtils]))
9 |
10 | (use-fixtures :each session/session-fixture)
11 |
12 | (defmethod content-type/content-type-response :graphviz [{:keys [value] :as response}]
13 | (let [{:keys [name edges]} value]
14 | (assoc response
15 | :content-type ["text/vnd.graphviz" {}]
16 | :body
17 | (str "graph " name " {\n"
18 | (str/join "\n"
19 | (for [[from to] edges]
20 | (str from " -- " to ";")))
21 | "\n}"))))
22 |
23 | (deftest content-type-middleware-test
24 | (testing "java.net.URI"
25 | (is (= {:body ""
26 | :content-type ["message/external-body"
27 | {:URL "https://lambdaisland.com"
28 | :access-type "URL"}]
29 | :status #{"done"}}
30 | (select-keys (session/message {:op "eval"
31 | :code "(java.net.URI. \"https://lambdaisland.com\")"
32 | :content-type "true"})
33 | [:body :content-type :content-transfer-encoding :status]))))
34 |
35 | (testing "java.net.URL"
36 | (is (= {:body ""
37 | :content-type ["message/external-body"
38 | {:URL "https://lambdaisland.com"
39 | :access-type "URL"}]
40 | :status #{"done"}}
41 | (select-keys (session/message {:op "eval"
42 | :code "(java.net.URL. \"https://lambdaisland.com\")"
43 | :content-type "true"})
44 | [:body :content-type :content-transfer-encoding :status]))))
45 |
46 | (testing "java.io.File"
47 | (let [f (java.io.File/createTempFile "foo" ".txt")
48 | path (.getCanonicalPath f)]
49 | (is (= {:body ""
50 | :content-type
51 | ["message/external-body"
52 | {:URL (str "file:" path) :access-type "URL"}]
53 | :status #{"done"}}
54 | (-> {:op "eval"
55 | :code (str "(java.io.File. " (pr-str path) ")")
56 | :content-type "true"}
57 | session/message
58 | (select-keys [:body :content-type :content-transfer-encoding :status]))))))
59 |
60 | (testing "java.awt.image.RenderedImage"
61 | (is (= {:body (if (SystemUtils/IS_JAVA_1_8)
62 | "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR42mNgYGAAAAAEAAHI6uv5AAAAAElFTkSuQmCC"
63 | "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4XmNgYGAAAAAEAAEP0q3kAAAAAElFTkSuQmCC")
64 | :content-type ["image/png" {}]
65 | :content-transfer-encoding "base64"
66 | :status #{"done"}}
67 | (-> {:op "eval"
68 | :code "(java.awt.image.BufferedImage. 1 1 java.awt.image.BufferedImage/TYPE_INT_RGB)"
69 | :content-type "true"}
70 | session/message
71 | (select-keys [:body :content-type :content-transfer-encoding :status])))))
72 |
73 | (testing "custom type implementation"
74 | (is (= {:body "graph foo {\na -- b;\nb -- c;\n}"
75 | :content-type ["text/vnd.graphviz" {}]
76 | :status #{"done"}}
77 | (-> {:op "eval"
78 | :code (str "^{:type :graphviz} "
79 | (pr-str
80 | {:name "foo"
81 | :edges [["a" "b"] ["b" "c"]]}))
82 | :content-type "true"}
83 | session/message
84 | (select-keys [:body :content-type :content-transfer-encoding :status]))))))
85 |
--------------------------------------------------------------------------------
/test/clj/cider/nrepl/middleware/info_spec_test.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.info-spec-test
2 | (:require
3 | [cider.nrepl.test-session :as session]
4 | [clojure.spec.alpha :as s]
5 | [clojure.test :refer :all]
6 | [cider.test-ns.first-test-ns]
7 | [cider.test-ns.second-test-ns]
8 | [cider.test-ns.third-test-ns]))
9 |
10 | (defn ranged-rand
11 | "Returns random int in range start <= rand < end."
12 | [start end]
13 | (+ start (long (rand (- end start)))))
14 |
15 | (s/fdef ranged-rand
16 | :args (s/and (s/cat :start int? :end int?)
17 | #(< (:start %) (:end %)))
18 | :ret int?
19 | :fn (s/and #(>= (:ret %) (-> % :args :start))
20 | #(< (:ret %) (-> % :args :end))))
21 |
22 | (use-fixtures :each session/session-fixture)
23 |
24 | (deftest integration-test
25 | (testing "spec info on a normal function with spec"
26 | (let [response (session/message {:op "info" :sym "ranged-rand" :ns "cider.nrepl.middleware.info-spec-test"})]
27 | (is (= (:status response) #{"done"}))
28 | (is (= (:ns response) "cider.nrepl.middleware.info-spec-test"))
29 | (is (= (:name response) "ranged-rand"))
30 | (is (= (:arglists-str response) "[start end]"))
31 | (is (nil? (:macro response)))
32 | (is (= (:doc response) "Returns random int in range start <= rand < end."))
33 | (is (= (:spec response) ["clojure.spec.alpha/fspec"
34 | ":args" ["clojure.spec.alpha/and"
35 | ["clojure.spec.alpha/cat" ":start" "clojure.core/int?" ":end" "clojure.core/int?"]
36 | ["clojure.core/fn" ["%"] ["clojure.core/<" [":start" "%"] [":end" "%"]]]]
37 | ":ret" "clojure.core/int?"
38 | ":fn" ["clojure.spec.alpha/and"
39 | ["clojure.core/fn" ["%"] ["clojure.core/>=" [":ret" "%"] ["clojure.core/->" "%" ":args" ":start"]]]
40 | ["clojure.core/fn" ["%"] ["clojure.core/<" [":ret" "%"] ["clojure.core/->" "%" ":args" ":end"]]]]]))))
41 | (testing "same name testing function without a spec"
42 | ;; spec is not defined for this function
43 | (let [response (session/message {:op "info" :sym "same-name-testing-function" :ns "cider.test-ns.first-test-ns"})]
44 | (is (= (:status response) #{"done"}))
45 | (is (= (:ns response) "cider.test-ns.first-test-ns"))
46 | (is (= (:name response) "same-name-testing-function"))
47 | (is (= (:arglists-str response) "[]"))
48 | (is (nil? (:macro response)))
49 | (is (= (:doc response) "Multiple vars with the same name in different ns's. Used to test ns-list-vars-by-name."))
50 | (is (nil? (:spec response)))))
51 |
52 | (testing "spec info on clojure.core/let"
53 | (let [response (session/message {:op "info" :sym "let" :ns "cider.nrepl.middleware.info-spec-test"})]
54 | (is (= (:status response) #{"done"}))
55 | (is (= (:ns response) "clojure.core"))
56 | (is (= (:name response) "let"))
57 | (is (= (:spec response) ["clojure.spec.alpha/fspec"
58 | ":args" ["clojure.spec.alpha/cat"
59 | ":bindings" ":clojure.core.specs.alpha/bindings"
60 | ":body" ["clojure.spec.alpha/*" "clojure.core/any?"]]
61 | ":ret" "clojure.core/any?" ":fn" ""])))))
62 |
--------------------------------------------------------------------------------
/test/clj/cider/nrepl/middleware/profile_test.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.profile-test
2 | (:require
3 | [cider.nrepl.test-session :as session]
4 | [cider.test-helpers :refer :all]
5 | [clojure.test :refer :all]
6 | [matcher-combinators.matchers :as mc]
7 | [orchard.profile]))
8 |
9 | (defn- with-clear-and-unprofile [f]
10 | (orchard.profile/clear)
11 | (orchard.profile/unprofile-all)
12 | (f))
13 |
14 | (use-fixtures :each session/session-fixture with-clear-and-unprofile)
15 |
16 | (deftest toggle-profile-test
17 | (testing "profile toggling"
18 | (is+ {:value ["profiled"] :status #{"done"}}
19 | (session/message {:op "cider/profile-toggle-var"
20 | :ns "clojure.core"
21 | :sym "zipmap"}))
22 | (is+ {:value ["unprofiled"] :status #{"done"}}
23 | (session/message {:op "cider/profile-toggle-var"
24 | :ns "clojure.core"
25 | :sym "zipmap"}))))
26 |
27 | (deftest profile-summary-test
28 | (testing "Var profile sumary"
29 | (session/message {:op "cider/profile-toggle-var"
30 | :ns "clojure.core"
31 | :sym "zipmap"})
32 | (is (zipmap [:a :b :c] [1 2 3]))
33 | (is+ {:status #{"done"}
34 | :value [(mc/via read-string
35 | (mc/prefix ["Class: " [:value "clojure.lang.ArraySeq" 0] [:newline]
36 | "Count: 1" [:newline] [:newline]
37 | "--- Contents:" [:newline] [:newline]]))]}
38 | (session/message {:op "cider/profile-summary"}))))
39 |
40 | (deftest toggle-profile-ns-test
41 | (testing "toggling profile ns"
42 | (is+ {:value ["profiled"] :status #{"done"}}
43 | (session/message {:op "cider/profile-toggle-ns"
44 | :ns "clojure.string"}))
45 | (is+ {:value ["unprofiled"] :status #{"done"}}
46 | (session/message {:op "cider/profile-toggle-ns"
47 | :ns "clojure.string"}))))
48 |
--------------------------------------------------------------------------------
/test/clj/cider/nrepl/middleware/reload_test.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.reload-test
2 | (:require
3 | [cider.nrepl.middleware.reload :as rl]
4 | [cider.nrepl.test-session :as session]
5 | [clojure.string :as str]
6 | [clojure.test :refer :all]))
7 |
8 | (use-fixtures :each session/session-fixture)
9 |
10 | (def ^:private dirs-to-reload
11 | ;; Limit the scope of what we reload, because (for example) reloading the
12 | ;; cider.nrepl.middleware.test-session ns causes *session* in that ns to be
13 | ;; unloaded, which breaks session-fixture, and hence all of the below tests.
14 | ["test/clj/cider/nrepl/middleware/util"])
15 |
16 | ;; Calling init from reload ns to work around mrandersonized version
17 | ;; See cider.nrepl.middleware.refresh-test for another test that suffers from this.
18 | (#'rl/init dirs-to-reload)
19 |
20 | (deftest user-reload
21 | (testing "returns fallback if clojure.tools.namespace isn't loaded"
22 | (with-redefs [resolve (constantly nil)]
23 | (is (= :foo (#'rl/user-reload 'reload :foo))))))
24 |
25 | (deftest reload-op-test
26 | (testing "reload op works"
27 | (let [response (session/message {:op "cider.clj-reload/reload"})]
28 | ;; There is nothing to reload since the files did not change,
29 | ;; but the message does come from clj-reload.core/reload.
30 | ;; It's two separate messages, but in (:progress response) they are
31 | ;; concatenated.
32 | (is (= "Nothing to unloadNothing to reload" (:progress response)))
33 | (is (= #{"done" "ok"} (:status response))))))
34 |
35 | (deftest reload-all-op-test
36 | (testing "reload-all op works"
37 | (let [response (session/message {:op "cider.clj-reload/reload-all"})
38 | progress-str (:progress response)]
39 | (is (str/includes? progress-str "Unloading cider.nrepl.middleware.util.meta-test"))
40 | (is (str/includes? progress-str "Loading cider.nrepl.middleware.util.meta-test"))
41 | (is (= #{"done" "ok"} (:status response))))))
42 |
43 | (deftest reload-clear-op-test
44 | (testing "reload-all op works"
45 | (let [response (session/message {:op "cider.clj-reload/reload-clear"})]
46 | (is (seq (:progress response)))
47 | (is (= "Nothing to unload" (:progress response)))
48 | (is (= #{"done" "ok"} (:status response))))))
49 |
50 |
--------------------------------------------------------------------------------
/test/clj/cider/nrepl/middleware/resource_test.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.resource-test
2 | (:require
3 | [cider.nrepl.test-session :as session]
4 | [clojure.test :refer :all]))
5 |
6 | (use-fixtures :once session/session-fixture)
7 | (deftest resource-op-test
8 | (let [response (session/message {:op "resource" :name "test.txt"})]
9 | (is (= #{"done"} (:status response)))
10 | (is (-> response ^String (:resource-path) (.endsWith "test/resources/test.txt")))))
11 |
12 | (deftest resources-list-test
13 | (testing "Basic checks"
14 | (let [response (session/message {:op "resources-list"})]
15 | (is (= #{"done"} (:status response)))
16 | (is (seq (:resources-list response)))
17 | (is (seq (filter #(re-matches #"test\.txt" (:relpath %))
18 | (:resources-list response))))
19 | (is (seq (filter #(re-matches #".*test/resources/test\.txt" (:file %))
20 | (:resources-list response)))))))
21 |
--------------------------------------------------------------------------------
/test/clj/cider/nrepl/middleware/slurp_test.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.slurp-test
2 | (:require
3 | [cider.nrepl.middleware.slurp :refer [slurp-url-to-content+body]]
4 | [clojure.java.io :as io]
5 | [clojure.test :as t]
6 | [clojure.string :as str]
7 | [orchard.misc :refer [java-api-version]]))
8 |
9 | (t/deftest test-project-clj-is-clj
10 | (let [resp (-> "project.clj"
11 | io/file
12 | io/as-url
13 | .toString
14 | slurp-url-to-content+body)]
15 | (t/is (= ["text/clojure" {}] (:content-type resp)))
16 | (t/is (not= "base64" (:content-transfer-encoding resp)))))
17 |
18 | (when-not (= 8 java-api-version)
19 | (t/deftest test-sum-types-is-base64
20 | (let [resp (slurp-url-to-content+body
21 | (.toString
22 | (io/resource "sum-types-are-cool.jpg")))]
23 | (t/is (= ["image/jpeg" {}] (:content-type resp)))
24 | (t/is (= "base64" (:content-transfer-encoding resp))))))
25 |
26 | (t/deftest test-unrecognized-file
27 | (let [resp (slurp-url-to-content+body
28 | (.toString (io/resource "unknown.unknown")))]
29 | (t/is (= ["application/octet-stream" {}] (:content-type resp)))
30 | (t/is (str/starts-with? (:body resp) "#binary[location="))
31 | (t/is (str/ends-with? (:body resp) ",size=681]"))))
32 |
33 | (t/deftest test-directory
34 | (let [resp (-> "test"
35 | io/file
36 | io/as-url
37 | .toString
38 | slurp-url-to-content+body)]
39 | (t/is (= ["application/octet-stream" {}] (:content-type resp)))
40 | (t/is (str/starts-with? (:body resp) "#binary[location="))
41 | (t/is (str/ends-with? (:body resp) ",size=0]"))))
42 |
--------------------------------------------------------------------------------
/test/clj/cider/nrepl/middleware/spec_test.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.spec-test
2 | (:require
3 | [cider.nrepl.middleware.spec :as cider-spec]
4 | [cider.nrepl.test-session :as session]
5 | [clojure.test :refer :all]))
6 |
7 | ;; integration tests
8 |
9 | (use-fixtures :each session/session-fixture)
10 |
11 | (deftest spec-list-integration-test
12 | (let [filter-regex "clojure"
13 | filtered-spec-list (:spec-list (session/message {:op "spec-list"
14 | :filter-regex filter-regex}))]
15 | (testing "Filtered spec list retrieving nothing extra"
16 | (is (every? #(re-find (re-pattern (str ":?" filter-regex)) %)
17 | filtered-spec-list)))
18 | (testing "Filtering with simple words regex"
19 | (is (= (count filtered-spec-list)
20 | (count (:spec-list (session/message {:op "spec-list"
21 | :filter-regex (str filter-regex ".+")}))))))))
22 |
--------------------------------------------------------------------------------
/test/clj/cider/nrepl/middleware/stacktrace_test.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.stacktrace-test
2 | (:require
3 | [cider.nrepl.middleware.stacktrace :as sut :refer :all]
4 | [cider.nrepl.test-session :as session]
5 | [clojure.test :refer :all]))
6 |
7 | (use-fixtures :each session/session-fixture)
8 |
9 | ;; Stacktrace op (deprecated)
10 |
11 | (deftest stacktrace-bound-test
12 | (testing "stacktrace op with most recent exception bound"
13 | (session/message {:op "eval" :code "(first 1)"})
14 | (let [response (session/message {:op "stacktrace"})]
15 | (testing "returns the exception class"
16 | (is (= "java.lang.IllegalArgumentException" (:class response))))
17 | (testing "returns the exception message"
18 | (is (= "Don't know how to create ISeq from: java.lang.Long" (:message response))))
19 | (testing "returns done status"
20 | (is (= #{"done"} (:status response)))))))
21 |
22 | (deftest stacktrace-unbound-test
23 | (testing "stacktrace op with most recent exception unbound"
24 | (let [response (session/message {:op "stacktrace"})]
25 | (testing "returns done and no-error status"
26 | (is (= #{"done" "no-error"} (:status response)))))))
27 |
28 | ;; Analyze last stacktrace op
29 |
30 | (deftest analyze-last-stacktrace-bound-test
31 | (testing "stacktrace op with most recent exception bound"
32 | (session/message {:op "eval" :code "(first 1)"})
33 | (let [response (session/message {:op "analyze-last-stacktrace"})]
34 | (testing "returns the exception class"
35 | (is (= "java.lang.IllegalArgumentException" (:class response))))
36 | (testing "returns the exception message"
37 | (is (= "Don't know how to create ISeq from: java.lang.Long" (:message response))))
38 | (testing "returns done status"
39 | (is (= #{"done"} (:status response)))))
40 |
41 | (testing "`inspect-last-exception` op"
42 | (testing "Index 0 for an exception without extra causes"
43 | (let [{[^String first-value] :value} (session/message {:op "inspect-last-exception" :index 0})]
44 | (is (.startsWith first-value "(\"Class: \" (:value")
45 | "Returns an Inspector response")))
46 | (testing "Index out of bounds"
47 | (is (nil? (:value (session/message {:op "inspect-last-exception" :index 1})))))
48 | (testing "Indices 0, 1, 2 for an exception with one extra cause"
49 | (session/message {:op "eval" :code "(deref (future (/ 2 0)))"})
50 | (session/message {:op "analyze-last-stacktrace"})
51 | (is (seq (:value (session/message {:op "inspect-last-exception" :index 0}))))
52 | (let [{[^String first-value] :value} (session/message {:op "inspect-last-exception" :index 0})]
53 | (is (.startsWith first-value "(\"Class: \" (:value \"java.util.concurrent.ExecutionException")
54 | "Returns an Inspector response for index 0"))
55 | (let [{[^String first-value] :value} (session/message {:op "inspect-last-exception" :index 1})]
56 | (is (.startsWith first-value "(\"Class: \" (:value \"java.lang.ArithmeticException")
57 | "Returns a different Inspector response for index 1"))
58 | (is (nil? (:value (session/message {:op "inspect-last-exception" :index 2})))
59 | "Doesn't return a value past the last index")))))
60 |
61 | (deftest analyze-last-stacktrace-unbound-test
62 | (testing "stacktrace op with most recent exception unbound"
63 | (let [response (session/message {:op "analyze-last-stacktrace"})]
64 | (testing "returns done and no-error status"
65 | (is (= #{"done" "no-error"} (:status response)))))))
66 |
--------------------------------------------------------------------------------
/test/clj/cider/nrepl/middleware/test/extensions_test.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.test.extensions-test
2 | (:require
3 | [cider.nrepl.middleware.test.extensions :as extensions]
4 | [clojure.test :refer [are deftest is testing]]))
5 |
6 | (deftest =-body-test
7 | (testing "Only evalulates expected form once"
8 | (let [x (eval
9 | `(let [~'x (atom 0)]
10 | ~(extensions/=-body "" '(swap! x inc) '(1))
11 | (deref ~'x)))]
12 | (is (= 1 x))))
13 | (testing "Evaluates forms in the right order"
14 | (let [a (atom 0)]
15 | (is (= (- (swap! a inc) 1)
16 | (- (swap! a inc) 2)
17 | (- (swap! a inc) 3)
18 | (- (swap! a inc) 4)
19 | (- (swap! a inc) 5)
20 | (- (swap! a inc) 6)
21 | (- (swap! a inc) 7)
22 | (- (swap! a inc) 8)
23 | (- (swap! a inc) 9)))))
24 | (testing ":actual is a scalar for (= x y) (i.e. arity 2)
25 | and an informative list for (= x y z) (i.e. arity 3+)"
26 | (are [subject args expected] (= expected
27 | (let [proof (atom nil)]
28 | (with-redefs [clojure.test/do-report
29 | (fn [m]
30 | (reset! proof m))]
31 | (eval (extensions/=-body "_" subject args))
32 | @proof)))
33 | 1 [1] '{:expected 1, :actual 1, :message "_", :type :pass}
34 | 1 [2] '{:expected 1, :actual 2, :message "_", :type :fail}
35 | 1 [2 3] '{:expected 1, :actual (not (= 1 2 3)), :message "_", :type :fail}))
36 |
37 | (testing ":diffs are only included for diffable objects"
38 | (are [subject args expected] (= expected
39 | (:diffs (extensions/maybe-assoc-diffs {} subject args)))
40 | 1 [1] nil
41 | 1 [2 3] nil
42 | ;; one different, diffable value:
43 | {:a :b} [{:a :c}] '[[{:a :c} ({:a :b} {:a :c} nil)]]
44 | ;; two different, diffable values:
45 | {:a :b} [{:a :c} {:a :d}] '[[{:a :c} ({:a :b} {:a :c} nil)]
46 | [{:a :d} ({:a :b} {:a :d} nil)]]
47 | ;; one different, diffable value at first position:
48 | {:a :b} [{:a :c} {:a :b}] '[[{:a :c} ({:a :b} {:a :c} nil)]
49 | ;; note that this diff is useless, but we include it for consistency for clients:
50 | [{:a :b} [nil nil {:a :b}]]]
51 | ;; one different, diffable value at last position:
52 | {:a :b} [{:a :b} {:a :d}] '[[{:a :b} [nil nil {:a :b}]] ;; (another 'useless' diff, see previous note)
53 | [{:a :d} ({:a :b} {:a :d} nil)]])))
54 |
--------------------------------------------------------------------------------
/test/clj/cider/nrepl/middleware/test_filter_tests.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.test-filter-tests
2 | "empty tests for tests testing the filter feature
3 | in `cider.nrepl.middleware.test-test` namespace"
4 | (:require
5 | [clojure.test :refer :all]))
6 |
7 | (deftest ^:smoke a-puff-of-smoke-test
8 | (is true "puff"))
9 |
10 | (deftest ^:integration ^:smoke a-smokey-test
11 | (is true "puff"))
12 |
13 | (deftest yet-an-other-test
14 | (is true "yet an other"))
15 |
16 | (deftest test-with-map-as-message
17 | (is true {:key "val"}))
18 |
--------------------------------------------------------------------------------
/test/clj/cider/nrepl/middleware/trace_test.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.trace-test
2 | (:require
3 | [cider.nrepl.middleware.trace :refer :all]
4 | [cider.nrepl.test-session :as session]
5 | [clojure.test :refer :all]
6 | [cider.test-ns.first-test-ns]))
7 |
8 | (use-fixtures :each session/session-fixture)
9 |
10 | (deftest toggle-trace-var-test
11 | (testing "toggling"
12 | (is (= {:var-name "#'clojure.core/zipmap" :var-status "traced"}
13 | (toggle-trace-var {:ns "clojure.core" :sym "zipmap"})))
14 | (is (= {:var-name "#'clojure.core/zipmap" :var-status "untraced"}
15 | (toggle-trace-var {:ns "clojure.core" :sym "zipmap"}))))
16 |
17 | (testing "misses"
18 | (testing "toggle-trace-var-op unresolvable, should return `not-found`"
19 | (is (= {:var-status "not-found" :status #{:toggle-trace-error :done}}
20 | (toggle-trace-var {:ns "clojure.core" :sym "mappp"}))))
21 |
22 | (testing "toggle-trace-var-op not traceable var, should return `not-traceable`"
23 | (is (= {:var-name "#'clojure.core/and" :var-status "not-traceable"}
24 | (toggle-trace-var {:ns "clojure.core" :sym "and"}))))))
25 |
26 | (deftest toggle-trace-ns-test
27 | (testing "toggling"
28 | (is (= {:ns-status "traced"}
29 | (toggle-trace-ns {:ns "orchard.inspect"})))
30 | (is (= {:ns-status "untraced"}
31 | (toggle-trace-ns {:ns "orchard.inspect"}))))
32 |
33 | (testing "toggle-trace-ns-op missing ns should return `not-found`"
34 | (is (= {:ns-status "not-found"}
35 | (toggle-trace-ns {:ns "clojure.corex"})))))
36 |
37 | (deftest integration-tests-var
38 | (testing "toggling"
39 | (let [on (session/message {:op "toggle-trace-var"
40 | :ns "cider.test-ns.first-test-ns"
41 | :sym "same-name-testing-function"})
42 | off (session/message {:op "toggle-trace-var"
43 | :ns "cider.test-ns.first-test-ns"
44 | :sym "same-name-testing-function"})]
45 | (is (= (:status on) (:status off) #{"done"}))
46 | (is (= (:var-name on) (:var-name off) "#'cider.test-ns.first-test-ns/same-name-testing-function"))
47 | (is (= (:var-status on) "traced"))
48 | (is (= (:var-status off) "untraced"))))
49 |
50 | (testing "unresolvable"
51 | (let [var-err (session/message {:op "toggle-trace-var"
52 | :ns "cider.test-ns.first-test-ns"
53 | :sym "missing"})
54 | ns-err (session/message {:op "toggle-trace-var"
55 | :ns "cider.test-ns.no-such-ns"
56 | :sym "same-name-testing-function"})]
57 | (is (= (:status var-err) (:status ns-err) #{"toggle-trace-error" "done"}))
58 | (is (:var-status var-err) "not-found"))))
59 |
60 | (deftest integration-test-ns
61 | (testing "toggling"
62 | (let [on (session/message {:op "toggle-trace-ns"
63 | :ns "cider.test-ns.first-test-ns"})
64 | off (session/message {:op "toggle-trace-ns"
65 | :ns "cider.test-ns.first-test-ns"})]
66 | (is (= (:status on) (:status off) #{"done"}))
67 | (is (= (:ns-status on) "traced"))
68 | (is (= (:ns-status off) "untraced")))
69 |
70 | (let [ns-err (session/message {:op "toggle-trace-ns"
71 | :ns "cider.test-ns.missing"})]
72 | (is (= (:status ns-err) #{"done"}))
73 | (is (= (:ns-status ns-err) "not-found")))))
74 |
--------------------------------------------------------------------------------
/test/clj/cider/nrepl/middleware/util/error_handling_test.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.util.error-handling-test
2 | (:require
3 | [cider.nrepl.middleware.util.error-handling :as err]
4 | [cider.nrepl.test-transport :as tt]
5 | [clojure.test :refer :all]))
6 |
7 | (deftest op-handler-test
8 | (is (= {:id 5 :status #{:test :done}} (err/op-handler :test {:id 5})))
9 | (is (= {:id 5 :status #{:more :than :one :done}} (err/op-handler [:more :than :one] {:id 5})))
10 | (is (= {:id 5 :anon 6 :status #{:done}} (err/op-handler (fn [m] {:anon (inc (:id m))}) {:id 5})))
11 | (is (= {:id 5 :inline :reply :status #{:done}} (err/op-handler {:inline :reply} {:id 5}))))
12 |
13 | (deftest error-handler-test
14 | (let [e (Exception. "testing")]
15 | (is (err/error-handler :done {:id 5} e))
16 | (is (err/error-handler [:more :than :one] {:id 5} e))
17 | (is (err/error-handler (fn [m e] {:anon (inc (:id m))}) {:id 5} e))
18 | (is (err/error-handler {:inline :reply} {:id 5} e))))
19 |
20 | (deftest bencode-test
21 | (testing "shallow-bencodable?"
22 | (let [bencodable? #'err/shallow-bencodable?]
23 | (is (bencodable? nil))
24 | (is (bencodable? 1))
25 | (is (not (bencodable? 1.2)))
26 | (is (not (bencodable? 1/2)))
27 | (is (bencodable? (byte-array [(byte 0x43) (byte 0x6c) (byte 0x6f)])))
28 | (is (bencodable? (boolean-array [false true false])))
29 | (is (bencodable? "string"))
30 | (is (bencodable? :kw))
31 | (is (bencodable? 'x))
32 | (is (bencodable? {:a :map}))
33 | (is (bencodable? [:a :vector 1 2 3]))
34 | (is (bencodable? '(:a :list)))
35 | (is (bencodable? #{:a :set}))
36 | (is (not (bencodable? *ns*)))
37 | (is (bencodable? [*ns*]) "This should pass since the function only does a shallow test.")))
38 |
39 | (testing "deep-bencodable-or-fail"
40 | (let [deep-bencodable? #'err/deep-bencodable-or-fail]
41 | (is (deep-bencodable? nil))
42 | (is (deep-bencodable? 1))
43 | (is (thrown? IllegalArgumentException (deep-bencodable? 1.2)))
44 | (is (thrown? IllegalArgumentException (deep-bencodable? 1/2)))
45 | (is (deep-bencodable? (byte-array [(byte 0x43) (byte 0x6c) (byte 0x6f)])))
46 | (is (deep-bencodable? (boolean-array [false true false])))
47 | (is (deep-bencodable? "string"))
48 | (is (deep-bencodable? :kw))
49 | (is (deep-bencodable? 'x))
50 | (is (deep-bencodable? {:a :map}))
51 | (is (deep-bencodable? [:a :vector 1 2 3]))
52 | (is (deep-bencodable? [:a :vector 1 {:a :map} 2 '(:a :list) 3]))
53 | (is (deep-bencodable? '(:a :list)))
54 | (is (deep-bencodable? #{:a :set}))
55 | (is (thrown? IllegalArgumentException (deep-bencodable? *ns*)))
56 | (is (thrown? IllegalArgumentException (deep-bencodable? [*ns*])))
57 | (is (thrown? IllegalArgumentException (deep-bencodable? [1 2 3 4 *ns*])))
58 | (is (deep-bencodable? [:a :vector 1 {:a :map} 2 '(:a {:bad-map *ns*} :list) 3])
59 | "Should pass since *ns* is inside a quoted list and doesn't get evaluated")
60 | (is (thrown? IllegalArgumentException (deep-bencodable? [:a :vector 1 {:a :map} 2 [:sub :vec :bad *ns*] '(:a :list) 3]))))))
61 |
62 | (deftest error-handler-root-ex
63 | (let [e (Exception. "testing" (Throwable. "root-cause"))
64 | e2 (Exception. "testing2")]
65 | (is (= "class java.lang.Throwable"
66 | (:root-ex (err/error-handler :done {:id 1} e))))
67 | (is (= "class java.lang.Exception"
68 | (:root-ex (err/error-handler :done {:id 2} e2))))))
69 |
--------------------------------------------------------------------------------
/test/clj/cider/nrepl/middleware/util/meta_test.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.util.meta-test
2 | (:require
3 | [cider.nrepl.middleware.util.meta :as um]
4 | [clojure.test :refer :all]))
5 |
6 | (defn- test-fn "docstring"
7 | ([a b] nil)
8 | ([a] nil)
9 | ([]))
10 |
11 | (deftest dummy-test)
12 |
13 | (deftest relevant-meta-test
14 | (is (= (um/relevant-meta (meta #'test-fn))
15 | {:arglists "([a b] [a] [])"
16 | :doc "\"docstring\""}))
17 | (is (= (:macro (um/relevant-meta (meta #'deftest)))
18 | "true"))
19 | (let [the-meta (meta #'dummy-test)]
20 | (is (= (um/relevant-meta (merge the-meta {:indent 1
21 | :cider-instrumented 2
22 | :something-else 3}))
23 | {:indent "1"
24 | :test (pr-str (:test the-meta))}))))
25 |
--------------------------------------------------------------------------------
/test/clj/cider/nrepl/middleware/version_test.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.version-test
2 | (:require
3 | [cider.nrepl.test-session :as session]
4 | [clojure.test :refer :all]))
5 |
6 | (use-fixtures :once session/session-fixture)
7 |
8 | (deftest integration-test
9 | (testing "cider-version op"
10 | (let [response (session/message {:op "cider-version"})
11 | version-map (:cider-version response)]
12 | (is (= #{"done"} (:status response)))
13 | (is (contains? version-map :major))
14 | (is (contains? version-map :minor))
15 | (is (contains? version-map :incremental))
16 | (is (contains? version-map :version-string))))
17 |
18 | (testing "describe op"
19 | (let [response (session/message {:op "describe"})
20 | aux-map (:aux response)
21 | version-map (:cider-version aux-map)]
22 | (is (= #{"done"} (:status response)))
23 | (is (contains? version-map :major))
24 | (is (contains? version-map :minor))
25 | (is (contains? version-map :incremental))
26 | (is (contains? version-map :version-string)))))
27 |
--------------------------------------------------------------------------------
/test/clj/cider/nrepl/middleware/xref_test.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.xref-test
2 | (:require
3 | [cider.nrepl.test-session :as session]
4 | [clojure.string :as str]
5 | [clojure.test :refer :all]))
6 |
7 | (use-fixtures :each session/session-fixture)
8 |
9 | (defn- foo [] (map inc (range 10)))
10 |
11 | (deftest fn-refs-integration-test
12 | (let [response (session/message {:op "fn-refs" :ns "clojure.core" :sym "map"})
13 | fn-refs (:fn-refs response)]
14 | (testing (pr-str response)
15 | (is (= (:status response) #{"done"}))
16 | (is (> (count fn-refs) 0))
17 | (is (every? map? fn-refs))))
18 |
19 | (testing "`:file-url`"
20 | (let [response (session/message {:op "fn-refs"
21 | :ns "cider.nrepl.middleware.xref"
22 | :sym "fn-refs-reply"})
23 | x (->> response
24 | :fn-refs
25 | (map :file-url))]
26 | (assert (= (:status response) #{"done"})
27 | (pr-str response))
28 | (assert (seq x))
29 | (doseq [i x]
30 | (is (or (str/starts-with? i "file:")
31 | (str/starts-with? i "jar:file:")))))))
32 |
33 | (deftest fn-deps-integration-test
34 | (let [response (session/message {:op "fn-deps" :ns "cider.nrepl.middleware.xref-test" :sym "foo"})
35 | fn-deps (:fn-deps response)]
36 | (is (= (:status response) #{"done"}))
37 | (is (= (count fn-deps) 3))
38 | (is (every? map? fn-deps)))
39 |
40 | (testing "`:file-url`"
41 | (let [x (->> {:op "fn-deps"
42 | :ns "cider.nrepl.middleware.xref"
43 | :sym "fn-refs-reply"
44 | :file-format "v2"}
45 | session/message
46 | :fn-deps
47 | (map :file-url))]
48 | (assert (seq x))
49 | (doseq [i x]
50 | (is (or (str/starts-with? i "file:")
51 | (str/starts-with? i "jar:file:")))))))
52 |
--------------------------------------------------------------------------------
/test/clj/cider/nrepl/plugin_test.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.plugin-test
2 | (:require
3 | [cider-nrepl.plugin :refer :all]
4 | [cider.nrepl.middleware :as mw]
5 | [cider.nrepl.version :refer [version-string]]
6 | [clojure.test :refer :all]))
7 |
8 | (def expected-output
9 | {:dependencies [['org.clojure/clojure]
10 | ['cider/cider-nrepl version-string]]
11 | :repl-options {:nrepl-middleware mw/cider-middleware}})
12 |
13 | (deftest version-checks
14 | (testing "undefined versions work"
15 | (is (= expected-output
16 | (middleware {:dependencies [['org.clojure/clojure]]}))))
17 | (testing "defined versions also work"
18 | (is (= (update-in expected-output [:dependencies 0] conj "1.10.0")
19 | (middleware {:dependencies [['org.clojure/clojure "1.10.0"]]})))))
20 |
--------------------------------------------------------------------------------
/test/clj/cider/nrepl/print_method_test.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.print-method-test
2 | (:require
3 | [cider.nrepl.print-method :refer :all]
4 | [clojure.test :refer :all]))
5 |
6 | (defn dummy-fn [o])
7 |
8 | (deftest print-atoms-test
9 | (is (= "#atom[\"\"]" (pr-str (atom ""))))
10 | (is (= "#atom[nil]" (pr-str (atom nil))))
11 | (is (= "#atom[{:foo :bar}]" (pr-str (atom {:foo :bar}))))
12 | (is (= "#atom[#function[clojure.core/+]]" (pr-str (atom +)))))
13 |
14 | (deftest print-idrefs-test
15 | (let [f (future (Thread/sleep 200) 1)
16 | p (promise)
17 | d (delay 1)
18 | a (agent 1)]
19 | (are [o r] (= r (pr-str o))
20 | a "#agent[1]"
21 | d "#delay[]"
22 | f "#future[]"
23 | p "#promise[]")
24 | (Thread/sleep 300)
25 | @d
26 | (deliver p 1)
27 | @f
28 | (are [o r] (= r (pr-str o))
29 | f "#future[1]"
30 | d "#delay[1]"
31 | p "#promise[1]")))
32 |
33 | (deftest print-functions-test
34 | (are [f s] (= (pr-str f) s)
35 | print-functions-test "#function[cider.nrepl.print-method-test/print-functions-test]"
36 | dummy-fn "#function[cider.nrepl.print-method-test/dummy-fn]"
37 | + "#function[clojure.core/+]"
38 | * "#function[clojure.core/*]"
39 | / "#function[clojure.core//]"
40 | fn? "#function[clojure.core/fn?]"))
41 |
42 | (deftest print-multimethods-test
43 | (require 'cider.nrepl.middleware.track-state)
44 | (let [var (resolve 'print-method)]
45 | (is (re-find (re-pattern (format "#multifn\\[%s 0x[a-z0-9]+\\]"
46 | (:name (meta var))))
47 | (pr-str @var)))))
48 |
49 | (deftest print-namespaces-test
50 | (are [f s] (= (pr-str f) s)
51 | (find-ns 'clojure.core) "#namespace[clojure.core]"
52 | (find-ns 'cider.nrepl.print-method) "#namespace[cider.nrepl.print-method]"
53 | (find-ns 'clojure.test) "#namespace[clojure.test]"))
54 |
--------------------------------------------------------------------------------
/test/clj/cider/nrepl/version_test.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.version-test
2 | (:require
3 | [cider.nrepl.version :as v]
4 | [clojure.test :refer :all]))
5 |
6 | (deftest version-string-test
7 | (is (string? v/version-string)))
8 |
9 | (deftest version-test
10 | (is (contains? v/version :major))
11 | (is (contains? v/version :minor))
12 | (is (contains? v/version :incremental))
13 | (is (contains? v/version :version-string)))
14 |
--------------------------------------------------------------------------------
/test/clj/cider/nrepl_test.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl-test
2 | (:require
3 | [cider.nrepl :as sut]
4 | [clojure.test :refer [deftest is testing]]))
5 |
6 | (deftest clojure-version-sanity-check
7 | (is (let [v (System/getenv "CLOJURE_VERSION")]
8 | (println "Running on Clojure" (clojure-version))
9 | (or (nil? v) (.startsWith ^String (clojure-version) v)))))
10 |
--------------------------------------------------------------------------------
/test/clj/cider/test_helpers.clj:
--------------------------------------------------------------------------------
1 | (ns cider.test-helpers
2 | (:require [clojure.test :refer :all]
3 | [matcher-combinators.matchers :as matchers]
4 | [matcher-combinators.test :refer [match?]]))
5 |
6 | (defmacro is+
7 | "Like `is` but wraps expected value in matcher-combinators's `match?`."
8 | ([expected actual]
9 | `(is+ ~expected ~actual nil))
10 | ([expected actual message]
11 | `(is (~'match? ~expected ~actual) ~message)))
12 |
--------------------------------------------------------------------------------
/test/cljs/cider/nrepl/middleware/cljs_complete_test.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.cljs-complete-test
2 | (:require
3 | [cider.nrepl.piggieback-test :refer [piggieback-fixture]]
4 | [cider.nrepl.test-session :as session]
5 | [clojure.test :refer :all]))
6 |
7 | (use-fixtures :once piggieback-fixture)
8 |
9 | (deftest cljs-complete-test
10 | (let [response (session/message {:op "complete"
11 | :prefix ""})]
12 | (is (= #{"done"} (:status response)))
13 | (is (sequential? (:completions response)))
14 | (is (every? map? (:completions response))))
15 |
16 | (let [response (session/message {:op "complete"
17 | :prefix "defpro"})
18 | candidate (first (:completions response))]
19 | (is (= "defprotocol" (:candidate candidate)))
20 | (is (= "cljs.core" (:ns candidate)))
21 | (is (= "macro" (:type candidate))))
22 |
23 | (testing "function metadata"
24 | (let [response (session/message {:op "complete"
25 | :prefix "assoc"
26 | :extra-metadata ["arglists" "doc"]})
27 | candidate (first (:completions response))]
28 | (is (= '("[coll k v]" "[coll k v & kvs]") (:arglists candidate)))
29 | (is (string? (:doc candidate)))))
30 |
31 | (testing "macro metadata"
32 | (let [response (session/message {:op "complete"
33 | :prefix "defprot"
34 | :extra-metadata ["arglists" "doc"]})
35 | candidate (first (:completions response))]
36 | (is (= '("[psym & doc+methods]") (:arglists candidate)))
37 | (is (string? (:doc candidate))))))
38 |
39 | (deftest cljs-complete-with-suitable-test
40 | (testing "js global completion"
41 | (let [response (session/message {:op "complete"
42 | :ns "cljs.user"
43 | :prefix "js/Ob"
44 | :enhanced-cljs-completion? "t"})
45 | candidates (:completions response)]
46 | (is (= [{:candidate "js/Object", :ns "js", :type "function"}] candidates))))
47 |
48 | (testing "manages context state"
49 | (session/message {:op "complete"
50 | :ns "cljs.user"
51 | :prefix ".xxxx"
52 | :context "(__prefix__ js/Object)"
53 | :enhanced-cljs-completion? "t"})
54 | (let [response (session/message {:op "complete"
55 | :ns "cljs.user"
56 | :prefix ".key"
57 | :context ":same"
58 | :enhanced-cljs-completion? "t"})
59 | candidates (:completions response)]
60 | (is (= [{:ns "js/Object", :candidate ".keys" :type "function"}] candidates))))
61 |
62 | (testing "no suitable completions without enhanced-cljs-completion? flag"
63 | (let [response (session/message {:op "complete"
64 | :ns "cljs.user"
65 | :prefix "js/Ob"})
66 | candidates (:completions response)]
67 | (is (empty? candidates))))
68 | (testing "local bindings"
69 | (let [response (session/message {:op "complete"
70 | :ns "cljs.user"
71 | :prefix "ba"
72 | ;; Including `quux :quux` helps ensuring that only
73 | ;; bindings with the specified prefix (ba*) will be
74 | ;; suggested, instead of all the local bindings.
75 | :context "(defn foo [bar] (let [baz :baz, quux :quux] (str __prefix__)))"
76 | :enhanced-cljs-completion? "t"})
77 | candidates (:completions response)]
78 | (is (= [{:candidate "bar", :type "local"}, {:candidate "baz", :type "local"}] candidates)))))
79 |
80 | (deftest cljs-complete-doc-test
81 | (testing "no suitable documentation can be found"
82 | (let [response (session/message {:op "complete-doc" :sym "tru"})]
83 | (is (= (:status response) #{"done"}))
84 | (is (empty? (:completion-doc response)) "an unknown symbol should have empty doc.")))
85 |
86 | (testing "suitable documentation for a symbol"
87 | (let [response (session/message {:op "complete-doc" :sym "map"})]
88 | (is (= (:status response) #{"done"}))
89 | (is (re-find #"\(\[f\] \[f coll\] \[f c1 c2\] \[f c1 c2 c3\] \[f c1 c2 c3 & colls\]\)" (:completion-doc response)) "should return the \"map\" docstring")))
90 |
91 | (testing "suitable documentation for a macro symbol"
92 | (let [response (session/message {:op "complete-doc" :sym "when"})]
93 | (is (= (:status response) #{"done"}))
94 | (is (re-find #"\(\[test & body\]\)" (:completion-doc response)) "should return the \"when\" docstring"))))
95 |
--------------------------------------------------------------------------------
/test/cljs/cider/nrepl/middleware/cljs_info_test.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.cljs-info-test
2 | (:require
3 | [cider.nrepl.piggieback-test :refer [piggieback-fixture]]
4 | [cider.nrepl.test-session :as session]
5 | [clojure.test :refer :all]))
6 |
7 | (use-fixtures :once piggieback-fixture)
8 |
9 | (deftest cljs-info-test
10 | (let [response (session/message {:op "info"
11 | :ns "cljs.core"
12 | :sym "map"})]
13 | (is (= "cljs.core" (:ns response)))
14 | (is (= "map" (:name response)))
15 | (is (string? (:arglists-str response)))
16 | (is (string? (:doc response)))
17 | (is (string? (:file response)))
18 | (is (:line response))
19 | (is (:column response))
20 | (is (= #{"done"} (:status response))))
21 |
22 | (let [{:keys [status]} (session/message {:op "info"
23 | :ns "cljs.core"
24 | :sym "non-existent-var"})]
25 | (is (= #{"no-info" "done"} status))))
26 |
27 | (deftest cljs-eldoc-test
28 | (let [response (session/message {:op "eldoc"
29 | :ns "cljs.core"
30 | :sym "println"})]
31 | (is (= [["&" "objs"]] (:eldoc response)))
32 | (is (= #{"done"} (:status response)))))
33 |
--------------------------------------------------------------------------------
/test/cljs/cider/nrepl/middleware/cljs_inspect_test.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.cljs-inspect-test
2 | (:require
3 | [cider.nrepl.middleware.inspect :as i]
4 | [cider.nrepl.middleware.inspect-test :as inspect-test]
5 | [cider.nrepl.piggieback-test :refer [piggieback-fixture]]
6 | [cider.nrepl.test-session :as session]
7 | [cider.test-helpers :refer :all]
8 | [clojure.edn :as edn]
9 | [clojure.test :refer :all]
10 | [matcher-combinators.matchers :as matchers]))
11 |
12 | (def nil-result
13 | ["Value: nil" [:newline] [:newline] "--- Contents:" [:newline] string?])
14 |
15 | (def code "(sorted-map :a {:b 1} :c \"a\" :d 'e :f [2 3])")
16 |
17 | (def inspect-contents
18 | ["--- Contents:" [:newline]
19 | " " [:value ":a" 1] " = " [:value "{:b 1}" 2] [:newline]
20 | " " [:value ":c" 3] " = " [:value "\"a\"" 4] [:newline]
21 | " " [:value ":d" 5] " = " [:value "e" 6] [:newline]
22 | " " [:value ":f" 7] " = " [:value "[2 3]" 8]])
23 |
24 | (def push-contents
25 | ["--- Contents:" [:newline]
26 | " " [:value ":b" 1] " = " [:value "1" 2]])
27 |
28 | (def next-page-contents
29 | ["--- Contents:" [:newline]
30 | " ..." [:newline]
31 | " 32. " [:value "32" 1] [:newline]
32 | " 33. " [:value "33" 2] [:newline]
33 | " 34. " [:value "34" 3]])
34 |
35 | (defn value [{:keys [value]}]
36 | (edn/read-string (first value)))
37 |
38 | ;; integration tests
39 |
40 | (defn with-fresh-inspector
41 | [f]
42 | (dorun (session/message {:op "inspect-clear"}))
43 | (f))
44 |
45 | (use-fixtures :once piggieback-fixture)
46 | (use-fixtures :each with-fresh-inspector)
47 |
48 | (deftest nil-integration-test
49 | (testing "nil renders correctly"
50 | (is+ (matchers/prefix nil-result)
51 | (value (session/message {:op "eval"
52 | :inspect "true"
53 | :code "nil"})))))
54 |
55 | (deftest pop-empty-integration-test
56 | (testing "popping an empty inspector renders nil"
57 | (is+ (matchers/prefix nil-result)
58 | (value (session/message {:op "inspect-pop"})))))
59 |
60 | (deftest pop-empty-idempotent-integration-test
61 | (testing "popping an empty inspector is idempotent"
62 | (is+ (matchers/prefix nil-result)
63 | (value (do
64 | (session/message {:op "inspect-pop"})
65 | (session/message {:op "inspect-pop"}))))))
66 |
67 | (deftest push-empty-integration-test
68 | (testing "pushing an empty inspector index renders nil"
69 | (is+ (matchers/prefix nil-result)
70 | (value (session/message {:op "inspect-push"
71 | :idx 2})))))
72 |
73 | (deftest push-empty-idempotent-integration-test
74 | (testing "pushing an empty inspector index is idempotent"
75 | (is+ (matchers/prefix nil-result)
76 | (value (do
77 | (session/message {:op "inspect-push"
78 | :idx 2})
79 | (session/message {:op "inspect-push"
80 | :idx 2}))))))
81 |
82 | (deftest refresh-empty-integration-test
83 | (testing "refreshing an empty inspector renders nil"
84 | (is+ (matchers/prefix nil-result)
85 | (value (session/message {:op "inspect-refresh"})))))
86 |
87 | (deftest refresh-empty-idempotent-integration-test
88 | (testing "refreshing an empty inspector renders nil"
89 | (is+ (matchers/prefix nil-result)
90 | (value (do
91 | (session/message {:op "inspect-refresh"})
92 | (session/message {:op "inspect-refresh"}))))))
93 |
94 | (deftest inspect-expr-integration-test
95 | (testing "rendering an expr"
96 | (is+ inspect-contents
97 | (-> (session/message {:op "eval"
98 | :inspect "true"
99 | :code code})
100 | value (inspect-test/section "Contents")))))
101 |
102 | (deftest push-integration-test
103 | (testing "pushing a rendered expr inspector idx"
104 | (is+ push-contents
105 | (-> (do
106 | (session/message {:op "eval"
107 | :inspect "true"
108 | :code code})
109 | (session/message {:op "inspect-push"
110 | :idx 2}))
111 | value (inspect-test/section "Contents")))))
112 |
113 | (deftest pop-integration-test
114 | (testing "popping a rendered expr inspector"
115 | (is+ inspect-contents
116 | (-> (do
117 | (session/message {:op "eval"
118 | :inspect "true"
119 | :code code})
120 | (session/message {:op "inspect-push"
121 | :idx 2})
122 | (session/message {:op "inspect-pop"}))
123 | value (inspect-test/section "Contents")))))
124 |
125 | (deftest next-page-integration-test
126 | (testing "jumping to next page in a rendered expr inspector"
127 | (is+ next-page-contents
128 | (-> (do (session/message {:op "eval"
129 | :inspect "true"
130 | :code "(list* (map identity (range 35)))"})
131 | (session/message {:op "inspect-next-page"}))
132 | value (inspect-test/section "Contents")))))
133 |
134 | (deftest refresh-integration-test
135 | (testing "refreshing a rendered expr inspector"
136 | (is inspect-contents
137 | (-> (do
138 | (session/message {:op "eval"
139 | :inspect "true"
140 | :code code})
141 | (session/message {:op "inspect-refresh"}))
142 | value (inspect-test/section "Contents")))))
143 |
144 | (deftest session-binding-integration-test
145 | (testing "session bindings can be inspected"
146 | (is+ inspect-contents
147 | (-> (do
148 | (session/message {:op "eval"
149 | :inspect "true"
150 | :code code})
151 | (session/message {:op "eval"
152 | :inspect "true"
153 | :code "*1"}))
154 | value (inspect-test/section "Contents")))))
155 |
--------------------------------------------------------------------------------
/test/cljs/cider/nrepl/middleware/cljs_ns_test.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.cljs-ns-test
2 | (:require
3 | [cider.nrepl.piggieback-test :refer [piggieback-fixture]]
4 | [cider.nrepl.test-session :as session]
5 | [clojure.test :refer :all]))
6 |
7 | (use-fixtures :once piggieback-fixture)
8 |
9 | (deftest cljs-ns-test
10 | (testing "ns-list op"
11 | (let [{:keys [ns-list]} (session/message {:op "ns-list"})]
12 | (is (sequential? ns-list))
13 | (is (every? string? ns-list))))
14 |
15 | (testing "ns-vars op"
16 | (let [{:keys [ns-vars]} (session/message {:op "ns-vars"
17 | :ns "cljs.core"})]
18 | (is (sequential? ns-vars))
19 | (is (every? string? ns-vars))
20 | (is (not (contains? (set ns-vars) "maybe-warn")))))
21 |
22 | (testing "ns-vars op with private? var-query"
23 | (let [{:keys [ns-vars]} (session/message {:op "ns-vars"
24 | :ns "cljs.core"
25 | :var-query {:private? 1}})]
26 | (is (sequential? ns-vars))
27 | (is (every? string? ns-vars))
28 | (is (contains? (set ns-vars) "maybe-warn"))))
29 |
30 | (testing "ns-vars-with-meta op"
31 | (let [ns-vars-with-meta (:ns-vars-with-meta
32 | (session/message {:op "ns-vars-with-meta"
33 | :ns "cljs.core"}))]
34 | (is (every? (comp map? second) ns-vars-with-meta))
35 | (is (= (:+ ns-vars-with-meta)
36 | {:arglists "(quote ([] [x] [x y] [x y & more]))"
37 | :doc "\"Returns the sum of nums. (+) returns 0.\""}))
38 | (is (not (contains? ns-vars-with-meta :maybe-warn)))))
39 |
40 | (testing "ns-vars-with-meta op with private? var-query"
41 | (let [ns-vars-with-meta (:ns-vars-with-meta
42 | (session/message {:op "ns-vars-with-meta"
43 | :ns "cljs.core"
44 | :var-query {:private? 1}}))]
45 | (is (every? (comp map? second) ns-vars-with-meta))
46 | (is (= (:maybe-warn ns-vars-with-meta)
47 | {:arglists "(quote ([e]))"}))))
48 |
49 | (testing "ns-path op"
50 | (let [{:keys [^String path ^String url]} (session/message {:op "ns-path"
51 | :ns "cljs.core"})]
52 | (is (.endsWith path "cljs/core.cljs"))
53 | (is (not= path url))
54 | (is (.startsWith url "jar:file:/")))
55 |
56 | (let [{:keys [^String path]} (session/message {:op "ns-path"
57 | :ns "cljs.repl"})]
58 | (is (.endsWith path "cljs/repl.cljs")))))
59 |
--------------------------------------------------------------------------------
/test/cljs/cider/nrepl/middleware/cljs_stacktrace_test.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.cljs-stacktrace-test
2 | (:require
3 | [cider.nrepl.piggieback-test :refer [piggieback-fixture]]
4 | [cider.nrepl.test-session :as session]
5 | [clojure.test :refer :all]))
6 |
7 | (use-fixtures :once piggieback-fixture)
8 |
9 | (deftest cljs-stacktrace-test
10 | (testing "no last error"
11 | (let [response (session/message {:op "stacktrace"})]
12 | (is (= #{"no-error" "done"}
13 | (:status response)))))
14 | (testing "last error stacktrace"
15 | (let [response (do (session/message {:op "eval"
16 | :code "(ffirst 1)"})
17 | (session/message {:op "stacktrace"}))]
18 | (is (= #{"done"}
19 | (:status response)))
20 | (is (= "clojure.lang.ExceptionInfo"
21 | (:class response))))))
22 |
--------------------------------------------------------------------------------
/test/cljs/cider/nrepl/piggieback_test.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.piggieback-test
2 | (:require
3 | [cider.piggieback :as piggieback]
4 | [cider.nrepl.test-session :as session]
5 | [cider.nrepl.middleware :refer [cider-middleware]]
6 | [cljs.repl.node :as node]
7 | [clojure.test :refer :all]
8 | [nrepl.core :as nrepl]
9 | [nrepl.server :as server]))
10 |
11 | (def repl-env
12 | (delay (node/repl-env)))
13 |
14 | (def piggieback-fixture
15 | (compose-fixtures
16 | session/session-fixture
17 | (fn [f]
18 | (binding [session/*handler* (apply server/default-handler
19 | (conj (map resolve cider-middleware)
20 | #'piggieback/wrap-cljs-repl))]
21 | ;; TODO check the result of this; we shouldn't run any tests if it fails
22 | (dorun (session/message
23 | {:op "eval"
24 | :code (nrepl/code (require '[cider.piggieback :as piggieback])
25 | (piggieback/cljs-repl @cider.nrepl.piggieback-test/repl-env))}))
26 | (dorun (session/message {:op "eval"
27 | :code (nrepl/code (require 'clojure.data))}))
28 | (f)
29 | (session/message {:op "eval"
30 | :code (nrepl/code :cljs/quit)})))))
31 |
32 | (use-fixtures :each piggieback-fixture)
33 |
34 | (deftest sanity-test
35 | (testing "cljs repl is active"
36 | (let [response (session/message {:op "eval"
37 | :code (nrepl/code (js/Object.))})]
38 | (is (= "cljs.user" (:ns response)))
39 | (is (= ["#js{}"] (:value response)))
40 | (is (= #{"done"} (:status response)))))
41 |
42 | (testing "eval works"
43 | (let [response (session/message {:op "eval"
44 | :code (nrepl/code (map even? (range 6)))})]
45 | (is (= "cljs.user" (:ns response)))
46 | (is (= ["(true false true false true false)"] (:value response)))
47 | (is (= #{"done"} (:status response)))))
48 |
49 | (testing "errors handled properly"
50 | (let [response (session/message {:op "eval"
51 | :code (nrepl/code (ffirst 1))})]
52 | (is (= "class clojure.lang.ExceptionInfo"
53 | (:ex response)
54 | (:root-ex response)))
55 | (is (= #{"eval-error" "done"} (:status response))))))
56 |
--------------------------------------------------------------------------------
/test/common/cider/nrepl/test_session.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.test-session
2 | (:require
3 | [cider.nrepl :refer [cider-nrepl-handler]]
4 | [clojure.test :refer :all]
5 | [nrepl.core :as nrepl]
6 | [nrepl.server :refer [start-server]]))
7 |
8 | (def ^:dynamic *handler* cider-nrepl-handler)
9 | (def ^:dynamic *session* nil)
10 |
11 | (def ^:dynamic ^nrepl.server.Server *server* nil)
12 | (def ^:dynamic ^nrepl.transport.FnTransport *transport* nil)
13 |
14 | (defn repl-session!
15 | "Start an nREPL session and set *session* accordingly.
16 |
17 | Eval'ing this function in the REPL will allow you to test out messages
18 | with [[message]].
19 |
20 | When dealing with tests that use [[session-fixture]], this can help you to be
21 | able to evaluate test forms in the REPL. Call [[close-session!]] when you're
22 | done."
23 | []
24 | (let [server (start-server :handler *handler*)
25 | transport (nrepl/connect :port (:port server))
26 | client (nrepl/client transport Long/MAX_VALUE)]
27 | (alter-var-root #'*server* (constantly server))
28 | (alter-var-root #'*transport* (constantly transport))
29 | (alter-var-root #'*session* (constantly (nrepl/client-session client)))))
30 |
31 | (defn close-session!
32 | "Stop the server/session created by [[repl-session!]], and reset the vars."
33 | []
34 | (.close *server*)
35 | (.close *transport*)
36 | (alter-var-root #'*server* (constantly nil))
37 | (alter-var-root #'*transport* (constantly nil))
38 | (alter-var-root #'*session* (constantly nil)))
39 |
40 | (defn session-fixture
41 | [f]
42 | (with-open [^nrepl.server.Server
43 | server (start-server :handler *handler*)
44 | ^nrepl.transport.FnTransport
45 | transport (nrepl/connect :port (:port server))]
46 | (let [client (nrepl/client transport Long/MAX_VALUE)
47 | session (nrepl/client-session client)]
48 | (binding [*server* server
49 | *transport* transport
50 | *session* session]
51 | (f)))))
52 |
53 | (defn message
54 | ([msg] (message msg true))
55 | ([msg combine-responses?]
56 | (let [responses (nrepl/message *session* msg)]
57 | (if combine-responses?
58 | (nrepl/combine-responses responses)
59 | responses))))
60 |
61 | (use-fixtures :each session-fixture)
62 |
63 | (deftest sanity-test
64 | (testing "eval works"
65 | (is (= ["(true false true false true false)"]
66 | (:value (message {:op "eval"
67 | :code (nrepl/code (map even? (range 6)))})))))
68 |
69 | (testing "unsupported op"
70 | (is (= #{"error" "unknown-op" "done"}
71 | (:status (message {:op "abcdefg"})))))
72 |
73 | (testing "describe works"
74 | (let [response (message {:op "describe"})
75 | verbose-response (message {:op "describe"
76 | :verbose? "true"})]
77 | (is (contains? response :ops))
78 | (is (contains? verbose-response :ops)))))
79 |
--------------------------------------------------------------------------------
/test/common/cider/nrepl/test_transport.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.test-transport
2 | "A transport for testing"
3 | (:use
4 | [nrepl.transport :only [Transport]]))
5 |
6 | (defrecord TestTransport [msgs]
7 | Transport
8 | (recv [_] nil)
9 | (send [_ msg] (swap! msgs conj (dissoc msg :transport))))
10 |
11 | (defn test-transport []
12 | (TestTransport. (atom [])))
13 |
14 | (defn messages [test-transport]
15 | @(:msgs test-transport))
16 |
--------------------------------------------------------------------------------
/test/common/cider/test_ns/first_test_ns.clj:
--------------------------------------------------------------------------------
1 | (ns cider.test-ns.first-test-ns)
2 |
3 | (def some-test-var
4 | "This is a test var used to check eldoc returned for a variable."
5 | 1)
6 |
7 | (defn same-name-testing-function
8 | "Multiple vars with the same name in different ns's. Used to test ns-list-vars-by-name."
9 | []
10 | true)
11 |
--------------------------------------------------------------------------------
/test/common/cider/test_ns/second_test_ns.clj:
--------------------------------------------------------------------------------
1 | (ns cider.test-ns.second-test-ns)
2 |
3 | (defn same-name-testing-function
4 | "Multiple vars with the same name in different ns's. Used to test ns-list-vars-by-name."
5 | []
6 | true)
7 |
--------------------------------------------------------------------------------
/test/common/cider/test_ns/third_test_ns.clj:
--------------------------------------------------------------------------------
1 | (ns cider.test-ns.third-test-ns)
2 |
3 | (defn same-name-testing-function
4 | "Multiple vars with the same name in different ns's. Used to test ns-list-vars-by-name."
5 | []
6 | true)
7 |
--------------------------------------------------------------------------------
/test/common/cider_nrepl/plugin_test.clj:
--------------------------------------------------------------------------------
1 | (ns cider-nrepl.plugin-test
2 | (:require [cider-nrepl.plugin :as plugin]
3 | [cider.nrepl.middleware :refer [cider-middleware]]
4 | [clojure.test :refer :all]
5 | [leiningen.core.main :as lein]))
6 |
7 | (defn- contains-cider-nrepl-dep? [{:keys [dependencies]}]
8 | (boolean (->> dependencies
9 | (some (fn [[id version & _]]
10 | (= 'cider/cider-nrepl id))))))
11 |
12 | (defn- contains-cider-nrepl-middleware? [{{:keys [nrepl-middleware]} :repl-options}]
13 | (= cider-middleware nrepl-middleware))
14 |
15 | (deftest plugin-test
16 | ;; Suppress output of leiningen.core.main/warn
17 | (binding [lein/*info* false]
18 | (with-redefs [lein/leiningen-version (constantly (plugin/minimum-versions :lein))]
19 | (testing "Valid Lein version; valid Clojure version"
20 | (let [project (plugin/middleware '{:dependencies [[org.clojure/clojure "1.10.0"]]})]
21 | (is (contains-cider-nrepl-dep? project))
22 | (is (contains-cider-nrepl-middleware? project))))
23 |
24 | (testing "Valid Lein version; no Clojure version specified"
25 | (let [project (plugin/middleware '{})]
26 | (is (contains-cider-nrepl-dep? project))
27 | (is (contains-cider-nrepl-middleware? project))))
28 |
29 | (testing "Valid Lein version; multiple Clojure versions specified"
30 | (let [project (plugin/middleware `{:dependencies [[org.clojure/clojure "1.5.1"]
31 | [org.clojure/clojure ~(plugin/minimum-versions :clojure)]]})]
32 | (is (contains-cider-nrepl-dep? project))
33 | (is (contains-cider-nrepl-middleware? project))))
34 |
35 | (testing "Valid Lein version; invalid Clojure version"
36 | (let [project (plugin/middleware '{:dependencies [[org.clojure/clojure "1.6.0"]]})]
37 | (is (not (contains-cider-nrepl-dep? project)))
38 | (is (not (contains-cider-nrepl-middleware? project))))))
39 |
40 | (with-redefs [lein/leiningen-version (constantly "2.5.1")]
41 | (testing "Invalid Lein version; valid Clojure version"
42 | (let [project (plugin/middleware '{:dependencies [[org.clojure/clojure "1.10.0"]]})]
43 | (is (not (contains-cider-nrepl-dep? project)))
44 | (is (not (contains-cider-nrepl-middleware? project)))))
45 |
46 | (testing "Invalid Lein version; no Clojure version specified"
47 | (let [project (plugin/middleware '{})]
48 | (is (not (contains-cider-nrepl-dep? project)))
49 | (is (not (contains-cider-nrepl-middleware? project)))))
50 |
51 | (testing "Invalid Lein version; invalid Clojure version"
52 | (let [project (plugin/middleware '{:dependencies [[org.clojure/clojure "1.6.0"]]})]
53 | (is (not (contains-cider-nrepl-dep? project)))
54 | (is (not (contains-cider-nrepl-middleware? project))))))))
55 |
--------------------------------------------------------------------------------
/test/java/cider/nrepl/test/AnotherTestClass.java:
--------------------------------------------------------------------------------
1 | package cider.nrepl.test;
2 |
3 | public class AnotherTestClass {
4 | public AnotherTestClass() {
5 | }
6 |
7 | public static int fnWithSameName(int a, String b, boolean c) {
8 | return 8;
9 | }
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/test/java/cider/nrepl/test/TestClass.java:
--------------------------------------------------------------------------------
1 | package cider.nrepl.test;
2 |
3 | public class TestClass {
4 | public TestClass() {
5 | }
6 |
7 | public int getInt() {
8 | return 3;
9 | }
10 |
11 | public boolean fnWithSameName() {
12 | return true;
13 | }
14 |
15 | private static void doSomething(int a, int b, String c) {
16 | String x = c + a + b;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/test/java/cider/nrepl/test/YetAnotherTest.java:
--------------------------------------------------------------------------------
1 | package cider.nrepl.test;
2 |
3 | import java.util.List;
4 |
5 | public class YetAnotherTest {
6 | public YetAnotherTest() {
7 | }
8 |
9 | public String fnWithSameName(byte[] prim, Object[] things, List generic) {
10 | return "something";
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/test/resources/cider/nrepl/middleware/test_with_throwing_fixtures.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.test-with-throwing-fixtures
2 | (:require
3 | [clojure.test :refer [deftest is use-fixtures]]))
4 |
5 | (use-fixtures :once (fn [t]
6 | (throw (ex-info "I'm an exception inside a fixture!" {:data 42}))
7 | (t)))
8 |
9 | (deftest foo
10 | (is (= 42 (* 21 2))))
11 |
--------------------------------------------------------------------------------
/test/resources/failing_test_ns.clj:
--------------------------------------------------------------------------------
1 | (ns failing-test-ns
2 | (:require [clojure.test :refer :all]))
3 |
4 | (deftest fast-failing-test
5 | (is (= 0 1)))
6 |
7 | (deftest slow-failing-test
8 | (Thread/sleep 1000)
9 | (is (= 0 1)))
10 |
--------------------------------------------------------------------------------
/test/resources/failing_test_ns_2.clj:
--------------------------------------------------------------------------------
1 | (ns failing-test-ns2
2 | (:require [clojure.test :refer :all]))
3 |
4 | (deftest two-clauses
5 | (is (= 0 (do
6 | (Thread/sleep 1000)
7 | 1)))
8 | (is (= 2 3)))
9 |
10 | (deftest uses-are
11 | (are [input expected] (= input (identity expected))
12 | 0 1
13 | 2 3))
14 |
--------------------------------------------------------------------------------
/test/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | %d{yyyy-MM-dd}T%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
6 |
7 |
8 | OFF
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/test/resources/logging.properties:
--------------------------------------------------------------------------------
1 | ############################################################
2 | # Default Logging Configuration File
3 | #
4 | # You can use a different file by specifying a filename
5 | # with the java.util.logging.config.file system property.
6 | # For example java -Djava.util.logging.config.file=myfile
7 | ############################################################
8 |
9 | ############################################################
10 | # Global properties
11 | ############################################################
12 |
13 | # "handlers" specifies a comma separated list of log Handler
14 | # classes. These handlers will be installed during VM startup.
15 | # Note that these classes must be on the system classpath.
16 | # By default we only configure a ConsoleHandler, which will only
17 | # show messages at the INFO and above levels.
18 | handlers=java.util.logging.FileHandler
19 |
20 | # To also add the FileHandler, use the following line instead.
21 | #handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler
22 |
23 | # Default global logging level.
24 | # This specifies which kinds of events are logged across
25 | # all loggers. For any given facility this global level
26 | # can be overriden by a facility specific level
27 | # Note that the ConsoleHandler also has a separate level
28 | # setting to limit messages printed to the console.
29 | .level= ALL
30 |
31 | ############################################################
32 | # Handler specific properties.
33 | # Describes specific configuration info for Handlers.
34 | ############################################################
35 |
36 | # default file output is in user's home directory.
37 | java.util.logging.FileHandler.pattern = %h/java%u.log
38 | java.util.logging.FileHandler.limit = 50000
39 | java.util.logging.FileHandler.count = 1
40 | java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
41 |
42 | # Limit the message that are printed on the console to INFO and above.
43 | java.util.logging.ConsoleHandler.level = INFO
44 | java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
45 |
46 | # Example to customize the SimpleFormatter output format
47 | # to print one-line log message like this:
48 | # : []
49 | #
50 | # java.util.logging.SimpleFormatter.format=%4$s: %5$s [%1$tc]%n
51 |
52 | ############################################################
53 | # Facility specific properties.
54 | # Provides extra control for each logger.
55 | ############################################################
56 |
57 | # For example, set the com.xyz.foo logger to only log SEVERE
58 | # messages:
59 | com.xyz.foo.level = SEVERE
60 |
--------------------------------------------------------------------------------
/test/resources/sum-types-are-cool.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/clojure-emacs/cider-nrepl/44c47adb4c73f817f99d5df5783bb6cef8b0063a/test/resources/sum-types-are-cool.jpg
--------------------------------------------------------------------------------
/test/resources/test.txt:
--------------------------------------------------------------------------------
1 | A test resource file
2 |
--------------------------------------------------------------------------------
/test/resources/unknown.unknown:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/clojure-emacs/cider-nrepl/44c47adb4c73f817f99d5df5783bb6cef8b0063a/test/resources/unknown.unknown
--------------------------------------------------------------------------------
/test/smoketest/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 |
--------------------------------------------------------------------------------
/test/smoketest/project.clj:
--------------------------------------------------------------------------------
1 | (defproject smoketest "0.1.0-SNAPSHOT"
2 | :dependencies [[nrepl "1.0.0"]
3 | [cider/cider-nrepl "RELEASE"]]
4 | :profiles {:1.10 {:dependencies [[org.clojure/clojure "1.10.1"]]}
5 | :1.11 {:dependencies [[org.clojure/clojure "1.11.1"]]}
6 | :1.12 {:dependencies [[org.clojure/clojure "1.12.0-rc1"]]}
7 | :uberjar {:aot :all}}
8 | :main smoketest.core)
9 |
--------------------------------------------------------------------------------
/test/smoketest/src/smoketest/core.clj:
--------------------------------------------------------------------------------
1 | (ns smoketest.core
2 | (:require
3 | [nrepl.core :as nrepl]
4 | [nrepl.server :refer [start-server]]
5 | [clojure.pprint])
6 | (:gen-class))
7 |
8 | ;; The cider-nrepl "smoke test" replicates a small sampling of the
9 | ;; library's unit test coverage executing in an uberjar. The point of
10 | ;; this test is to confirm the cider-nrepl artifact compiles,
11 | ;; installs, and can be used in a standalone jar. Comprehensive test
12 | ;; coverage is not a goal of this smoke test.
13 |
14 |
15 | ;; see cider-nrepl issue #447
16 | (defn nrepl-handler []
17 | (requiring-resolve 'cider.nrepl/cider-nrepl-handler))
18 |
19 | (defn nrepl-server-fixture
20 | "Derived from the cider-nrepl test fixture. Launch the nrepl server,
21 | establish a client session, and call the function f with the client
22 | session as its sole argument."
23 | [f]
24 | (with-open [server (start-server :bind "localhost" :handler (nrepl-handler))
25 | ;; for now binding "localhost" circumvents the bug
26 | ;; https://dev.clojure.org/jira/browse/NREPL-87
27 | transport (nrepl/connect :port (:port server))]
28 | (let [client (nrepl/client transport Long/MAX_VALUE)
29 | session (nrepl/client-session client)]
30 | (f session))))
31 |
32 | (defn message
33 | "Send message to session and return the combined response."
34 | [session msg]
35 | (let [responses (nrepl/message session msg)]
36 | (nrepl/combine-responses responses)))
37 |
38 | ;; Tests are lifted from the unit test coverage and rewritten to
39 | ;; execute without the clojure.test framework. This results in some
40 | ;; repetition but keeps this smoke test simple and lightweight.
41 |
42 | (defn check-version
43 | "Call version middleware and check response."
44 | [session]
45 | ;; This test generates reflection warnings in java9, but passes.
46 | (let [response (message session {:op "cider-version"})
47 | version-map (:cider-version response)]
48 | (and (= #{"done"} (:status response))
49 | (contains? version-map :major)
50 | (contains? version-map :minor)
51 | (contains? version-map :incremental)
52 | (contains? version-map :version-string))))
53 |
54 | (defn check-classpath
55 | "Call classpath middleware and check response."
56 | [session]
57 | (let [response (message session {:op "classpath"})
58 | classpaths (:classpath response)]
59 | (and (= (:status response) #{"done"})
60 | (> (count classpaths) 0)
61 | (every? string? classpaths)
62 | (some? (some #(re-find #".*smoketest-.*standalone\.jar" %) classpaths)))))
63 |
64 | (defn check-ns-path
65 | "Call ns middleware and check response."
66 | [session]
67 | (let [response (message session {:op "ns-path" :ns "cider.nrepl"})
68 | ns-path (:path response)]
69 | (.endsWith ns-path "cider/nrepl.clj")))
70 |
71 |
72 | ;; For simplistic reporting: {"test1" true, "test2" false, ... }
73 |
74 | (def testnames ["check-version"
75 | "check-classpath"
76 | "check-ns-path"])
77 |
78 | (def tests (apply juxt (map (comp resolve symbol) testnames)))
79 |
80 | (defn -main
81 | "Execute all smoke tests and exit 0 (or 1) to signal
82 | success (or failure) to CI."
83 | []
84 | (let [results (nrepl-server-fixture tests)]
85 | (clojure.pprint/pprint (zipmap testnames results))
86 | (shutdown-agents)
87 | (when-not (every? identity results)
88 | (println "smoketest: FAIL")
89 | (System/exit 1)))
90 | (println "smoketest: OK")
91 | (System/exit 0))
92 |
--------------------------------------------------------------------------------
/test/src/cider/nrepl/middleware/debug_integration_test/fn.clj:
--------------------------------------------------------------------------------
1 | (ns cider.nrepl.middleware.debug-integration-test.fn
2 | "Function for debug integration test.
3 |
4 | NOTE: if modifying this file, modify corresponding tests in
5 | debug_integration_test.clj.")
6 |
7 | (defn as-sym
8 | [x]
9 | (cond
10 | (symbol? x) x
11 | (string? x) (if-let [[_ ns sym] (re-matches #"(.+)/(.+)" x)]
12 | (symbol ns sym)
13 | (symbol x))))
14 |
--------------------------------------------------------------------------------