Description
This is the companion feature issue for #525 as requested.
Currently if a cljr-slash
magic require is triggered in a project with mixed cljs, cljc and clj files, cljr-slash
first prompts for the language context and then only allows selection of namespace aliases from cases that were previously seen in that context.
As example, in scratch.cljc
if I type gl/
And if I select cljs
I am prompted to select either thi.ng.geom.line
or thi.ng.geom.gl.core
, which then adds the selected namespace:
In this case, both thi.ng.geom.line
& thi.ng.geom.gl.core
are cljc libraries and are applicable in both a clj and cljs context. However for my example project, thi.ng.geom.gl.core
has only been used in a cljs context previously, so the middleware incorrectly believes that alias is only applicable in a cljs context.
There are several problems above, but I think think the two most critical is that cljr-slash
incorrectly assumes that namespaces previously encountered in one context should only be presented in a matching context. The second and overlapping interface problem is that it prompts twice, and doesn't suggest all the available choices for the user to decide in a single prompt.
In #525, with the defcustom cljr-magic-require-prompts-includes-context
enabled, the behavior is:
This is a single prompt, and allows the user to make their own determination if the other available aliases are applicable in the current context. This functionality is complete in #525 without requiring any changes to the middleware.
Potential Future Improvements
Force a prompt where language context does matter
In #525 if only one namespace usage matches an alias, regardless of the language context, it will immediately insert that without prompting. If we had more reliable detection of language context, or even if the current file did not match any of the language contexts the alias had previously been seen in, then it could prompt the user to verify the new require makes sense before inserting a new require.
Simplifying middleware response to reduce elisp code and allow more fine grained responses in the future
#525 converts the middleware responses from an EDN format like:
{:clj {t [clojure.test] set [clojure.set] alias [namespace]}
:cljs {t [cljs.test] set [clojure.set] alias2 [namespace2}}
Into a list of lists like:
'(((alias namespace (:clj))
(alias2 namespace2 (:cljs))
(set clojure.set (:clj :cljs))
(t clojure.test (:clj))
(t cljs.test (:cljs))
What I would propose is that the middleware should instead return something like:
[{alias: "alias" libspec: "[namespace :as alias]" context: #{:clj}}
{alias: "alias2" libspec: "[namespace2 :as alias]" context: #{:cljs}}
{alias: "set", libspec: "[clojure.set :as set]" context: #{:clj :cljs}}
{alias: "t", libspec: "[clojure.test :as t]" context: #{:clj}}
{alias: "t", libspec: "[cljs.test :as t]" context: #{:cljs}}]
This would reduce the amount of elisp code to convert formats, and allow for future extensions. By listing exact libspecs, it allows the completing-read to return a libspec typed in by the user manually. It also allows suggesting libspecs for an alias with an :as-alias
, :refer
or :include-macros
. A good example would be to promp with the following libspec in a CLJS file: [clojure.test :as t :refer [deftest is] :include-macros true]
. This could also be extended to include a suggested ranking order, or another field for inserting matching :import
fields, if we can reliably associate requires aliases to imports.
Automatically Suggest Reader Conditionals for Alias in CLJC file
Occasionally in a CLJC file, duplicate aliases are used in a reader conditional so that multiple libraries with identical functions can be used under a single namespace alias. For those cases, the middleware could respond something like:
[{alias: "priority"
libspec: "#?(:clj [clojure.data.priority-map :as priority] :cljs [tailrecursion.priority-map :as priority])"
context: #{:cljc}}
{alias: "priority" libspec: "[tailrecursion.priority-map :as priority]" context: #{:cljs}}
{alias: "priority" libspec: "[clojure.data.priority-map :as priority]" context: #{:clj}}]
Critically, it should only suggest a reader conditional if an existing libspec in the project uses a reader conditional to dispatch between different namespaces under a single alias. The latter two examples would only show up if there were clj or cljs files using that libspec in the project. This might be one of the few cases where a matching alias should be excluded from the prompt if the context does not match, as I don't think you can use a reader conditional in a cljs or clj file, and in those cases we should prefer the context specific namespace.