-
Notifications
You must be signed in to change notification settings - Fork 112
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
Prompt user for new requires across language contexts using a single completing read #521
Prompt user for new requires across language contexts using a single completing read #521
Conversation
* Ignores the suggested ns * Always prompts even if one selection * Need a better predicate to limit suggestions
After triggering a require with cljr-slash, calculate a list of all completions from the middleware. This is returned in the form of a nested hash of type => alias-name => namespace to require. However, many cljs, clj namespaces can overlap if referencing cljc, so filter the namespaces into the form of a list of (alias, namespace, types), where types is the set of source file types the require has been observed in. Then further reduce the completion list to only those namespaces with an alias equal to the string prior to the / from cljr-slash. Finally, execute a completing-read on the list of options, and use that to magically require a namespace. Lots of efficiency problems here, as computing the whole index is likely expensive. However, it might be valuable to detect if there is a known option or change the prompt on an unrecognized namespace.
Now that the middleware response can be filtered by a completing read including the type information, resume using cljr--magic-requires-lookup-alias to generate candidates, amended to include source type information. Then use cljr--namespace-completion to select from candidates.
not removing cljr--clj-context-p as it might still be useful for other cases that need to distinguish between source types.
Also stops passing short-alias, as there is no need for an initial prompt.
It's not strictly needed, and while it helped for development it's just a transformation method and the intervening state is not useful.
This is not *quite* right as we are still double prompting here if there is more than one candidate namespace for the given alias.
If we have >1 candidate for selection, it already prompts with a completing read. Now instead of falling back on a `yes-or-no-p` to prompt the user about the require, force a completing read if prompt is enabled and only a single candidate namespace is available.
This should handle adding a include if another file is referring to the namespace by it's full name. As example, typing: (cljs.pprint/) Should autoinsert a require for cljs.pprint using whichever alias the project previously used. However, cljs.pprint will still be the name before the slash, even though the alias is inserted.
Shows how prefixes and namespaces can be shared across a clj and cljs namespace.
Thanks for taking an interest! Most welcome to get some more eyes on the clj{c,s} features! This definitely looks like an improvement to me 👍
Yeah, this will probably get merged as a single feature commit, but we can wait with that until the very end. I'm not sure what policy we have on using |
I'm fine with using These days there's also a built-in destructuring form as a potential alternative for |
Will be reviewing 👍 the idea seems nice to me as the current UX is a bit redundant, yes. As a quick note on commits, as hinted, they are now far too many. But sometimes just one squashed commit leaves a poor/conflated history. If you have it at hand, as the last step after review you might want to leave it at a few commits, e.g. one for refactoring, another for the addition of the feature. Personally I've never used It matters to me because much of my maintenance around clj-refactor/refactor-nrepl is for cljr-slash. Probably many people using clj-refactor use it mostly for cljr-slash as well, and as @expez says it's good that anyone can understand what's going on. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dgtized could you please annotate the GH diff with comments?
I'd like to understand what changed (especially for the changes - the additions are self-explanatory) and why.
At the same time, sometimes the best change is no change, and use additions instead.
For instance this kind of diff seems bound to give problems:
- (list short (list long))))))))
+ (list (list (intern short) (intern long) '()))))))))
You can instead create more functions, and choose which function is being invoked. And unit-test those new functions.
So as hinted over Slack, I'd prefer a defcustom and no visible changes for now. We could promote the feature to the 'default' after a couple months, when everything is polished and time-proven.
@@ -1957,35 +1957,98 @@ following this convention: https://stuartsierra.com/2015/05/10/clojure-namespace | |||
(cljr--call-middleware-sync "namespace-aliases") | |||
parseedn-read-str)) | |||
|
|||
(defun cljr--get-aliases-from-middleware () |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't change names. Even if internal, they can easily have well-known meanings for maintainers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed back, though I suspect will need to instead duplicate and leave the existing cljr--get-aliases-from-middleware,
and add the new behavior into a new function, as I think the custom to enable/disable this behavior will need to change the dispatch from inside cljr--slash
.
(when (and cljr-magic-require-namespaces ; a regex against "" always triggers | ||
(string-match-p (cljr--magic-requires-re) short)) | ||
;; This when-let might seem unnecessary but the regexp match | ||
;; isn't perfect. | ||
(when-let (long (cljr--aget cljr-magic-require-namespaces short)) | ||
(list short (list long)))))))) | ||
(list (list (intern short) (intern long) '())))))))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is ensuring that both the candidates
path above and the path where we construct candidates from cljr-magic-require-namespaces
both return a list of triplets, where each triplet contains the alias, namespace, and the list of contexts it appeared in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They are interned because the output from cljr--get-aliases-from-middleware
are all symbols due to being a filter on the parseedn-read-str
output. It's possible this could be cleaner if we change the type in cljr--get-aliases-from-middleware
.
So it seems like the major concern is just that we want to support the existing behavior with a defcustom flag. It's kind of an invasive change as it's adjusting the whole workflow through I wonder if all the concerns with the refactors could be addressed by splitting this into two PRs. One that simply re-arranges and adds tests without changing the existing behavior but leaves an easier cut-point to insert the new behavior, and then a second after that supports the new behavior behind a defcustom? That might make it easier to review and a safer path? |
1bc7551
to
1e96243
Compare
Changes behavior for
cljr--slash
to prompt from a list of candidate requires, while annotating language contexts those requires come from. Previously incljc
files, typingshort-name/
would prompt if completion should occur from cljs or clj context and then prompt a second time for an appropriate namespace to require and alias. This shortcuts the context prompt by showing the namespaces for that alias that are used in the project, and annotating whether the source is from a clj or cljs file.I believe this solution is preferable to current behavior as often namespaces in cljc files overlap aliases used in both cljs and clj files, yet still defers to the user to determine if the require is appropriate for the current language context. I think this can still be improved when in a
#(:cljs)
context block as we might be able to prefer candidates with the matching context, but it's much nicer than bprompting for context on every / require in a cljc file. However I think some of the context block logic under the previous approach may have been better at detecting the only available require, so that may be a regression. I feel as if the overall workflow allows for more flexibility though if we treat it as:a. Shortcutting if only one namespace matches regardless if it is for cljs, clj, or both
I brought this up in the clojure Slack a couple months back but forgot to add a corresponding issue. I'm happy to add an issue now if that would be the preferable workflow. For now I'm opening this as a draft PR to solicit review. I'm also happy to cleanup the commit history a bit, but thought it would give some context on how I arrived where I did. Finally, I'm having a lot of trouble updating the ecukes/espud tests that interact with the completing-read as they are only asserting the final state, so it's unclear what's failing in between. Any assistance there would be appreciated.
Thanks for your consideration, hope others find this useful!
Before submitting a PR make sure the following things have been done (and denote this
by checking the relevant checkboxes):
cask exec emacs -batch -Q -L . -eval "(progn (setq byte-compile-error-on-warn t) (batch-byte-compile))" clj-refactor.el
)./run-tests.sh
)Thanks!