Skip to content
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

Introduce help context #1484

Open
wants to merge 20 commits into
base: main
Choose a base branch
from

Conversation

Niloth-p
Copy link
Collaborator

What does this PR do, and why?

Introduces context to track the current focus.
Uses context to generate relevant footer hints.

Adds missing tests and new tests.

External discussion & connections

  • Discussed in #zulip-terminal in Context Based Help Tips and Adding context to hotkeys
  • Fully fixes #
  • Partially fixes issue Make help hints context-dependent #515
  • Builds upon previous unmerged work in PR #
  • Is a follow-up to work in PR #
  • Requires merge of PR #
  • Merge will enable work on #

How did you test this?

  • Manually - Behavioral changes
  • Manually - Visual changes
  • Adapting existing automated tests
  • Adding automated tests for new behavior (or missing tests)
  • Existing automated tests should already cover this (only a refactor of tested code)

@zulipbot zulipbot added the size: XL [Automatic label added by zulipbot] label Apr 18, 2024
Hotkeys that used to be excluded from general help hints,
can be useful as hints in particular contexts.
- Add a context property.
- Update footer text on context changes,
if a footer event is not already in progress.

Tests added.
Set context to "editor" only if it is
not already set to more specific editor contexts.

Reset context to "" on exiting any editor.

Tests added and updated.
@Niloth-p Niloth-p force-pushed the 1484-introduce-help-context branch from b75f6f5 to 04c2f4f Compare April 23, 2024 19:26
@Niloth-p Niloth-p force-pushed the 1484-introduce-help-context branch from 04c2f4f to 5b38003 Compare April 24, 2024 17:17
Add checks for clashing keys in the same context.

Added new `context` argument.
Generates a new file `hotkeys-by-context.md`.

Refactored all the functions to take any field as an input,
either category / context, by passing newly created variables.

Added FileNotFoundError exception.

Removed `Esc` from `ALL_MESSAGES` to pass linting.
@Niloth-p Niloth-p force-pushed the 1484-introduce-help-context branch from 5b38003 to 0a1909c Compare April 24, 2024 17:34
@Niloth-p Niloth-p marked this pull request as ready for review April 24, 2024 19:18
Copy link
Collaborator Author

@Niloth-p Niloth-p left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a bunch of unfinished work here, and some significant changes to be made. I've mentioned the main ones in the CZO discussion.
I wanted to first ensure that I'm going in the right direction, so I've opened it up for review already.

@@ -220,7 +220,7 @@ class KeyBinding(TypedDict):
'key_context': 'stream_view',
},
'ALL_MESSAGES': {
'keys': ['a', 'esc'],
'keys': ['a'],
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a temporary change. To make the linting tests pass.
As both the Esc key mappings clash in the 'general' context.

I could add an exception instead too, if necessary later.
Or I could move the ALL_MESSAGES command to the "message_view" context instead until that bug is resolved.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was discussed in the stream recently, I believe? :)

Your last suggestion reflects the current reality, so might be the short-term solution, with a comment re the bug/issue/discussion.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know the changes to this file would be pretty difficult to read and verify.
Do I need to break it down into layers?
Tested manually for the following cases:

[x] no hotkeys-by-context.md file
[x] mismatching existing hotkeys-by-context.md file
[x] matching existing hotkeys-by-context.md file

[x] no hotkeys.md file
[x] mismatching existing hotkeys.md file
[x] matching existing hotkeys.md file

[x] no arguments
[x] context argument
[x] fix argument
[x] both arguments

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would definitely help to split this up, since this is multiple aspects:

  • linting in any new ways, or relaxing linting as we move forward
  • adding the new doc generation, if we end up going in that direction

However, as you say it would also be easier to verify, such as if you eg. refactor existing names first.

Copy link
Collaborator

@neiljp neiljp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Niloth-p There's a lot here!

I did give this a manual test, and many parts definitely seem to be working. I mainly moved around areas of the UI, and since some hotkeys (including just ?) effectively show a new random hint, it allows some degree of sampling of them.

I've not looked at the hotkeys assignment doc, since keys.py works fairly well in the first instance, and I'd started reading that before you added the feature :)

I've only left inline comments until prior to the specific context setting. That's partly since I'm wondering if there may be an alternative approach rather than setting it explicitly in many places. For example, you've already set the context depending on a focus path in a few places instead, and for popups this could be a class variable that is accessed by generic popup show code?

One helpful change would be to address the 'cycling' issue sooner rather than later, but even before or part of that we can improve the design slightly. The original design was to indicate that ? is general help, and show a random help next to it, but currently we have the help key right up next to Help and in parentheses rather than square brackets (which we now tend to use). There's also a colon rather than an indication that the general help is distinct from the hint. As an extension specific to this change, it'd be great to show the relevance of the hint - does it work in compose, stream list, selected message, etc. I could tell that it was displaying reasonable hints based on the context, but showing the context itself would be helpful for those less familiar :)

Comment on lines 271 to +281
'SEARCH_STREAMS': {
'keys': ['q'],
'help_text': 'Search Streams',
'key_category': 'searching',
'key_context': 'general',
},
'SEARCH_TOPICS': {
'keys': ['q'],
'help_text': 'Search topics in a stream',
'key_category': 'searching',
'key_context': 'topic_view',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you mentioned this, but there is nuance with these two, since they depend on the state?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, they both would actually belong in a general context, but need to apply a check (based on the state) to determine which one is applicable at the moment.

},
'REPLY_MESSAGE': {
'keys': ['r', 'enter'],
'help_text': 'Reply to the current message',
'key_category': 'msg_actions',
'key_context': 'message_view',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The _view doesn't appear to add much descriptively, or for other context names?

This relates to a message being selected/focused, right?

Copy link
Collaborator Author

@Niloth-p Niloth-p Apr 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True.
I hadn't considered just not using any suffix, was instead trying to choose a good suffix.
Good idea!

Yes, it does. I'll add that as a comment to the HELP_CONTEXTS dict.

},
'CYCLE_COMPOSE_FOCUS': {
'keys': ['tab'],
'help_text': 'Cycle through recipient and content boxes',
'key_category': 'msg_compose',
'key_context': 'write_box',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per another comment, using a user-facing name would be preferable for contexts.

This could be similar to the key_category, eg composing_message? Keeping names easily searchable (distinct from others) could be useful, so the same as key_category could be challenging if we want that.

},
'AUTOCOMPLETE': {
'keys': ['ctrl f'],
'help_text': ('Autocomplete @mentions, #stream_names, :emoji:'
' and topics'),
'key_category': 'msg_compose',
'key_context': 'editor',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this has the same help text, it only applies to the message composition content, though we do support autocomplete in the stream/topic/recipient boxes for composing too - but only in the matching boxes, and no @mentions or #streams with prefixes, and never emoji.

This may come down to my understanding your use of editor, and the overrides or subsets.

@@ -220,7 +220,7 @@ class KeyBinding(TypedDict):
'key_context': 'stream_view',
},
'ALL_MESSAGES': {
'keys': ['a', 'esc'],
'keys': ['a'],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was discussed in the stream recently, I believe? :)

Your last suggestion reflects the current reality, so might be the short-term solution, with a comment re the bug/issue/discussion.

Comment on lines 128 to 133
# Avoid updating repeatedly (then pausing and showing default text)
# This is simple, though doesn't avoid starting one thread for each call
if context_change and self._is_footer_event_running:
return
if text_list == self._w.footer.text:
return
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existing comment doesn't apply to your new code? Though see my other response, since I suspect it could do, depending if you refactor first and how you rewrite.

Comment on lines 149 to 154
@asynch
def _update_context(self, context_value: str) -> None:
self._context = context_value
self.set_footer_text(context_change=True)

context = property(lambda self: self._context, _update_context)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be in a new thread?

Comment on lines +130 to +131
if context_change and self._is_footer_event_running:
return
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea here is that we don't interrupt a running event, ie. timed-duration message, with a context change?

Paraphrasing the logic: if there's no context change (ie. previous to this commit), or a footer event isn't running, we continue with the previous behavior. That seems good, though can we use the new state boolean you added in the previous commit to simplify the existing logic at that point? That would make the previous commit a useful refactor, and this a more clear standalone change that only adds the context_change part.

Comment on lines +134 to +139
if event_running:
view.frame.footer.set_text.assert_not_called()
view.controller.update_screen.assert_not_called()
else:
view.frame.footer.set_text.assert_called_once_with(["some help text"])
view.controller.update_screen.assert_called_once_with()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this split, it's pretty clear that this should be two separate tests.

It's occasionally reasonable to have these conditionals for complex multi-parameter inputs, but you only have a boolean here :)

One alternative is to simplify the mock asserts to be a bool; it depends how important the parameters to the call being asserted are.

Comment on lines 123 to +126
text_list: Optional[List[Any]] = None,
style: str = "footer",
duration: Optional[float] = None,
context_change: Optional[bool] = False,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has become an increasingly complex (threaded!) function, which stores various state in the UI and the view, and has different calling styles depending upon the usage.

After this commit, I believe we'll support calling this with:

  • no parameters -> reset to show random value
  • specific fixed text/style (eg. autocomplete) -> show text
  • specific fixed text/style with a duration -> show text, then later call with no parameters
  • context change -> show different random value?

However, we can't generally mix and match all these parameters, or at least the new one? It's also unclear by the name, that no parameters does a 'reset' to show the default/random value.

For now, the best change would be to ensure using assert(s) that all calls have the correct combination of parameters, and extend it to incorporate context_change in this commit.

For the future this may warrant refactoring to explicitly cover the different uses and state for this.

@neiljp
Copy link
Collaborator

neiljp commented Apr 26, 2024

I know you have lots of ideas and are getting code out well, but I'd suggest being cautious regarding pushing too far in one direction. It can be better to have work in another area to work on in parallel, rather than needing to heavily prune/drop/adjust a single direction if work in that one area takes a different path.

@neiljp neiljp added the PR awaiting update PR has been reviewed & is awaiting update or response to reviewer feedback label Apr 26, 2024
@Niloth-p Niloth-p mentioned this pull request May 9, 2024
1 task
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
PR awaiting update PR has been reviewed & is awaiting update or response to reviewer feedback size: XL [Automatic label added by zulipbot]
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants