Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

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

Provide ways to open pop ups in a new buffer / split #8748

Closed
jerabaul29 opened this issue Nov 7, 2023 · 6 comments · May be fixed by #9311
Closed

Provide ways to open pop ups in a new buffer / split #8748

jerabaul29 opened this issue Nov 7, 2023 · 6 comments · May be fixed by #9311
Labels
A-command Area: Commands C-enhancement Category: Improvements

Comments

@jerabaul29
Copy link
Contributor

Pop ups are very convenient for small items (a few lines), however I found them a bit inconvenient for large items (for example, a long manual page for a function in numpy or scipy).

Some examples in my workflow when pop ups are not so convenient:

  1. When using pop ups to get access to long documentation that I have to read through in details, I end up spending quite a lot of time reading and moving from inside the pop up itself to read it, which is not very convenient as the possibilities for moving around are much more limited than on a normal buffer: I would much better enjoy to move around in the pop up with the full power of helix commands.

  2. When displaying the documentation for a slightly advanced function through a pop up, I may want to be able to look both, at the same time, at my code and the pop up documentation, as I may need to think a bit, compare my use with the different documentation examples, etc. I may also want to copy paste some lines from the pop up examples to my code and to tweak it in my code. This is not achievable as far as I know with the pop ups as these are now.

So after a bit of use, it feels like the pop ups are not too adapted to display more than a few lines of information: when more information is displayed, such as a long manpage with a lot of explanations and examples, I would really prefer to have the possibility to get the pop up content into a (temporary) buffer for moving around / copying examples to my code, etc.

Do you think / any idea how a good UX could be for that? For example, when inside a pop up, should we add a new pop up mode, with a few "popup mode commands" that would allow to, for example:

  • open the pop up in a new temporary buffer displayed as a full screen buffer ("b" key in pop up mode?)
  • open the pop up in a new temporary buffer displayed as a split (with different shortcuts to open the split on the top, bottom, right, left of the current buffer / split, for example by using the "hjkl" keys in pop up mode?).

Could something like this make sense? I do not think something like this is available at the moment, or am I missing something? :)

@jerabaul29 jerabaul29 added the C-enhancement Category: Improvements label Nov 7, 2023
@jerabaul29
Copy link
Contributor Author

I was looking a bit into the code; it looks like the popup class is available at:

https://github.com/helix-editor/helix/blob/master/helix-term/src/ui/popup.rs

and in particular, the handling of incoming keys is done by:

match key {
// esc or ctrl-c aborts the completion and closes the menu
key!(Esc) | ctrl!('c') => {
let _ = self.contents.handle_event(event, cx);
EventResult::Consumed(Some(close_fn))
}
ctrl!('d') => {
self.scroll(self.size.1 as usize / 2, true);
EventResult::Consumed(None)
}
ctrl!('u') => {
self.scroll(self.size.1 as usize / 2, false);
EventResult::Consumed(None)
}
_ => {
let contents_event_result = self.contents.handle_event(event, cx);
if self.auto_close {
if let EventResult::Ignored(None) = contents_event_result {
return EventResult::Ignored(Some(close_fn));
}
}
contents_event_result
}
}

Then I guess that it would be enough, to implement this, to:

  • add a new ctrl!('SOME_KEY') branch in the match
  • have this match branch:
    • render the self.contents in a new buffer / split (we could have, for example, (i) ctrl-o to open in a new buffer altogether, (ii) ctrl-h to split the current buffer and open below, (iii) crtl-v to split the current buffer and open on the right?)
    • close the current popup

Would this make sense / be feasible as described here? If so, I think the main block for me to implement it is to understand how to perform the actions (i), (ii), (iii). Any pointer to locations in the code that illustrate how to do this? Is this as simple as (a) grabbing the contents field of the Popup struct, then issuing the "right" higher level helix command taking this content as argument?

@jerabaul29

This comment was marked as outdated.

@jerabaul29

This comment was marked as outdated.

@jerabaul29
Copy link
Contributor Author

Actually, it may be easier to create a new buffer and paste the content in it; probably something inspired from:

  • to create a new empty buffer:

fn new_file(
cx: &mut compositor::Context,
_args: &[Cow<str>],
event: PromptEvent,
) -> anyhow::Result<()> {
if event != PromptEvent::Validate {
return Ok(());
}
cx.editor.new_file(Action::Replace);
Ok(())
}

  • to paste the content in it

fn paste_impl(
values: &[String],
doc: &mut Document,
view: &mut View,
action: Paste,
count: usize,
mode: Mode,
) {
if values.is_empty() {
return;
}
let repeat = std::iter::repeat(
// `values` is asserted to have at least one entry above.
values
.last()
.map(|value| Tendril::from(value.repeat(count)))
.unwrap(),
);
// if any of values ends with a line ending, it's linewise paste
let linewise = values
.iter()
.any(|value| get_line_ending_of_str(value).is_some());
// Only compiled once.
static REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"\r\n|\r|\n").unwrap());
let mut values = values
.iter()
.map(|value| REGEX.replace_all(value, doc.line_ending.as_str()))
.map(|value| Tendril::from(value.as_ref().repeat(count)))
.chain(repeat);
let text = doc.text();
let selection = doc.selection(view.id);
let mut offset = 0;
let mut ranges = SmallVec::with_capacity(selection.len());
let mut transaction = Transaction::change_by_selection(text, selection, |range| {
let pos = match (action, linewise) {
// paste linewise before
(Paste::Before, true) => text.line_to_char(text.char_to_line(range.from())),
// paste linewise after
(Paste::After, true) => {
let line = range.line_range(text.slice(..)).1;
text.line_to_char((line + 1).min(text.len_lines()))
}
// paste insert
(Paste::Before, false) => range.from(),
// paste append
(Paste::After, false) => range.to(),
// paste at cursor
(Paste::Cursor, _) => range.cursor(text.slice(..)),
};
let value = values.next();
let value_len = value
.as_ref()
.map(|content| content.chars().count())
.unwrap_or_default();
let anchor = offset + pos;
let new_range = Range::new(anchor, anchor + value_len).with_direction(range.direction());
ranges.push(new_range);
offset += value_len;
(pos, pos, value)
});
if mode == Mode::Normal {
transaction = transaction.with_selection(Selection::new(ranges, selection.primary_index()));
}
doc.apply(&transaction, view.id);
doc.append_changes_to_history(view);
}

need to understand / explore how to set the name of the buffer to doc_NAME instead of scratch.

@jerabaul29
Copy link
Contributor Author

Starting to work on a drag PR, help and comments welcome :) .

@jerabaul29
Copy link
Contributor Author

Actually, I do not think that grabbing the content of a popup to display it in a new buffer is doable at present; I am discussing a bit some ideas of how this could be done in the PR drag #9311 . Any input there welcome :) .

@kirawi kirawi added E-easy Call for participation: Experience needed to fix: Easy / not much E-good-first-issue Call for participation: Issues suitable for new contributors A-command Area: Commands and removed E-easy Call for participation: Experience needed to fix: Easy / not much labels Mar 31, 2024
@pascalkuthe pascalkuthe removed the E-good-first-issue Call for participation: Issues suitable for new contributors label Apr 8, 2024
@helix-editor helix-editor locked and limited conversation to collaborators Apr 8, 2024
@pascalkuthe pascalkuthe converted this issue into discussion #10284 Apr 8, 2024

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
A-command Area: Commands C-enhancement Category: Improvements
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants