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

"Failed to find tab with index" when renaming after deleting tabs with plugin api. #3535

Open
Pytness opened this issue Aug 1, 2024 · 1 comment

Comments

@Pytness
Copy link

Pytness commented Aug 1, 2024

Basic information

Version: zellij 0.40.1
uname -av: Linux *** 5.15.146.1-microsoft-standard-WSL2 #1 SMP Thu Jan 11 04:09:03 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux

Issue description

Creating and deleting tabs through the plugin api may cause zellij to misreport their position

Minimal reproduction

Here is an example using a timer to allow zellij to assimilate what is happening and report tab updates:

#[derive(Default, Debug)]
struct TimerState {
    index: usize,
}

impl TimerState {
    fn new() -> Self {
        Self { index: 0 }
    }

    fn next(&mut self) -> bool {
        let mut running = true;

        match self.index {
            // We start with the default tab
            //
            0 => new_tab(), // 2 << Rename this

            1 => new_tab(), // 3 << Delete this
            2 => new_tab(), // 4 << Delete this

            4 => new_tab(), // 5 << Try renaming this
            5 => new_tab(), // 6 << Renaming this renames 3

            // expected state: ["1", "2", "3", "4", "5", "6"]
            9 => rename_tab(1 + 1, "this is the second tab".to_string()),

            // expected state: ["1", "second", "3", "4", "5", "6"]

            // delete the third tab
            10 => {
                go_to_tab(2);
                close_focused_tab();
            }

            // delete the third tab again
            11 => {
                go_to_tab(2);
                close_focused_tab();
            }

            // expected state: ["1", "second",  "5", "6"]

            // // rename the fourth tab
            // // We deleted the third tab, so the fourth tab is now the third tab
            12 => rename_tab(2 + 1, "this is the third tab".to_string()),

            // expected state: ["1", "second", "third", "6"]

            // rename the fourth tab
            13 => rename_tab(3 + 1, "this is the fourth tab".to_string()),
            // expected state: ["1", "second", "third", "fourth"]]

            // rename the fith tab
            14 => rename_tab(4 + 1, "this is the fith tab".to_string()),

            // expected state: ["1", "second", "third", "fourth"]]
            // observed state: ["1", "second", "fith",  "6"]]
            // Reported state of the tabs is not the same as the expected state
            // DEBUG  |...| 2024-08-01 15:51:51.109 [id: 1     ] Tab: pos 0,  name: "Tab #1"
            // DEBUG  |...| 2024-08-01 15:51:51.109 [id: 1     ] Tab: pos 1,  name: "this is the second tab"
            // DEBUG  |...| 2024-08-01 15:51:51.109 [id: 1     ] Tab: pos 2,  name: "this is the fith tab"
            // DEBUG  |...| 2024-08-01 15:51:51.109 [id: 1     ] Tab: pos 3,  name: "Tab #6"
            15 => {
                running = false;
            }

            _ => {}
        }

        self.index += 1;

        running
    }
}


impl ZellijPlugin for State {
    fn load(&mut self, configuration: BTreeMap<String, String>) {
        // we need the ReadApplicationState permission to receive the ModeUpdate and TabUpdate
        // events
        // we need the ChangeApplicationState permission to Change Zellij state (Panes, Tabs and UI)
        request_permission(&[
            PermissionType::ReadApplicationState,
            PermissionType::ChangeApplicationState,
        ]);

        self.config = Config::from_configuration(configuration);

        subscribe(&[
            EventType::PaneUpdate,
            EventType::TabUpdate,
            EventType::Key,
            EventType::Timer,
        ]);
    }

    fn update(&mut self, event: Event) -> bool {
        if !self.initialized {
            self.initialize = true;
            set_timeout(0.5);
        }

        match event {
            Event::Timer(_) => {
                if self.timer.next() {
                    set_timeout(0.5);
                };
            }
	}
    }
    // ...
}

Other relevant information

asciicast

@Pytness
Copy link
Author

Pytness commented Sep 9, 2024

any updates?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant