-
Notifications
You must be signed in to change notification settings - Fork 291
feat: overhaul delete implementation #5894
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
Conversation
|
Here's what I'm thinking:
If Follow-up PR to make everything consistent:
|
| `delete.branch /bar` deletes the branch `bar` in the | ||
| current project | ||
| delete.force |
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.
Do we still need delete.force, with this PR?
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.
Yeah, it's needed if precondition checks prevent a delete, like if you are trying to delete a stray constructor. I tried to make all of the error messages mention it, not delete, where appropriate.
| 2. bar#cq22mm4sca | ||
| Tip: Use `move.term` or `delete.term` to resolve the | ||
| Tip: Use `move.term` or `delete.term.force` to resolve the |
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.
Oh hm. What happens if you do a regular delete.term on a conflicted name? (And is that the obvious right thing, and why?)
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.
You would have to use delete.term.force to delete a conflicted name, since delete.term has precondition checks including no conflicted name. I think that's the obvious right thing given how delete works like merge and update that have the same preconditions and share a lot of code that takes advantage of them. Ideally there wouldn't be a restriction but in practice I don't think it will matter much because conflicted names aren't really a thing.
| This branch has more than one term with the name `x`. Please | ||
| delete or rename all but one of them, then try the update | ||
| again. | ||
| Sorry, I can't do that right now, because there's more than |
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 a kind of weird outcome... I guess only one of the x might be in the scratch file?
| update when a type exists nested under an alias of itself. | ||
| Please separate them or delete one copy, and then try updating | ||
| again. | ||
| Please separate them or `delete.force` one copy, and then try |
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.
What happens if you just use delete?
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.
You can't use delete to delete something when there's this precondition failure, you have to use delete.force.
|
Thanks—erm for some reason that link doesn't work for me in Safari or Chrome. What file is it? |
|
Regarding this suggestion, Here is the current wording in the PR: And I based this on the existing message for an So, I feel if we change the "delete failed" output message I feel we should change the "update failed" message to match. Otherwise, I think the inconsistency is probably unnecessary and maybe jarring. |
|
Regarding this, I actually don't think it would be better to have a special case that just says "Ok.". For one thing, it's weird to get feedback sometimes but not others. But also, since delete performs suffix matching, it is useful to see the full name of the thing that was deleted, even if only one thing matched. Example: |
|
Regarding the suggestion to handle the "no match" case differently, there's on additional source of short-circuiting: constructor names. In the current PR, every argument is resolved as follows:
So, you can Anyway, I can see the appeal of not failing on (1) but instead recording those for later. However, do we want to do something analogous for (2), i.e. if any name resolves only to constructors, don't fail the whole delete, but rather note that in the output message later? (I think so). And either way: could we push that refactoring to a follow-up PR? (Just in the interest of merging improvements quickly and iterating) |
Did you see the changed "update/upgrade/merge failed" messages in the same comment with the "delete failed" message? I do want to make all four of them consistent. |
Right, I see. The idea here was to give feedback if and only if there was meaningful feedback to give; a helpful signal as to when/whether it's worth reading the message at all. I agree it could be useful to show the expanded name where relevant. If the user fully explicitly gives the name though, and then we do exactly what they, then telling them back exactly what we did is a waste of reading that the user can't even identify until they finish reading it. If deleting by suffix is the primary way we end up using it though, then maybe that explicit case is too rare for it to matter. I had missed that you added capability that in this PR. We can try it!
I think so.
Yes, will you create a ticket for whatever gets pushed though. |
|
Planning on doing these in follow-up:
I don't think we should pursue this one, though:
That pretty much goes against delete UX best practices as far as I understand. Maybe @hojberg could chime in. Basically, we shouldn't minimize or eliminate feedback about what was deleted in a CLI context where the user isn't, like, clicking on an X to delete. It's very important to assure to the user they didn't delete something other than what they expected and build trust in the tool. (Our current implementation fails here, just says "Done." unless you explicitly ask for feedback with a |
I think I understand what you're saying (I won't summarize, because I think you stated it clearly). I would say that verbosity is important exactly when you already don't trust the tool. I'm not sure if we are in that situation currently; though maybe we are. Long term, we're not; knock on wood. Consider: # User was explicit - verbose output is redundant
$ delete-thing foo-bar-baz-v1.2.3
Deleted: foo-bar-baz-v1.2.3 # ← no new info,
# I have to compare characters to determine what happened
# Tool extrapolated - verbose output adds info
$ delete-thing foo
Deleted: foo-bar-baz-v1.2.3
Deleted: foo-test-v0.9.1
$ delete-thing foo
Error: Couldn't find `foo`The "reading fatigue" issue is underrated in CLI design. Every line of output has a cost, it:
Even a message like "Done (deleted exact match)." or something, is better than having to scrutinize the standard output template to determine whether the thing you asked for happened, or if the result was different. As far as: what do we do right now? I'm not sure. If my proposal is hard, let's skip it for now. |


Overview
This PR overhauls the
deleteimplementation to kick you into an update mode if you try to delete a set of things that would leave behind something with a nameless dependency (rather than just say "I can't do that" and print out the dependents that block the delete).The update mode functions exactly like a failed
update. You get put on a branch (with sameupdate-prefix convention, notdelete-) with the delete targets deleted, and all of the transitive dependents of the subset of delete targets that no longer have any name are put into the scratch file, and don't exist in the underlying namespace. So, if you comment them all out, for example, you'll see the slurp output indicate anupdatewould effectively "delete" them all by merging the branch back into its parent. The top of the scratch file says:You can't run
deletewhile in the middle of resolving an update, upgrade, or delete.This implementation essentially relies on a couple healthy-namespace checks that are preconditions of
mergeandupdateas well: you can't have any conflicted names, and your type declarations must all pass all the checks (one name per constructor, each nested underneath its type in the namespace, etc).However, certain situations could previously have been solved with a
delete, such as deleting a stray constructor. So, I've added back a sort of dumb version,delete.force, which just deletes all of the names you give it, without any restrictions. I tried to update various error messages to mentiondelete.forceas necessary, too. I don't expectdelete.forceto be particularly common, but it is useful in transcripts as a way of setting up test scenarios, like a type without a name for one of its constructors.deleteanddelete.typenow delete all of a type's constructors (woohoo). You can't delete a constructor withdeleteordelete.termanymore.I also added a suffix-resolving mechanism to
delete:delete foo.barwill delete the thing calledfoo.bar, or if it doesn't exist, everything with the suffixfoo.bar. If constructors and terms exist that match the suffix, the constructors are ignored. If only constructors exist, you get the "I can't delete constructors" error message. My thinking here was that getting blocked by an error message is annoying, and there's alwaysundoif you deleted more than intended. However, I think this might have been the wrong call, and a "which did you mean?" error message, with numbered args, would be better overall. WDYT?I deleted the
.verbosevariants of delete in favor of just always printing out the list of names that were deleted. I don't think you ever want to just see "Done" without any idea of precisely what was deleted.Test coverage
I added a couple new cases to the main
delete.mdtranscript.