files
This commit is contained in:
+253
@@ -13,7 +13,9 @@ use crate::api::{AppApi, WindowCommand, drain_window_commands};
|
||||
use crate::app::MyApp;
|
||||
use crate::editor::{Language, Workspace};
|
||||
use crate::keymap::{KeyBinding, KeyMap, Modifiers as KeyModifiers};
|
||||
use crate::panel::PanelBlock;
|
||||
use crate::script::ScriptEngine;
|
||||
use crate::tree::TreeNode;
|
||||
|
||||
fn install_api(workspace: Rc<RefCell<Workspace>>) -> (AppApi, Rc<RefCell<KeyMap>>) {
|
||||
let keymap = Rc::new(RefCell::new(KeyMap::default()));
|
||||
@@ -137,6 +139,62 @@ fn api_lists_tabs_and_closes_them_by_number() {
|
||||
assert_eq!(workspace.borrow().documents().len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn api_sets_and_clears_tab_icons() {
|
||||
let workspace = Rc::new(RefCell::new(Workspace::default()));
|
||||
let (api, _keymap) = install_api(workspace);
|
||||
|
||||
// По умолчанию у всех вкладок иконка file.
|
||||
assert_eq!(api.tabs()[0].icon, "file");
|
||||
assert_eq!(api.tab_icon(0).as_deref(), Some("file"));
|
||||
|
||||
assert!(api.set_tab_icon(0, "file-code"));
|
||||
assert_eq!(api.tabs()[0].icon, "file-code");
|
||||
assert_eq!(api.tab_icon(0).as_deref(), Some("file-code"));
|
||||
|
||||
// Имя нормализуется, а вкладка возвращает канонический вариант.
|
||||
assert!(api.set_tab_icon(0, "icons/book-open.svg"));
|
||||
assert_eq!(api.tab_icon(0).as_deref(), Some("book-open"));
|
||||
|
||||
// Неизвестная иконка и несуществующая вкладка ничего не меняют.
|
||||
assert!(!api.set_tab_icon(0, "definitely-not-an-icon"));
|
||||
assert_eq!(api.tab_icon(0).as_deref(), Some("book-open"));
|
||||
assert!(!api.set_tab_icon(99, "file"));
|
||||
|
||||
assert!(api.clear_tab_icon(0));
|
||||
assert_eq!(api.tab_icon(0).as_deref(), Some("file"));
|
||||
assert!(!api.clear_tab_icon(99));
|
||||
assert_eq!(api.tab_icon(99), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lua_script_can_set_tab_icons() {
|
||||
let workspace = Rc::new(RefCell::new(Workspace::default()));
|
||||
let (api, _keymap) = install_api(workspace.clone());
|
||||
|
||||
let dir = temp_script_dir("tab-icons");
|
||||
let script = dir.join("icons.lua");
|
||||
std::fs::write(
|
||||
&script,
|
||||
r#"
|
||||
assert(app:tab_icon(0) == "file")
|
||||
assert(app:set_tab_icon(0, "book-open"))
|
||||
assert(app:tab_icon(0) == "book-open")
|
||||
assert(app:tabs()[2].icon == "file")
|
||||
assert(not app:set_tab_icon(0, "no-such-icon"))
|
||||
assert(app:clear_tab_icon(0))
|
||||
assert(app:tab_icon(0) == "file")
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let engine = ScriptEngine::new(api).expect("движок Lua");
|
||||
engine.run_file(&script).expect("скрипт иконок");
|
||||
|
||||
drop(engine);
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn api_save_writes_active_tab_to_file_named_after_it() {
|
||||
// Тест пишет во временный файл, а не в каталог проекта.
|
||||
@@ -297,6 +355,103 @@ fn lua_script_can_control_panels() {
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lua_tree_block_appends_sorts_and_removes_nodes() {
|
||||
let workspace = Rc::new(RefCell::new(Workspace::default()));
|
||||
let (api, _keymap) = install_api(workspace);
|
||||
|
||||
let dir = temp_script_dir("tree-block");
|
||||
let script = dir.join("tree.lua");
|
||||
std::fs::write(
|
||||
&script,
|
||||
r#"
|
||||
local tree = app.left_panel:add_tree()
|
||||
tree:append({
|
||||
{ id = "b", label = "banana", expandable = false },
|
||||
{ id = "src", label = "src", icon = "folder", expandable = true, expanded = true },
|
||||
{ id = "a", label = "Apple", expandable = false },
|
||||
}, "")
|
||||
tree:append({
|
||||
{ id = "src/main.rs", label = "main.rs", icon = "file-code" },
|
||||
}, "src")
|
||||
|
||||
local toggled, clicked = 0, 0
|
||||
tree:on_toggle(function(expanded, id, loaded) toggled = toggled + 1 end)
|
||||
tree:on_click(function(id) clicked = clicked + 1 end)
|
||||
assert(toggled == 0 and clicked == 0)
|
||||
assert(tree:remove("missing") == false)
|
||||
"#,
|
||||
)
|
||||
.expect("tree.lua");
|
||||
|
||||
let engine = ScriptEngine::new(api.clone()).expect("движок Lua");
|
||||
engine
|
||||
.run_file(&script)
|
||||
.expect("дерево должно наполняться из Lua");
|
||||
drop(engine);
|
||||
|
||||
let panel = api.left_panel_handle();
|
||||
let panel = panel.borrow();
|
||||
let PanelBlock::Tree(tree) = &panel.blocks()[0];
|
||||
let rows = tree.borrow().rows();
|
||||
assert_eq!(
|
||||
rows.iter()
|
||||
.map(|row| (row.label.as_str(), row.depth))
|
||||
.collect::<Vec<_>>(),
|
||||
vec![("src", 0), ("main.rs", 1), ("Apple", 0), ("banana", 0)]
|
||||
);
|
||||
|
||||
assert!(tree.borrow_mut().remove("src"));
|
||||
assert_eq!(
|
||||
tree.borrow()
|
||||
.rows()
|
||||
.iter()
|
||||
.map(|row| row.label.as_str())
|
||||
.collect::<Vec<_>>(),
|
||||
vec!["Apple", "banana"]
|
||||
);
|
||||
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lua_list_dir_returns_sorted_entries_with_paths() {
|
||||
let workspace = Rc::new(RefCell::new(Workspace::default()));
|
||||
let (api, _keymap) = install_api(workspace);
|
||||
|
||||
let dir = temp_script_dir("list-dir");
|
||||
std::fs::create_dir(dir.join("sub")).expect("подкаталог");
|
||||
std::fs::write(dir.join("b.txt"), "b").expect("b.txt");
|
||||
std::fs::write(dir.join("A.txt"), "a").expect("A.txt");
|
||||
let path = dir.to_string_lossy().into_owned();
|
||||
let quoted = format!("{path:?}");
|
||||
|
||||
let scripts = temp_script_dir("list-dir-script");
|
||||
let script = scripts.join("list.lua");
|
||||
std::fs::write(
|
||||
&script,
|
||||
format!(
|
||||
r#"
|
||||
local entries = app:list_dir({quoted})
|
||||
assert(#entries == 3, #entries)
|
||||
assert(entries[1].name == "A.txt")
|
||||
assert(entries[2].name == "b.txt")
|
||||
assert(entries[3].name == "sub")
|
||||
assert(entries[1].is_dir == false)
|
||||
assert(entries[3].is_dir == true)
|
||||
assert(entries[1].id == {quoted} .. "/A.txt")
|
||||
"#
|
||||
),
|
||||
)
|
||||
.expect("list.lua");
|
||||
|
||||
let engine = ScriptEngine::new(api).expect("движок Lua");
|
||||
engine.run_file(&script).expect("app:list_dir");
|
||||
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
let _ = std::fs::remove_dir_all(&scripts);
|
||||
}
|
||||
|
||||
/// Временный каталог с уникальным именем для Lua-сценариев.
|
||||
fn temp_script_dir(prefix: &str) -> std::path::PathBuf {
|
||||
let dir = std::env::temp_dir().join(format!(
|
||||
@@ -700,6 +855,104 @@ fn opened_panels_render_without_panicking(cx: &mut TestAppContext) {
|
||||
assert!(api.bottom_panel_handle().borrow().is_open());
|
||||
}
|
||||
|
||||
#[gpui_kit::test]
|
||||
fn tree_block_renders_with_nested_nodes(cx: &mut TestAppContext) {
|
||||
cx.update(gpui_kit::init);
|
||||
|
||||
let (app, visual) = cx.add_window_view(|window, cx| MyApp::new(window, cx));
|
||||
|
||||
let api = AppApi::global();
|
||||
let tree = api.left_panel_handle().borrow_mut().add_tree();
|
||||
tree.borrow_mut()
|
||||
.append(
|
||||
"",
|
||||
vec![TreeNode {
|
||||
id: "src".to_owned(),
|
||||
label: "src".to_owned(),
|
||||
icon: Some("folder".to_owned()),
|
||||
expandable: true,
|
||||
expanded: true,
|
||||
}],
|
||||
)
|
||||
.unwrap();
|
||||
tree.borrow_mut()
|
||||
.append(
|
||||
"src",
|
||||
vec![TreeNode {
|
||||
id: "src/main.rs".to_owned(),
|
||||
label: "main.rs".to_owned(),
|
||||
icon: Some("file-code".to_owned()),
|
||||
expandable: false,
|
||||
expanded: false,
|
||||
}],
|
||||
)
|
||||
.unwrap();
|
||||
api.left_panel().open();
|
||||
|
||||
visual.update(|_window, cx| {
|
||||
app.update(cx, |_, cx| cx.notify());
|
||||
});
|
||||
visual.run_until_parked();
|
||||
|
||||
assert!(api.left_panel_handle().borrow().is_open());
|
||||
assert_eq!(tree.borrow().rows().len(), 2);
|
||||
}
|
||||
|
||||
#[gpui_kit::test]
|
||||
fn focused_tree_moves_selection_with_arrow_keys(cx: &mut TestAppContext) {
|
||||
cx.update(gpui_kit::init);
|
||||
|
||||
let (app, visual) = cx.add_window_view(|window, cx| MyApp::new(window, cx));
|
||||
|
||||
let api = AppApi::global();
|
||||
let tree = api.left_panel_handle().borrow_mut().add_tree();
|
||||
tree.borrow_mut()
|
||||
.append(
|
||||
"",
|
||||
vec![
|
||||
TreeNode {
|
||||
id: "src".to_owned(),
|
||||
label: "src".to_owned(),
|
||||
icon: Some("folder".to_owned()),
|
||||
expandable: true,
|
||||
expanded: false,
|
||||
},
|
||||
TreeNode {
|
||||
id: "readme".to_owned(),
|
||||
label: "README.md".to_owned(),
|
||||
icon: Some("file".to_owned()),
|
||||
expandable: false,
|
||||
expanded: false,
|
||||
},
|
||||
],
|
||||
)
|
||||
.unwrap();
|
||||
api.left_panel().open();
|
||||
tree.borrow_mut().focus("src");
|
||||
|
||||
visual.update(|_window, cx| {
|
||||
app.update(cx, |_, cx| cx.notify());
|
||||
});
|
||||
visual.run_until_parked();
|
||||
assert_eq!(tree.borrow().selected(), Some("src"));
|
||||
|
||||
// Панель закрыли и открыли снова: фокус должен вернуться на выбранную
|
||||
// строку, иначе стрелка уйдёт в редактор.
|
||||
api.left_panel().close();
|
||||
visual.update(|_window, cx| {
|
||||
app.update(cx, |_, cx| cx.notify());
|
||||
});
|
||||
visual.run_until_parked();
|
||||
api.left_panel().open();
|
||||
visual.update(|_window, cx| {
|
||||
app.update(cx, |_, cx| cx.notify());
|
||||
});
|
||||
visual.run_until_parked();
|
||||
|
||||
visual.simulate_keystrokes("down");
|
||||
assert_eq!(tree.borrow().selected(), Some("readme"));
|
||||
}
|
||||
|
||||
#[gpui_kit::test]
|
||||
fn typing_into_editor_updates_active_document(cx: &mut TestAppContext) {
|
||||
cx.update(gpui_kit::init);
|
||||
|
||||
Reference in New Issue
Block a user