-
Notifications
You must be signed in to change notification settings - Fork 1.8k
feat: user choice group api #19828
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
Open
discord9
wants to merge
6
commits into
rust-lang:master
Choose a base branch
from
discord9:interact_select
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
feat: user choice group api #19828
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
3fea861
feat: add user_choice_group
discord9 443b292
todo: add in handle_code_action to global snapshot
discord9 3d5b46d
todo: build&apply source change
discord9 71c8a09
die horribly with null request id
discord9 d4cf386
chore: fix ci
discord9 fc9f9bb
chore: clippy
discord9 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,8 @@ | |
//! | ||
//! It can be viewed as a dual for `Change`. | ||
|
||
use std::collections::VecDeque; | ||
use std::sync::{Arc, Mutex}; | ||
use std::{collections::hash_map::Entry, fmt, iter, mem}; | ||
|
||
use crate::text_edit::{TextEdit, TextEditBuilder}; | ||
|
@@ -557,3 +559,159 @@ impl PlaceSnippet { | |
} | ||
} | ||
} | ||
|
||
/// a function that takes a `SourceChangeBuilder` and a slice of indices | ||
/// which represent the indices of the choices made by the user | ||
/// which is the choice being made, each one from corresponding choice list in `Assists::add_choices` | ||
pub type ChoiceCallback = dyn FnOnce(&mut SourceChangeBuilder, &[usize]) + Send + 'static; | ||
|
||
/// Represents a group of choices offered to the user(Using ShowMessageRequest), along with a callback | ||
/// to be executed based on the user's selection. | ||
/// | ||
/// This is typically used in scenarios like "assists" or "quick fixes" where | ||
/// the user needs to pick from several options to proceed with a source code change. | ||
#[derive(Clone)] | ||
pub struct UserChoiceGroup { | ||
/// A list of choice groups. Each inner tuple's first string is title, second vector represents a set of options | ||
/// from which the user can make one selection. | ||
/// For example, `choice_options[0]` might be `["Question 1", ["Option A", "Option B"]]` and | ||
/// `choices[1]` might be `["Question 2", ["Setting X", "Setting Y"]]`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't this be |
||
choice_options: Vec<UserChoice>, | ||
/// The callback function to be invoked with the user's selections. | ||
/// The `&[usize]` argument to the callback will contain the indices | ||
/// of the choices made by the user, corresponding to each group in `choice_options`. | ||
callback: Arc<Mutex<Option<Box<ChoiceCallback>>>>, | ||
/// The current choices made by the user, represented as a vector of indices. | ||
cur_choices: Vec<usize>, | ||
/// The file ID associated with the choices. Used for construct SourceChangeBuilder. | ||
/// This is typically the file where the changes will be applied. | ||
file: FileId, | ||
} | ||
|
||
#[derive(Debug, Clone)] | ||
pub struct UserChoice { | ||
pub title: String, | ||
pub actions: Vec<String>, | ||
} | ||
|
||
impl UserChoice { | ||
pub fn new(title: String, actions: Vec<String>) -> Self { | ||
Self { title, actions } | ||
} | ||
} | ||
|
||
impl std::fmt::Debug for UserChoiceGroup { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
f.debug_struct("UserChoiceGroup") | ||
.field("choice_options", &self.choice_options) | ||
.field("callback", &"<ChoiceCallback>") | ||
.field("cur_choices", &self.cur_choices) | ||
.finish() | ||
} | ||
} | ||
|
||
impl UserChoiceGroup { | ||
/// Creates a new `UserChoiceGroup`. | ||
/// | ||
/// # Arguments | ||
/// | ||
/// * `choice_options`: A vector of `UserChoice` objects representing the choices | ||
/// * `callback`: A function that will be called with the indices of the | ||
/// user's selections after they make their choices. | ||
/// | ||
pub fn new( | ||
choice_options: Vec<UserChoice>, | ||
callback: impl FnOnce(&mut SourceChangeBuilder, &[usize]) + Send + 'static, | ||
file: FileId, | ||
) -> Self { | ||
Self { | ||
cur_choices: vec![], | ||
choice_options, | ||
callback: Arc::new(Mutex::new(Some(Box::new(callback)))), | ||
file, | ||
} | ||
} | ||
|
||
/// Returns (`idx`, `title`, `choices`) of the current question. | ||
/// | ||
pub fn get_cur_question(&self) -> Option<(usize, &UserChoice)> { | ||
if self.cur_choices.len() < self.choice_options.len() { | ||
let idx = self.cur_choices.len(); | ||
let user_choice = &self.choice_options[idx]; | ||
Some((idx, user_choice)) | ||
} else { | ||
None | ||
} | ||
} | ||
|
||
/// Whether the user has finished making their choices. | ||
pub fn is_done_asking(&self) -> bool { | ||
self.cur_choices.len() == self.choice_options.len() | ||
} | ||
|
||
/// Make the idx-th choice in the group. | ||
/// `choice` is the index of the choice in the group(0-based). | ||
/// This function will be called when the user makes a choice. | ||
pub fn make_choice(&mut self, question_idx: usize, choice: usize) -> Result<(), String> { | ||
if question_idx < self.choice_options.len() && question_idx == self.cur_choices.len() { | ||
self.cur_choices.push(choice); | ||
} else { | ||
return Err("Invalid index for choice group".to_owned()); | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
/// Finalizes the choices made by the user and invokes the callback. | ||
/// This function should be called when the user has finished making their choices. | ||
pub fn finish(self, builder: &mut SourceChangeBuilder) { | ||
let mut callback = self.callback.lock().unwrap(); | ||
let callback = callback.take().expect("Callback already"); | ||
callback(builder, &self.cur_choices); | ||
} | ||
|
||
pub fn file_id(&self) -> FileId { | ||
self.file | ||
} | ||
} | ||
|
||
/// A handler for managing user choices in a queue. | ||
#[derive(Debug, Default)] | ||
pub struct UserChoiceHandler { | ||
/// If multiple choice group are made, we will queue them up and ask the user | ||
/// one by one. | ||
queue: VecDeque<UserChoiceGroup>, | ||
/// Indicates if the first choice group in the queue is being processed. Prevent send requests repeatedly. | ||
is_awaiting: bool, | ||
} | ||
|
||
impl UserChoiceHandler { | ||
/// Creates a new `UserChoiceHandler`. | ||
pub fn new() -> Self { | ||
Self::default() | ||
} | ||
|
||
/// Adds a new `UserChoiceGroup` to the queue. | ||
pub fn add_choice_group(&mut self, group: UserChoiceGroup) { | ||
self.queue.push_back(group); | ||
} | ||
|
||
pub fn first_mut_choice_group(&mut self) -> Option<&mut UserChoiceGroup> { | ||
self.queue.front_mut() | ||
} | ||
|
||
pub fn pop_choice_group(&mut self) -> Option<UserChoiceGroup> { | ||
self.set_awaiting(false); | ||
self.queue.pop_front() | ||
} | ||
|
||
/// Whether awaiting for sent request's response. | ||
pub fn is_awaiting(&self) -> bool { | ||
self.is_awaiting | ||
} | ||
|
||
/// Sets the awaiting state. | ||
pub fn set_awaiting(&mut self, awaiting: bool) { | ||
self.is_awaiting = awaiting; | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
I once again got a bit confused with the distinction between a multiple choice question and many consecutive questions. Surely the problem is with me, but a clarification never hurts. Thanks.
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.
yes that's confusing, I should come up with a better name for them, because this api intended to provide multiple consecutive questions with multiple choices, it's a bit of confusing.