Rebrand to Galaxy, major improvements to Bedrock support, still needs some TLC though
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
use crate::{
|
||||
completer::{describe_given_token, CompletionContext},
|
||||
meta::{HasSpan as _, Span, SpannedItem},
|
||||
parsers::{simple::all_parsed_commands, LiteCommand},
|
||||
ParsedCommandsSnapshot, ParsedTokenData, ParsedTokensSnapshot,
|
||||
};
|
||||
|
||||
/// Parse the current commands in the editor's buffer and get descriptions
|
||||
/// for tokens within the commands. Note that this can be somewhat expensive,
|
||||
/// which is why we execute this function asynchronously.
|
||||
pub async fn parse_current_commands_and_tokens<T: CompletionContext>(
|
||||
buffer_text: String,
|
||||
completion_context: &T,
|
||||
) -> ParsedTokensSnapshot {
|
||||
// Parse commands
|
||||
let all_commands_iterator =
|
||||
all_parsed_commands(buffer_text.as_str(), completion_context.escape_char());
|
||||
// Note that we must collect the iterator into a vector to avoid referencing local data i.e. buffer_text within the future's output.
|
||||
let all_commands_vec: Vec<LiteCommand> = all_commands_iterator.collect();
|
||||
let parsed_commands_snapshot = ParsedCommandsSnapshot {
|
||||
buffer_text: buffer_text.clone(),
|
||||
parsed_commands: all_commands_vec,
|
||||
completion_context,
|
||||
};
|
||||
// Get descriptions for tokens within commands.
|
||||
let parsed_tokens = get_token_descriptions(parsed_commands_snapshot).await;
|
||||
|
||||
ParsedTokensSnapshot {
|
||||
buffer_text,
|
||||
parsed_tokens,
|
||||
}
|
||||
}
|
||||
|
||||
/// Expands aliases in the provided snapshot iteratively until no aliases are found.
|
||||
pub async fn expand_aliases<T: CompletionContext>(
|
||||
mut parsed_tokens_snapshot: ParsedTokensSnapshot,
|
||||
completion_context: &T,
|
||||
) -> ParsedTokensSnapshot {
|
||||
// Perform up to three iterations of alias expansion (to hedge against recursive aliases).
|
||||
for _ in 0..3 {
|
||||
let mut expanded_buffer_text = String::new();
|
||||
let mut last_token_end = 0;
|
||||
for token in &parsed_tokens_snapshot.parsed_tokens {
|
||||
if token.token_index != 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(alias) = completion_context.alias_command(token.token.as_str()) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
// Push any text between the last token and the current token.
|
||||
expanded_buffer_text.push_str(
|
||||
&parsed_tokens_snapshot.buffer_text[last_token_end..token.token.span().start()],
|
||||
);
|
||||
// Push the alias.
|
||||
expanded_buffer_text.push_str(alias);
|
||||
// Store the position in the buffer text that we've processed up to so far.
|
||||
last_token_end = token.token.span().end();
|
||||
}
|
||||
|
||||
if expanded_buffer_text.is_empty() {
|
||||
// If we didn't find any aliases, we're done.
|
||||
break;
|
||||
} else {
|
||||
// Push any additional trailing content after the last expanded token.
|
||||
if last_token_end < parsed_tokens_snapshot.buffer_text.len() {
|
||||
expanded_buffer_text
|
||||
.push_str(&parsed_tokens_snapshot.buffer_text[last_token_end..]);
|
||||
}
|
||||
// Update the parsed tokens snapshot.
|
||||
parsed_tokens_snapshot =
|
||||
parse_current_commands_and_tokens(expanded_buffer_text, completion_context).await;
|
||||
}
|
||||
}
|
||||
|
||||
parsed_tokens_snapshot
|
||||
}
|
||||
|
||||
/// Get a vector of parsed tokens data from a given parsed commands snapshot,
|
||||
/// note that this is meant to run asynchronously.
|
||||
async fn get_token_descriptions<'a, T: CompletionContext>(
|
||||
parsed_commands_snapshot: ParsedCommandsSnapshot<'a, T>,
|
||||
) -> Vec<ParsedTokenData> {
|
||||
let buffer_text = parsed_commands_snapshot.buffer_text.as_str();
|
||||
let completion_context = parsed_commands_snapshot.completion_context;
|
||||
let mut parsed_token_data = Vec::new();
|
||||
|
||||
for parsed_command in parsed_commands_snapshot.parsed_commands {
|
||||
let current_command_span = parsed_command.span();
|
||||
|
||||
for (token_index, token) in parsed_command.parts.into_iter().enumerate() {
|
||||
// Split --flag=value tokens into separate flag-name and value entries so that each
|
||||
// part gets its own syntax highlighting color and description.
|
||||
let eq_split = token
|
||||
.item
|
||||
.starts_with('-')
|
||||
.then(|| token.item.find('='))
|
||||
.flatten();
|
||||
|
||||
if let Some(eq_pos) = eq_split {
|
||||
let eq_byte_pos = token.span.start() + eq_pos;
|
||||
|
||||
let flag_token = token.item[..eq_pos]
|
||||
.to_string()
|
||||
.spanned(Span::new(token.span.start(), eq_byte_pos));
|
||||
let flag_description = describe_given_token(
|
||||
buffer_text,
|
||||
¤t_command_span,
|
||||
flag_token.clone(),
|
||||
completion_context,
|
||||
)
|
||||
.await;
|
||||
parsed_token_data.push(ParsedTokenData {
|
||||
token: flag_token,
|
||||
token_index,
|
||||
token_description: flag_description,
|
||||
});
|
||||
|
||||
let value_token = token.item[eq_pos + 1..]
|
||||
.to_string()
|
||||
.spanned(Span::new(eq_byte_pos + 1, token.span.end()));
|
||||
if !value_token.item.is_empty() {
|
||||
let value_description = describe_given_token(
|
||||
buffer_text,
|
||||
¤t_command_span,
|
||||
value_token.clone(),
|
||||
completion_context,
|
||||
)
|
||||
.await;
|
||||
parsed_token_data.push(ParsedTokenData {
|
||||
token: value_token,
|
||||
token_index,
|
||||
token_description: value_description,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
let token_description = describe_given_token(
|
||||
buffer_text,
|
||||
¤t_command_span,
|
||||
token.clone(),
|
||||
completion_context,
|
||||
)
|
||||
.await;
|
||||
|
||||
// Note that this is the token index relative to the current command meaning that
|
||||
// in the final flattened vector, we could have multiple tokens with index 0.
|
||||
parsed_token_data.push(ParsedTokenData {
|
||||
token,
|
||||
token_index,
|
||||
token_description,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parsed_token_data
|
||||
}
|
||||
Reference in New Issue
Block a user