rm-lisa 0.3.2

A logging library for rem-verse, with support for inputs, tasks, and more.
Documentation
//! An 'autocomplete' provider, providing 'tab completion' for input providers
//! that support it.
//!
//! Although lisa doesn't provide any auto input providers itself it provides
//! the common trait that all folks can use. You should be your own
//! autocomplete solution that feels right to you.
//!
//! However, your autocomplete provider should be fast as if provided it can
//! delay user input from rendering which may cause a suboptimal user
//! experience.

/// Something that can provide auto-complete results.
///
/// Lisa has built this interface in such a way to make it easy to write
/// relatively stateless, if not fully ZST auto complete providers. The
/// current input provider should store the 'auto-complete' state, along with
/// determining when to call the autocomplete provider.
///
/// It should also be important to note that not all input mechanisms may
/// support an autocomplete system (e.g. if you accept HTTP API calls in,
/// you may not need autocomplete at all).
///
/// Please consult your input providers documentation for more information
/// about how it does, or does not use autocomplete.
///
/// Your autocomplete provider should be fast as if provided it can
/// delay user input from rendering which may cause a suboptimal user
/// experience.
pub trait AutocompleteProvider: Send + Sync {
	/// Whether or not this autocomplete provider can actually provide
	/// autocompletes.
	#[must_use]
	fn can_autocomplete(&self) -> bool;

	/// Get the characters to display after a users current input as a
	/// suggestion.
	///
	/// This should return only the _net new_ characters to add to
	/// `current_input` to make a full command. While this value should be cached
	/// by the display provider, it is dependent on which display provider you
	/// are using. Even in a cached world though this function should return
	/// _fast_ as it will impact the display rendering time when called.
	#[must_use]
	fn get_displayable_suggestion(&self, current_input: String) -> Option<String>;

	/// Get the characters to add to an input when a user hits 'tab'.
	///
	/// This should return the _net new_ characters to add to `input` to complete
	/// the completion.
	///
	/// The 'last completion index' is just a counter that goes up to keep track
	/// of which particular completion to complete too. If there isn't another
	/// completion for the index, you should wrap around and return the next
	/// completion.
	#[must_use]
	fn get_tab_completion(
		&self,
		last_completion_index: Option<usize>,
		input: &str,
	) -> Option<String>;
}

#[cfg(test)]
pub mod test_helpers {
	use super::*;

	/// A manual list of suggestions.
	///
	/// Used to do very basic autocomplete suggestions in tests.
	pub struct AutocompleteSuggestionList(pub Vec<String>);

	impl AutocompleteProvider for AutocompleteSuggestionList {
		fn can_autocomplete(&self) -> bool {
			true
		}

		fn get_displayable_suggestion(&self, current_input: String) -> Option<String> {
			self.get_tab_completion(None, &current_input)
		}

		fn get_tab_completion(
			&self,
			last_completion_index: Option<usize>,
			input: &str,
		) -> Option<String> {
			// We never have conflicting suggestions, so we always have only one completion. You should
			// probably NOT do this unless all your commands truly are unique from the very first character.
			let to_ignore_count = last_completion_index.map(|val| val + 1).unwrap_or_default();
			let mut ignored = 0_usize;
			for suggestion in &self.0 {
				if input.len() >= suggestion.len() {
					continue;
				}

				if suggestion.starts_with(input) {
					if ignored >= to_ignore_count {
						return Some(suggestion[input.len()..].to_owned());
					} else {
						ignored += 1;
					}
				}
			}

			None
		}
	}
}