-
Notifications
You must be signed in to change notification settings - Fork 69
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement new middleware op: suggest-libspecs
#384
Comments
Part of #384 Can be tried out with: ``` (thread-first "cljr-suggest-libspecs" (cljr--create-msg "lib-prefix" "s" "language-context" "cljc" "preferred-aliases" (prin1-to-string cljr-magic-require-namespaces)) (cljr--call-middleware-sync "suggestions") parseedn-read-str) ```
Thank you for carefully going through all this and writing it up. I think overall the API spec makes a lot of sense in terms of inputs and the shape of the outputs. I'm also excited about the potential that future recommendations could handle refers, include-macros and reader conditionals more easily. I have a few comments and concerns though:
Even without the overlap with the clj-refactor magic namespaces feature, I think it would be helpful to a consider a few cases with duplicate alias namespace usage like:
I don't think we need to enumerate all of them in the detailed way you have above, but I'm think it would be helpful to understand what you think should be returned in a few of those cases to ensure we are understanding each other. Thanks again for carefully writing this up. While I see that you closed the existing PR, I think the majority of that code should support using this new API as the input. Once we have discussed some of these issues above (in particular question 4), I'll take a look at trying to use that new API with the old code. |
For point
|
^String language-context ;; "clj" "cljs" or "cljc" representing the filename extension (or a user choice for edge cases e.g. it's a buffer/repl without a filename) |
i.e. it's compatible with the use case you describe. language-context
will simply mean that, "language context". refactor-nrepl won't care how is it gathered.
- If you hit cljr-slash within
#?(:clj |)
, the language-context (to be sent from clj-refactor.el) would be clj. - If you hit cljr-slash within
#?(:cljs |)
, the language-context (to be sent from clj-refactor.el) would be cljs. - If you hit cljr-slash within
#?(|)
(which should be a quite extreme edge case), the language-context (to be sent from clj-refactor.el) would be cljc. - If you hit cljr-slash outside a reader conditional in a .clj file, the language-context (to be sent from clj-refactor.el) would be clj.
- If you hit cljr-slash outside a reader conditional in a .cljs file, the language-context (to be sent from clj-refactor.el) would be cljs.
- If you hit cljr-slash outside a reader conditional in a .cljc file, the language-context (to be sent from clj-refactor.el) would be cljc.
For point 3
Yes, we won't place artificial limitations, and the middleware can always be improved iteratively.
For point 4
I see you mention multiple return values right near the end for multiple matching aliases, but I'm not seeing any of the 7 examples listed showing a case where a project already contains multiple namespaces mapping to one one alias?
An easy case would be when I type foo/
and the project namespaces contain [a :as foo]
and [b :as foo]
.
Then the middleware suggests both choices.
I think a worst case of this might be something like [...]
I'll check it out carefully at some point this week. Either way, the final feature will have all these stories as unit tests (in table form via are
- no yucky user stories :) ). Which is to mean, you'll be able to check them out and raise concerns. Such tables tend to be great for iteration.
I think it would be helpful to a consider a few cases with duplicate alias namespace usage like:
I will in the mentioned table. Thanks!
I think the majority of that code should support using this new API as the input.
Would be happy about that 🍻
Thanks for the careful response, I look forward to further details on point 4 when you get the time. I wanted to leave one more comment about point 2 though, as I think there may be some nuance that was lost. If we only send the current context and ignore if the filetype is cljc vs clj or cljs, I believe it will make it harder to recommend a require with an appropriate language context block. More concretely if operating in a cljc file, and the user types something like My concern is, if the language context only reports the current active context, how can the middleware differentiate between a top level language context or a nested one? That was why I was inclined to send |
Alright, got it. Thanks much! Probably a good API would be to accept these two:
LMK if it sounds good to you. Since the middleware is just a POC I won't immediately add this refinement (it will be avaliable within a couple weeks). You are free to send these two keys in addition to the now-legacy |
Thanks, that makes sense to me. I'll take a look at re-implementing the completing-read prompt using this API later this week. |
@dgtized : we now have refactor-nrepl 3.7.0 / clj-refactor 3.7.0 (to be visible in MELPA soon). Would be happy to hear how it works. After that and a few refinements (like ensuring a stable sort order for the suggestions) we can change the |
https://github.com/clojure-emacs/refactor-nrepl/tree/v3.5.4#namespace-aliases is currently used for cljr-slash. As the op name says, it returns the namespaces aliases contained in the current project.
However, a more sophisticated (and context-aware, for .cljc files)
cljr-slash
needs more than simply a project map. We want to compute suggestions, based on logic, besides from project data.The best place to perform this logic is refactor-nrepl (vs. clj-refactor), so we'd need a new piece of middleware. This prevents
namespace-aliases
from being bloated and possibly inflicting breaking changes.As an additional benefit, the middleware responses for cljr-slash will be much smaller (just the useful info vs. a huge map, 99% of which will be unused).
The
suggest-libspecs
middleware opsuggest-libspecs takes as inputs:
io
, representing what the user just typedio/
, triggering cljr-slash)cljr-magic-require-namespaces
defcustomcljr-magic-require-namespaces
clj-refactor.el#530out of those inputs, and of project analysis, it decides which libspec(s) can be suggested to the user.
The output format is as follows:
i.e. it is a simple sequence of choices, ordered by inferred likelihood of choice.
There is no need for metadata e.g.
{:context :clj}
, since the middleware already returns choices that are valid for the provided inputs.Choices might be expressed as reader conditionals:
["[#?(:clj foo.clj :cljs foo.cljs) :as alias]"]
...such reader conditionals would reflect the already-used reader conditionals from a given project, or be synthetically built when it makes sense.
.choices can include
:refer
clauses in a couple cases:other than that, a generalized inclusion of
:refer
s is not foreseen....and similarly,
:include-macros true
clauses can be included.When nothing can be suggested, an empty sequence
[]
will be returned.Suggestions are suject to caching.
Example stories
1
2
3
4
5
6
7
8
Other test cases found in the GH thread below.
General guidelines for suggestion logic
Example stories
(in6
we cannot so aggressively guess what is meant; in7
the user has already implicitly expressed his intent by using certain customs in other existing namespace files)The text was updated successfully, but these errors were encountered: