├── .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 | [![CircleCI](https://img.shields.io/circleci/build/github/clojure-emacs/cider-nrepl/master.svg)](https://circleci.com/gh/clojure-emacs/cider-nrepl/tree/master) 2 | [![Coverage](https://codecov.io/gh/clojure-emacs/cider-nrepl/branch/master/graph/badge.svg)](https://codecov.io/gh/clojure-emacs/cider-nrepl/) 3 | [![Clojars Project](https://img.shields.io/clojars/v/cider/cider-nrepl.svg)](https://clojars.org/cider/cider-nrepl) 4 | [![cljdoc badge](https://cljdoc.org/badge/cider/cider-nrepl)](https://cljdoc.org/d/cider/cider-nrepl/CURRENT) 5 | [![Downloads](https://img.shields.io/clojars/dt/cider/cider-nrepl?color=cornflowerblue)](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 | --------------------------------------------------------------------------------