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

Live feedback in prompt #3050

Open
A-Walrus opened this issue Jul 12, 2022 · 2 comments
Open

Live feedback in prompt #3050

A-Walrus opened this issue Jul 12, 2022 · 2 comments
Labels
A-helix-term Area: Helix term improvements C-enhancement Category: Improvements

Comments

@A-Walrus
Copy link
Contributor

Describe your feature request

When using a commands, such as select:, search:, :theme, :set-option, etc... some inputs are invalid (invalid regex, theme/option that doesn't exist).
It would be nice to have visual feedback while typing that indicates that what you are typing is invalid, for example marking your typed text or the prompt in the error color.

Kakoune for example makes the select: text red on invalid regex while you are still typing, which can be more useful than feedback only after you submit the command.

I feel like many commands could benefit from this sort of live feedback, but mostly the regex ones.
image

@A-Walrus A-Walrus added the C-enhancement Category: Improvements label Jul 12, 2022
@the-mikedavis the-mikedavis added the A-helix-term Area: Helix term improvements label Jul 12, 2022
@the-mikedavis
Copy link
Member

#7738 improved on this a little: we show tree-sitter-regex error highlighting in the prompt. We could improve this further by validating the regex with the regex-syntax crate which we already depend on transitively: we can merge highlight ranges for syntax error spans into the prompt line's highlighting. Maybe we could also show the syntax error message in the doc popup too.

@kirawi
Copy link
Member

kirawi commented Apr 22, 2024

This can be done in

pub fn raw_regex_prompt(
cx: &mut crate::commands::Context,
prompt: std::borrow::Cow<'static, str>,
history_register: Option<char>,
completion_fn: impl FnMut(&Editor, &str) -> Vec<prompt::Completion> + 'static,
fun: impl Fn(&mut crate::compositor::Context, rope::Regex, &str, PromptEvent) + 'static,
) {
let (view, doc) = current!(cx.editor);
let doc_id = view.doc;
let snapshot = doc.selection(view.id).clone();
let offset_snapshot = view.offset;
let config = cx.editor.config();
let mut prompt = Prompt::new(
prompt,
history_register,
completion_fn,
move |cx: &mut crate::compositor::Context, input: &str, event: PromptEvent| {
match event {
PromptEvent::Abort => {
let (view, doc) = current!(cx.editor);
doc.set_selection(view.id, snapshot.clone());
view.offset = offset_snapshot;
}
PromptEvent::Update | PromptEvent::Validate => {
// skip empty input
if input.is_empty() {
return;
}
let case_insensitive = if config.search.smart_case {
!input.chars().any(char::is_uppercase)
} else {
false
};
match rope::RegexBuilder::new()
.syntax(
rope::Config::new()
.case_insensitive(case_insensitive)
.multi_line(true),
)
.build(input)
{
Ok(regex) => {
let (view, doc) = current!(cx.editor);
// revert state to what it was before the last update
doc.set_selection(view.id, snapshot.clone());
if event == PromptEvent::Validate {
// Equivalent to push_jump to store selection just before jump
view.jumps.push((doc_id, snapshot.clone()));
}
fun(cx, regex, input, event);
let (view, doc) = current!(cx.editor);
view.ensure_cursor_in_view(doc, config.scrolloff);
}
Err(err) => {
let (view, doc) = current!(cx.editor);
doc.set_selection(view.id, snapshot.clone());
view.offset = offset_snapshot;
if event == PromptEvent::Validate {
let callback = async move {
let call: job::Callback = Callback::EditorCompositor(Box::new(
move |_editor: &mut Editor, compositor: &mut Compositor| {
let contents = Text::new(format!("{}", err));
let size = compositor.size();
let popup = Popup::new("invalid-regex", contents)
.position(Some(helix_core::Position::new(
size.height as usize - 2, // 2 = statusline + commandline
0,
)))
.auto_close(true);
compositor.replace_or_push("invalid-regex", popup);
},
));
Ok(call)
};
cx.jobs.callback(callback);
}
}
}
}
}
},
)
.with_language("regex", std::sync::Arc::clone(&cx.editor.syn_loader));
// Calculate initial completion
prompt.recalculate_completion(cx.editor);
// prompt
cx.push_layer(Box::new(prompt));
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-helix-term Area: Helix term improvements C-enhancement Category: Improvements
Projects
None yet
Development

No branches or pull requests

3 participants