use std::collections::BTreeSet;
use egui::{Key, KeyboardShortcut, Modifiers};
use crate::app::{
actions::{EditorAction, GraphAction},
editors::spans::{ActiveInteraction, SpanEditor},
};
use super::NodeIndexType;
fn exactly_one_segmentation_or_token_selected(editor: &SpanEditor) -> bool {
editor
.selected_nodes
.iter()
.filter_map(|node_name| editor.node_index_by_name.get(node_name))
.filter(|node| {
matches!(node, NodeIndexType::Token { .. })
|| matches!(node, NodeIndexType::Segmentation { .. })
})
.count()
== 1
}
fn exactly_one_non_token_selected(editor: &SpanEditor) -> bool {
editor
.selected_nodes
.iter()
.filter_map(|node_name| editor.node_index_by_name.get(node_name))
.filter(|node| !matches!(node, NodeIndexType::Token { .. }))
.count()
== 1
}
pub(super) struct AddSpanAction;
impl EditorAction for AddSpanAction {
type EditorImpl = SpanEditor;
fn label() -> &'static str {
"Add span for selected tokens"
}
fn perform(editor: &mut Self::EditorImpl) {
editor.active_interaction = Some(ActiveInteraction::AddingNewSpan {
namespace: String::default(),
name: String::default(),
is_segmentation: false,
});
}
fn enabled(editor: &Self::EditorImpl) -> bool {
let selected_base_token_idx = editor.selected_base_token_indices();
!selected_base_token_idx.is_empty()
}
fn shortcut() -> Option<egui::KeyboardShortcut> {
Some(KeyboardShortcut::new(Modifiers::NONE, Key::Insert))
}
}
pub(super) struct AddBaseTokenBefore;
impl EditorAction for AddBaseTokenBefore {
type EditorImpl = SpanEditor;
fn label() -> &'static str {
"Add base token before selection"
}
fn perform(editor: &mut Self::EditorImpl) {
let selected_base_token_idx = editor.selected_base_token_indices();
if let Some(smallest_idx) = selected_base_token_idx.first()
&& let Some(t) = editor.token.get(*smallest_idx)
{
editor
.pending_graph_actions
.push(GraphAction::AddBaseTokenBefore {
reference_node: t.node_name.clone(),
parent_name: editor.parent_name.clone(),
});
}
}
fn enabled(editor: &Self::EditorImpl) -> bool {
let selected_base_token_idx = editor.selected_base_token_indices();
!selected_base_token_idx.is_empty()
}
fn shortcut() -> Option<KeyboardShortcut> {
Some(KeyboardShortcut::new(
Modifiers::CTRL.plus(Modifiers::SHIFT),
Key::Enter,
))
}
}
pub(super) struct AddBaseTokenAfter;
impl EditorAction for AddBaseTokenAfter {
type EditorImpl = SpanEditor;
fn label() -> &'static str {
"Add base token after selection"
}
fn perform(editor: &mut Self::EditorImpl) {
let selected_base_token_idx = editor.selected_base_token_indices();
if let Some(largest_idx) = selected_base_token_idx.last()
&& let Some(t) = editor.token.get(*largest_idx)
{
editor
.pending_graph_actions
.push(GraphAction::AddBaseTokenAfter {
reference_node: t.node_name.clone(),
parent_name: editor.parent_name.clone(),
});
}
}
fn enabled(editor: &Self::EditorImpl) -> bool {
let selected_base_token_idx = editor.selected_base_token_indices();
!selected_base_token_idx.is_empty()
}
fn shortcut() -> Option<KeyboardShortcut> {
Some(KeyboardShortcut::new(Modifiers::CTRL, Key::Enter))
}
}
pub(super) struct GoToFirstToken;
impl EditorAction for GoToFirstToken {
type EditorImpl = SpanEditor;
fn label() -> &'static str {
"Go to first base token"
}
fn perform(editor: &mut Self::EditorImpl) {
editor.selected_nodes.clear();
if let Some(t) = editor.token.first() {
editor.selected_nodes.insert(t.node_name.clone());
editor.scroll_to_token = Some(t.start)
}
}
fn enabled(editor: &Self::EditorImpl) -> bool {
!editor.token.is_empty()
}
fn shortcut() -> Option<KeyboardShortcut> {
Some(KeyboardShortcut::new(Modifiers::NONE, Key::Home))
}
}
pub(super) struct GoToLastToken;
impl EditorAction for GoToLastToken {
type EditorImpl = SpanEditor;
fn label() -> &'static str {
"Go to last base token"
}
fn perform(editor: &mut Self::EditorImpl) {
editor.selected_nodes.clear();
if let Some(t) = editor.token.last() {
editor.selected_nodes.insert(t.node_name.clone());
editor.scroll_to_token = Some(t.end)
}
}
fn enabled(editor: &Self::EditorImpl) -> bool {
!editor.token.is_empty()
}
fn shortcut() -> Option<KeyboardShortcut> {
Some(KeyboardShortcut::new(Modifiers::NONE, Key::End))
}
}
pub(super) struct DeleteSelectedNode;
impl EditorAction for DeleteSelectedNode {
type EditorImpl = SpanEditor;
fn label() -> &'static str {
"Delete selected"
}
fn perform(editor: &mut Self::EditorImpl) {
for (_, segmentation_token) in editor.segmentations.iter_mut() {
segmentation_token.retain(|t| !editor.selected_nodes.contains(&t.node_name));
}
{
editor.pending_graph_actions.push(GraphAction::DeleteNodes {
node_names: editor.selected_nodes.iter().cloned().collect(),
});
}
editor.selected_nodes.clear();
}
fn enabled(editor: &Self::EditorImpl) -> bool {
!editor.selected_nodes.is_empty()
}
fn shortcut() -> Option<KeyboardShortcut> {
Some(KeyboardShortcut::new(Modifiers::NONE, Key::Delete))
}
}
pub(super) struct EditWhitespaceBefore;
impl EditorAction for EditWhitespaceBefore {
type EditorImpl = SpanEditor;
fn label() -> &'static str {
"Edit whitespace before"
}
fn perform(editor: &mut Self::EditorImpl) {
let selected_token_or_segmentation_nodes: BTreeSet<_> = editor
.selected_nodes
.iter()
.filter_map(
|node_name| match editor.node_index_by_name.get(node_name)? {
NodeIndexType::Token { .. } | NodeIndexType::Segmentation { .. } => {
Some(node_name)
}
_ => None,
},
)
.collect();
if selected_token_or_segmentation_nodes.len() == 1
&& let Some(node_name) = selected_token_or_segmentation_nodes.first()
{
editor.active_interaction = Some(ActiveInteraction::EditWhitespaceBefore {
node_name: node_name.to_string(),
});
}
}
fn enabled(editor: &Self::EditorImpl) -> bool {
exactly_one_segmentation_or_token_selected(editor)
}
fn shortcut() -> Option<KeyboardShortcut> {
Some(KeyboardShortcut::new(
Modifiers::COMMAND.plus(Modifiers::SHIFT),
Key::W,
))
}
}
pub(super) struct EditWhitespaceAfter;
impl EditorAction for EditWhitespaceAfter {
type EditorImpl = SpanEditor;
fn label() -> &'static str {
"Edit whitespace after"
}
fn perform(editor: &mut Self::EditorImpl) {
let selected_token_or_segmentation_nodes: BTreeSet<&String> = editor
.selected_nodes
.iter()
.filter_map(
|node_name| match editor.node_index_by_name.get(node_name)? {
NodeIndexType::Token { .. } | NodeIndexType::Segmentation { .. } => {
Some(node_name)
}
_ => None,
},
)
.collect();
if selected_token_or_segmentation_nodes.len() == 1
&& let Some(node_name) = selected_token_or_segmentation_nodes.first()
{
editor.active_interaction = Some(ActiveInteraction::EditWhitespaceAfter {
node_name: node_name.to_string(),
});
}
}
fn enabled(editor: &Self::EditorImpl) -> bool {
exactly_one_segmentation_or_token_selected(editor)
}
fn shortcut() -> Option<KeyboardShortcut> {
Some(KeyboardShortcut::new(Modifiers::COMMAND, Key::W))
}
}
pub(super) struct ExpandNodeRight;
impl EditorAction for ExpandNodeRight {
type EditorImpl = SpanEditor;
fn label() -> &'static str {
"Expand to the right"
}
fn shortcut() -> Option<KeyboardShortcut> {
Some(KeyboardShortcut::new(Modifiers::COMMAND, Key::ArrowRight))
}
fn perform(editor: &mut Self::EditorImpl) {
if let Some(node_name) = editor.selected_nodes.iter().next()
&& editor.get_token_index_by_name(node_name).is_none()
{
editor
.pending_graph_actions
.push(GraphAction::ExpandNodeRight {
reference_node: node_name.clone(),
});
}
}
fn enabled(editor: &Self::EditorImpl) -> bool {
exactly_one_non_token_selected(editor)
}
}
pub(super) struct ExpandNodeLeft;
impl EditorAction for ExpandNodeLeft {
type EditorImpl = SpanEditor;
fn label() -> &'static str {
"Expand to the left"
}
fn shortcut() -> Option<KeyboardShortcut> {
Some(KeyboardShortcut::new(Modifiers::COMMAND, Key::ArrowLeft))
}
fn perform(editor: &mut Self::EditorImpl) {
if let Some(node_name) = editor.selected_nodes.iter().next()
&& editor.get_token_index_by_name(node_name).is_none()
{
editor
.pending_graph_actions
.push(GraphAction::ExpandNodeLeft {
reference_node: node_name.clone(),
});
}
}
fn enabled(editor: &Self::EditorImpl) -> bool {
exactly_one_non_token_selected(editor)
}
}
pub(super) struct ShrinkNodeRight;
impl EditorAction for ShrinkNodeRight {
type EditorImpl = SpanEditor;
fn label() -> &'static str {
"Shrink on the right side"
}
fn shortcut() -> Option<KeyboardShortcut> {
Some(KeyboardShortcut::new(
Modifiers::COMMAND.plus(Modifiers::SHIFT),
Key::ArrowLeft,
))
}
fn perform(editor: &mut Self::EditorImpl) {
if let Some(node_name) = editor.selected_nodes.iter().next()
&& editor.get_token_index_by_name(node_name).is_none()
&& let Some(node_index_entry) = editor.node_index_by_name.get(node_name)
&& editor.get_token_range(node_index_entry).count() > 1
{
editor
.pending_graph_actions
.push(GraphAction::ShrinkNodeRight {
reference_node: node_name.clone(),
});
}
}
fn enabled(editor: &Self::EditorImpl) -> bool {
if exactly_one_non_token_selected(editor)
&& let Some(node_name) = editor.selected_nodes.iter().next()
&& let Some(node_index_entry) = editor.node_index_by_name.get(node_name)
&& editor.get_token_range(node_index_entry).count() > 1
{
true
} else {
false
}
}
}
pub(super) struct ShrinkNodeLeft;
impl EditorAction for ShrinkNodeLeft {
type EditorImpl = SpanEditor;
fn label() -> &'static str {
"Shrink on the left side"
}
fn shortcut() -> Option<KeyboardShortcut> {
Some(KeyboardShortcut::new(
Modifiers::COMMAND.plus(Modifiers::SHIFT),
Key::ArrowRight,
))
}
fn perform(editor: &mut Self::EditorImpl) {
if let Some(node_name) = editor.selected_nodes.iter().next()
&& editor.get_token_index_by_name(node_name).is_none()
&& let Some(node_index_entry) = editor.node_index_by_name.get(node_name)
&& editor.get_token_range(node_index_entry).count() > 1
{
editor
.pending_graph_actions
.push(GraphAction::ShrinkNodeLeft {
reference_node: node_name.clone(),
});
}
}
fn enabled(editor: &Self::EditorImpl) -> bool {
if exactly_one_non_token_selected(editor)
&& let Some(node_name) = editor.selected_nodes.iter().next()
&& let Some(node_index_entry) = editor.node_index_by_name.get(node_name)
&& editor.get_token_range(node_index_entry).count() > 1
{
true
} else {
false
}
}
}