From: "st0012 (Stan Lo)" Date: 2022-09-27T13:22:14+00:00 Subject: [ruby-core:110107] [Ruby master Feature#18996] Proposal: Introduce new APIs to reline for changing dialog UI colours Issue #18996 has been updated by st0012 (Stan Lo). > It would be better to find the original source of ANSI escape code instead of the Wikipedia article. The 8-color part of ANSI escape code (not the entire standard) were defined in the [ECMA-48 standard](https://www.ecma-international.org/wp-content/uploads/ECMA-48_5th_edition_june_1991.pdf) 's page 62. (This standard later became ISO-6429) In the standard, it uses `[color] display` to refer to foreground colors, and `[color] background` for background colors. However, it's worth noting that in other terminal related documents, like the wiki page I linked above or [this Console Virtual Terminal Sequences manual from Microsoft](https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#text-formatting), `foreground` and `background` are the more common terms. So I think we should stick with `foreground` and `background` as `display` is more ambiguous. **`bg_color` or `background_color`** I'm fine with both the longer `foreground_color` and `background_color` or the short versions like `fg_color` and `bg_color`. ### New Configuration Interface After reading the feedback and doing more research, I want to propose a new configuration interface: ```rb Reline.dialog_config.merge!({ background_color: :black, foreground_color: :white, highlighted_background_color: :white, highlighted_foreground_color: :black, # and in the future, if we want to introduce more styles text_style: [:italic, :underscore], highlighted_text_style: [:bold], }) ``` **Why `dialog_config` instead of `color_config`** When it comes to UI configurations, I think it makes more sense to group the options by the component (dialog) than the type of property (color). For example, when defining CSS we declare a selector's styles (background, font...etc.) in the same block. ``` .dropdown-content��{�� �� position:��absolute; �� background-color:��#f9f9f9; �� min-width:��160px; ����box-shadow:��0px 8px 16px 0px rgba(0,0,0,0.2); �� padding:��12px 16px; �� z-index:��1; } ``` This also means that we don't need to have the component prefix (`dialog_`) in every option. **Item states: `highlighted` vs `selection`** `reline`'s' dialog can have 2 types of usages: 1. Dropdown selection, like `irb`'s autocompletion 2. Content display, like `irb`'s documentation dialog The difference is not well documented but the behavior is controlled by the dialog's [`pointer` attribute](https://github.com/ruby/reline/blob/aeead48f1c/lib/reline/line_editor.rb#L685). If it's assigned to an integer that matches one of the items' index, [the item will be highlighted](https://github.com/ruby/reline/blob/aeead48f1c/lib/reline/line_editor.rb#L745) (scenario 1). If it's assigned with a `nil`, then all contents will be displayed the same way (scenario 2). Given that `reline`'s dialog isn't just for autocompletion dropdown, I think using `selection` as a state name is too narrow. And purpose of `pointer` is to display a specific content with different set of color(s), so I think `highlighted` is a better name here. #### The Current (`v0.3.1`) Implementation's `bg_color` I forgot to mention that in `reline v0.3.1`, there's already a way to configure a dialog's background color by [passing `bg_color` to `DialogRenderInfo`](https://github.com/ruby/reline/blob/v0.3.1/lib/reline.rb#L36). And `irb` is already using it to [set its documentation dialog's background color](https://github.com/ruby/irb/blob/4192683ba27b6c25f709f682136b664cf6d0bc2a/lib/irb/input-method.rb#L410). However, I think it's not the API we should settle with. Let me explain why: To build a `reline` dialog, there are 2 levels of APIs to use. The lowest level is to build a proc that handles the dialog logic (which needs to returns a `DialogRenderInfo`), and use the [`add_dialog_proc` method](https://github.com/ruby/reline/blob/aeead48f1cdffbb0d5ca392ac13a7d5a7b9a3f88/lib/reline.rb#L183-L187) to register it. This is what `irb`'s [documentation dialog](https://github.com/ruby/irb/blob/4192683ba27b6c25f709f682136b664cf6d0bc2a/lib/irb/input-method.rb#L299) uses (the complete [`SHOW_DOC_DIALOG` proc](https://github.com/ruby/irb/blob/4192683ba27b6c25f709f682136b664cf6d0bc2a/lib/irb/input-method.rb#L315-L411)). And the `bg_color` attribute is only accessible at this level. And on top of that, `reline` provides a default dialog proc for autocompletion ([`DEFAULT_DIALOG_PROC_AUTOCOMPLETE`](https://github.com/ruby/reline/blob/aeead48f1cdffbb0d5ca392ac13a7d5a7b9a3f88/lib/reline.rb#L234-L280)). In this case, the user only needs to [generate content candidates in a proc](https://github.com/ruby/irb/blob/4192683ba27b6c25f709f682136b664cf6d0bc2a/lib/irb/completion.rb#L126-L136), and register it with `Reline.completion_proc` ([example from `irb`](https://github.com/ruby/irb/blob/4192683ba27b6c25f709f682136b664cf6d0bc2a/lib/irb/input-method.rb#L284)). And since this approach doesn't build a `DialogRenderInfo` from ground up, it won't be able to configure `bg_color`. So even if we add a `fg_color` to the `DialogRenderInfo` struct, we still won't be able to configure it from `irb`. Therefore, I think the proposed configuration interface is still necessary. ---------------------------------------- Feature #18996: Proposal: Introduce new APIs to reline for changing dialog UI colours https://bugs.ruby-lang.org/issues/18996#change-99365 * Author: st0012 (Stan Lo) * Status: Open * Priority: Normal ---------------------------------------- ### TL;DR I want to add APIs to `reline` for changing its dialog item's colors. The APIs I want to add actually have been merged but becaue: 1. This is a design change 2. The maintainer @aycabta is not available to approve nor reject them I want to raise it here to decide if we should: 1. Drop them 2. Modify them 3. Officiallty accept them ### Background After version `1.4`, `irb` provides autocompletion support, which is a great feature and has increased many developers' productivity significantly. But there's an user-experience issue: the completion items' UI colors (set in `reline`) [are not configurable](https://github.com/ruby/reline/blob/9ab5850444b49aff8e360a84eb1dfa425e96ced1/lib/reline/line_editor.rb#L744-L749). So depending on the user's terminal theme, some may find it hard to use because the background and the text having low-contrast colors, like this: ![](https://user-images.githubusercontent.com/3303032/148653612-e3dff786-1a10-4923-a0eb-3975cae10a7f.png) And if that happens, the user has no way to fix it. This caused users to open issues like: - https://github.com/ruby/irb/issues/351 - https://github.com/ruby/irb/issues/328 for being able to change it. Some users even decided to disable it completely because the colors are unreadable to them. I have also seen people sharingtips for disabling this feature: [example](https://twitter.com/sdogruyol/status/1538512030449254400). So I believe it may be bothering many developers. Personally I really like this feature but the background also bothers me: ![Screenshot 2022-09-07 at 22 55 12](https://user-images.githubusercontent.com/5079556/188990620-5ec7ba0c-97ab-48d6-a51f-fd16315e3631.png) And that's why I want to improve it by making the colors configurable and potentially also by providing simple light/dark themes from `irb`. ### Proposal For the dialog UI, there are 2 element states: `highlighted` and `default`. In `irb`'s case, the selected completion candidate will be `highlighted`, and the rest of options will be `default`. And each state has 2 colors: `foreground (text)` and `background (block)`. This means the `reline` should allow `irb` and/or users to configure: - Default items' foreground color - Default items' background color - Highlighted items' foreground color - Highlighted items' background color That brings us to these APIs: - `Reline.dialog_default_fg_color` - `Reline.dialog_default_bg_color` - `Reline.dialog_highlight_fg_color` - `Reline.dialog_highlight_bg_color` And because `reline` only supports coloring through ANSI sequences, these APIs only has 8 available colors if we exclude their bright variants: - Black - Red - Green - Yellow - Blue - Magenta - Cyan - White Given the limited options and also to prevent users from entering non-color ANSI sequences, these APIs only take color names directly: - :black - :red - :green - :yellow - :blue - :magenta - :cyan - :white Example: ```rb Reline.dialog_default_bg_color = :black puts Reline.dialog_default_bg_color_sequence #=> 40 Reline.dialog_default_fg_color = :white puts Reline.dialog_default_fg_color_sequence #=> 37 Reline.dialog_highlight_bg_color = :blue puts Reline.dialog_highlight_bg_color_sequence #=> 34 Reline.dialog_highlight_fg_color = :black puts Reline.dialog_highlight_fg_color_sequence #=> 30 ``` I have made a [proof of concept PR](https://github.com/ruby/irb/pull/380) on `irb` to show what these APIs can achieve if they or similar ones are adopted. #### Related PRs The related changes are made through multiple PRs: - [Initial APIs PR by @pocari](https://github.com/ruby/reline/pull/413) - [PR to improve the APIs and make them safer to use](https://github.com/ruby/reline/pull/454) - [PR to rename the APIs](https://github.com/ruby/reline/pull/456) #### Other Thoughts This is more of a concern on the `irb` part, but to make the UI looks comfortable, I think it's better to follow these conditions: 1. An item's foreground and background colors should have high contrast with each other so the texts (foreground) are readable. 2. For the `highlighted` item, its background color should be easily distinguishable from the rest of `default` items. 3. When using dark terminal themes, the `default` items' background is better to be dark as well. -- https://bugs.ruby-lang.org/ Unsubscribe: