gutter and keys
This commit is contained in:
+378
-80
@@ -36,12 +36,12 @@ fn lua_lsp_api_lists_no_servers_and_validates_config() {
|
||||
std::fs::write(
|
||||
&script,
|
||||
r#"
|
||||
assert(#app:lsp_servers() == 0)
|
||||
assert(app:stop_lsp("missing") == false)
|
||||
assert(app:restart_lsp("missing") == false)
|
||||
assert(#app.editor:lsp_servers() == 0)
|
||||
assert(app.editor:stop_lsp("missing") == false)
|
||||
assert(app.editor:restart_lsp("missing") == false)
|
||||
|
||||
-- Без cmd сервер не запускается, а ошибка объясняет, чего не хватает.
|
||||
local ok, err = pcall(function() app:run_lsp({ name = "broken" }) end)
|
||||
local ok, err = pcall(function() app.editor:run_lsp({ name = "broken" }) end)
|
||||
assert(not ok)
|
||||
assert(tostring(err):find("cmd", 1, true), tostring(err))
|
||||
"#,
|
||||
@@ -127,7 +127,7 @@ fn api_lists_tabs_and_closes_them_by_number() {
|
||||
let workspace = Rc::new(RefCell::new(Workspace::default()));
|
||||
let (api, _keymap) = install_api(workspace.clone());
|
||||
|
||||
let tabs = api.tabs();
|
||||
let tabs = api.editor().tabs();
|
||||
assert_eq!(
|
||||
tabs.iter()
|
||||
.map(|tab| (tab.index, tab.title.as_str()))
|
||||
@@ -136,13 +136,13 @@ fn api_lists_tabs_and_closes_them_by_number() {
|
||||
);
|
||||
|
||||
// Несуществующий номер ничего не закрывает.
|
||||
assert!(!api.close_tab(99));
|
||||
assert_eq!(api.tabs().len(), 1);
|
||||
assert!(!api.editor().close_tab(99));
|
||||
assert_eq!(api.editor().tabs().len(), 1);
|
||||
|
||||
api.open_tab();
|
||||
assert_eq!(api.tabs().len(), 2);
|
||||
assert!(api.close_tab(1));
|
||||
assert_eq!(api.tabs().len(), 1);
|
||||
api.editor().open_tab();
|
||||
assert_eq!(api.editor().tabs().len(), 2);
|
||||
assert!(api.editor().close_tab(1));
|
||||
assert_eq!(api.editor().tabs().len(), 1);
|
||||
assert_eq!(workspace.borrow().documents().len(), 1);
|
||||
}
|
||||
|
||||
@@ -152,26 +152,26 @@ fn api_sets_and_clears_tab_icons() {
|
||||
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_eq!(api.editor().tabs()[0].icon, "file");
|
||||
assert_eq!(api.editor().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.editor().set_tab_icon(0, "file-code"));
|
||||
assert_eq!(api.editor().tabs()[0].icon, "file-code");
|
||||
assert_eq!(api.editor().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.editor().set_tab_icon(0, "icons/book-open.svg"));
|
||||
assert_eq!(api.editor().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.editor().set_tab_icon(0, "definitely-not-an-icon"));
|
||||
assert_eq!(api.editor().tab_icon(0).as_deref(), Some("book-open"));
|
||||
assert!(!api.editor().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);
|
||||
assert!(api.editor().clear_tab_icon(0));
|
||||
assert_eq!(api.editor().tab_icon(0).as_deref(), Some("file"));
|
||||
assert!(!api.editor().clear_tab_icon(99));
|
||||
assert_eq!(api.editor().tab_icon(99), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -184,14 +184,14 @@ fn lua_script_can_set_tab_icons() {
|
||||
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")
|
||||
app:open_tab()
|
||||
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")
|
||||
assert(app.editor:tab_icon(0) == "file")
|
||||
assert(app.editor:set_tab_icon(0, "book-open"))
|
||||
assert(app.editor:tab_icon(0) == "book-open")
|
||||
app.editor:open_tab()
|
||||
assert(app.editor:tabs()[2].icon == "file")
|
||||
assert(not app.editor:set_tab_icon(0, "no-such-icon"))
|
||||
assert(app.editor:clear_tab_icon(0))
|
||||
assert(app.editor:tab_icon(0) == "file")
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -203,6 +203,87 @@ fn lua_script_can_set_tab_icons() {
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn api_sets_and_clears_gutter_icons_per_document() {
|
||||
let workspace = Rc::new(RefCell::new(Workspace::default()));
|
||||
let (api, _keymap) = install_api(workspace);
|
||||
|
||||
assert_eq!(api.editor().gutter_icon(0), None);
|
||||
assert!(api.editor().set_gutter_icon(2, "circle", None));
|
||||
assert_eq!(api.editor().gutter_icon(2).as_deref(), Some("circle"));
|
||||
assert_eq!(api.editor().gutter_color(2), None);
|
||||
|
||||
// Неизвестная иконка и некорректный цвет ничего не меняют.
|
||||
assert!(
|
||||
!api.editor()
|
||||
.set_gutter_icon(3, "definitely-not-an-icon", None)
|
||||
);
|
||||
assert!(
|
||||
!api.editor()
|
||||
.set_gutter_icon(3, "circle", Some("not-a-color"))
|
||||
);
|
||||
assert_eq!(api.editor().gutter_icon(3), None);
|
||||
|
||||
assert!(api.editor().set_gutter_icon(2, "play", Some("#ff0000")));
|
||||
assert_eq!(api.editor().gutter_icon(2).as_deref(), Some("play"));
|
||||
assert_eq!(api.editor().gutter_color(2).as_deref(), Some("#ff0000"));
|
||||
|
||||
// Иконки принадлежат вкладке, а не приложению.
|
||||
api.editor().open_tab();
|
||||
assert_eq!(api.editor().gutter_icon(2), None);
|
||||
assert!(api.editor().set_gutter_icon(0, "circle", None));
|
||||
api.editor().set_active_tab(0);
|
||||
assert_eq!(api.editor().gutter_icon(2).as_deref(), Some("play"));
|
||||
|
||||
assert!(api.editor().clear_gutter_icon(2));
|
||||
assert!(!api.editor().clear_gutter_icon(2));
|
||||
assert_eq!(api.editor().gutter_icon(2), None);
|
||||
|
||||
api.editor().clear_gutter();
|
||||
assert_eq!(api.editor().gutter_icon(0), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lua_script_can_set_gutter_icons() {
|
||||
let workspace = Rc::new(RefCell::new(Workspace::default()));
|
||||
let (api, _keymap) = install_api(workspace);
|
||||
|
||||
let dir = temp_script_dir("gutter-icons");
|
||||
let script = dir.join("gutter.lua");
|
||||
std::fs::write(
|
||||
&script,
|
||||
r##"
|
||||
assert(app.editor:gutter_icon(0) == nil)
|
||||
assert(app.editor:set_gutter_icon(4, "circle"))
|
||||
assert(app.editor:gutter_icon(4) == "circle")
|
||||
assert(app.editor:gutter_color(4) == nil)
|
||||
|
||||
assert(app.editor:set_gutter_icon(4, "play", "#00ff00"))
|
||||
assert(app.editor:gutter_icon(4) == "play")
|
||||
assert(app.editor:gutter_color(4) == "#00ff00")
|
||||
|
||||
assert(not app.editor:set_gutter_icon(1, "no-such-icon"))
|
||||
assert(not app.editor:set_gutter_icon(1, "circle", "oops"))
|
||||
assert(app.editor:gutter_icon(1) == nil)
|
||||
|
||||
assert(app.editor:clear_gutter_icon(4))
|
||||
assert(not app.editor:clear_gutter_icon(4))
|
||||
assert(app.editor:gutter_icon(4) == nil)
|
||||
|
||||
app.editor:set_gutter_icon(0, "circle")
|
||||
app.editor:clear_gutter()
|
||||
assert(app.editor:gutter_icon(0) == nil)
|
||||
"##,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let engine = ScriptEngine::new(api).expect("движок Lua");
|
||||
engine.run_file(&script).expect("скрипт gutter");
|
||||
|
||||
drop(engine);
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn api_save_writes_active_tab_to_file_named_after_it() {
|
||||
// Тест пишет во временный файл, а не в каталог проекта.
|
||||
@@ -223,7 +304,7 @@ fn api_save_writes_active_tab_to_file_named_after_it() {
|
||||
assert!(workspace.borrow().active().is_dirty());
|
||||
|
||||
let (api, _keymap) = install_api(workspace.clone());
|
||||
api.save().expect("сохранение должно удаться");
|
||||
api.editor().save().expect("сохранение должно удаться");
|
||||
|
||||
assert_eq!(
|
||||
std::fs::read_to_string(&path).expect("файл должен существовать"),
|
||||
@@ -239,19 +320,19 @@ fn api_switches_tabs_forward_and_back_in_a_cycle() {
|
||||
let workspace = Rc::new(RefCell::new(Workspace::default()));
|
||||
let (api, _keymap) = install_api(workspace.clone());
|
||||
|
||||
api.open_tab();
|
||||
api.set_active_tab(0);
|
||||
assert_eq!(api.active_tab_index(), 0);
|
||||
api.editor().open_tab();
|
||||
api.editor().set_active_tab(0);
|
||||
assert_eq!(api.editor().active_tab_index(), 0);
|
||||
|
||||
assert_eq!(api.next_tab(), 1);
|
||||
assert_eq!(api.active_tab_index(), 1);
|
||||
assert_eq!(api.editor().next_tab(), 1);
|
||||
assert_eq!(api.editor().active_tab_index(), 1);
|
||||
|
||||
// С последней вкладки следующая — первая.
|
||||
assert_eq!(api.next_tab(), 0);
|
||||
assert_eq!(api.editor().next_tab(), 0);
|
||||
|
||||
// С первой вкладки предыдущая — последняя.
|
||||
assert_eq!(api.previous_tab(), 1);
|
||||
assert_eq!(api.previous_tab(), 0);
|
||||
assert_eq!(api.editor().previous_tab(), 1);
|
||||
assert_eq!(api.editor().previous_tab(), 0);
|
||||
assert_eq!(workspace.borrow().active_index(), 0);
|
||||
}
|
||||
|
||||
@@ -572,7 +653,7 @@ fn lua_diagnostics_handler_receives_every_server_and_file() {
|
||||
std::fs::write(
|
||||
&script,
|
||||
r#"
|
||||
app:on_lsp_diagnostics(0, function(servers)
|
||||
app.editor:on_lsp_diagnostics(0, function(servers)
|
||||
assert(#servers == 1, #servers)
|
||||
local entry = servers[1]
|
||||
assert(entry.server == "test")
|
||||
@@ -589,7 +670,7 @@ fn lua_diagnostics_handler_receives_every_server_and_file() {
|
||||
assert(first.message == "плохо")
|
||||
|
||||
assert(entry.diagnostics[2].severity_name == "warning")
|
||||
app:open_tab()
|
||||
app.editor:open_tab()
|
||||
end)
|
||||
"#,
|
||||
)
|
||||
@@ -597,7 +678,10 @@ fn lua_diagnostics_handler_receives_every_server_and_file() {
|
||||
|
||||
let engine = ScriptEngine::new(api.clone()).expect("движок Lua");
|
||||
engine.run_file(&script).expect("регистрация обработчика");
|
||||
assert_eq!(api.diagnostics_delay(), Some(std::time::Duration::ZERO));
|
||||
assert_eq!(
|
||||
api.editor().diagnostics_delay(),
|
||||
Some(std::time::Duration::ZERO)
|
||||
);
|
||||
|
||||
let uri = crate::lsp::path_to_uri(Path::new("/tmp/qode-diagnostics.go"));
|
||||
let diagnostics = vec![
|
||||
@@ -623,7 +707,7 @@ fn lua_diagnostics_handler_receives_every_server_and_file() {
|
||||
assert_eq!(report[0].path.as_deref(), Some("/tmp/qode-diagnostics.go"));
|
||||
assert_eq!(report[0].diagnostics.len(), 2);
|
||||
|
||||
assert!(api.emit_diagnostics(report));
|
||||
assert!(api.editor().emit_diagnostics(report));
|
||||
assert_eq!(
|
||||
workspace.borrow().documents().len(),
|
||||
2,
|
||||
@@ -696,13 +780,13 @@ fn lua_script_can_include_files_and_control_the_app() {
|
||||
let main = dir.join("main.lua");
|
||||
std::fs::write(
|
||||
&helper,
|
||||
"function lua_config() app:set_active_text(\"from lua\") end\n",
|
||||
"function lua_config() app.editor:set_active_text(\"from lua\") end\n",
|
||||
)
|
||||
.expect("helper.lua");
|
||||
std::fs::write(
|
||||
&main,
|
||||
format!(
|
||||
"include({:?})\nlua_config()\napp:open_tab()\napp:set_language(1, \"python\")\n",
|
||||
"include({:?})\nlua_config()\napp.editor:open_tab()\napp.editor:set_language(1, \"python\")\n",
|
||||
helper.to_string_lossy()
|
||||
),
|
||||
)
|
||||
@@ -720,12 +804,12 @@ fn lua_script_can_include_files_and_control_the_app() {
|
||||
assert_eq!(
|
||||
state.active_index(),
|
||||
1,
|
||||
"app:open_tab() не переключил вкладку"
|
||||
"app.editor:open_tab() не переключил вкладку"
|
||||
);
|
||||
assert_eq!(
|
||||
state.documents()[1].language,
|
||||
Some(Language::Python),
|
||||
"app:set_language() не включил подсветку из Lua"
|
||||
"app.editor:set_language() не включил подсветку из Lua"
|
||||
);
|
||||
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
@@ -740,7 +824,7 @@ fn api_opens_file_from_disk_and_detects_language() {
|
||||
let path = dir.join("main.go");
|
||||
std::fs::write(&path, "package main\n").unwrap();
|
||||
|
||||
let index = api.open(&path).expect("файл открыт");
|
||||
let index = api.editor().open(&path).expect("файл открыт");
|
||||
{
|
||||
let state = workspace.borrow();
|
||||
assert_eq!(index, state.active_index());
|
||||
@@ -752,7 +836,7 @@ fn api_opens_file_from_disk_and_detects_language() {
|
||||
|
||||
// Повторное открытие чистого файла переиспользует вкладку.
|
||||
let tabs = workspace.borrow().documents().len();
|
||||
assert_eq!(api.open(&path).unwrap(), index);
|
||||
assert_eq!(api.editor().open(&path).unwrap(), index);
|
||||
assert_eq!(workspace.borrow().documents().len(), tabs);
|
||||
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
@@ -770,8 +854,14 @@ fn lua_open_controls_focus_and_cursor() {
|
||||
|
||||
// Без второго аргумента фокус остаётся прежним.
|
||||
let plain = dir.join("plain.lua");
|
||||
std::fs::write(&plain, format!("app:open({:?})\n", path.to_string_lossy())).unwrap();
|
||||
engine.run_file(&plain).expect("app:open без позиции");
|
||||
std::fs::write(
|
||||
&plain,
|
||||
format!("app.editor:open({:?})\n", path.to_string_lossy()),
|
||||
)
|
||||
.unwrap();
|
||||
engine
|
||||
.run_file(&plain)
|
||||
.expect("app.editor:open без позиции");
|
||||
assert_eq!(
|
||||
workspace.borrow_mut().take_focus_after_sync(),
|
||||
Some(FocusAfterSync::Keep)
|
||||
@@ -781,10 +871,12 @@ fn lua_open_controls_focus_and_cursor() {
|
||||
let focus = dir.join("focus.lua");
|
||||
std::fs::write(
|
||||
&focus,
|
||||
format!("app:open({:?}, {{}})\n", path.to_string_lossy()),
|
||||
format!("app.editor:open({:?}, {{}})\n", path.to_string_lossy()),
|
||||
)
|
||||
.unwrap();
|
||||
engine.run_file(&focus).expect("app:open с пустой таблицей");
|
||||
engine
|
||||
.run_file(&focus)
|
||||
.expect("app.editor:open с пустой таблицей");
|
||||
assert_eq!(
|
||||
workspace.borrow_mut().take_focus_after_sync(),
|
||||
Some(FocusAfterSync::Editor(None))
|
||||
@@ -794,10 +886,10 @@ fn lua_open_controls_focus_and_cursor() {
|
||||
let at = dir.join("at.lua");
|
||||
std::fs::write(
|
||||
&at,
|
||||
format!("app:open({:?}, {{2, 5}})\n", path.to_string_lossy()),
|
||||
format!("app.editor:open({:?}, {{2, 5}})\n", path.to_string_lossy()),
|
||||
)
|
||||
.unwrap();
|
||||
engine.run_file(&at).expect("app:open с позицией");
|
||||
engine.run_file(&at).expect("app.editor:open с позицией");
|
||||
assert_eq!(
|
||||
workspace.borrow_mut().take_focus_after_sync(),
|
||||
Some(FocusAfterSync::Editor(Some(19)))
|
||||
@@ -815,7 +907,7 @@ fn api_open_missing_file_creates_an_empty_buffer() {
|
||||
let dir = temp_script_dir("open-missing");
|
||||
let path = dir.join("new.go");
|
||||
|
||||
let index = api.open(&path).expect("новый буфер");
|
||||
let index = api.editor().open(&path).expect("новый буфер");
|
||||
let state = workspace.borrow();
|
||||
assert_eq!(index, state.active_index());
|
||||
assert_eq!(state.active().name, path.to_string_lossy());
|
||||
@@ -839,14 +931,14 @@ fn lua_app_open_opens_a_file_and_returns_its_tab() {
|
||||
std::fs::write(
|
||||
&script,
|
||||
format!(
|
||||
"local index = app:open({:?})\nassert(index == app:active_tab())\n",
|
||||
"local index = app.editor:open({:?})\nassert(index == app.editor:active_tab())\n",
|
||||
path.to_string_lossy()
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let engine = ScriptEngine::new_in(api, &dir).expect("движок Lua");
|
||||
engine.run_file(&script).expect("app:open из Lua");
|
||||
engine.run_file(&script).expect("app.editor:open из Lua");
|
||||
|
||||
let state = workspace.borrow();
|
||||
assert_eq!(state.active().name, path.to_string_lossy());
|
||||
@@ -866,7 +958,7 @@ fn lua_engine_loads_init_from_a_custom_config_dir() {
|
||||
let dir = temp_script_dir("config-dir");
|
||||
std::fs::write(
|
||||
dir.join("helper.lua"),
|
||||
"function config_value() app:set_active_text(\"custom config\") end\n",
|
||||
"function config_value() app.editor:set_active_text(\"custom config\") end\n",
|
||||
)
|
||||
.expect("helper.lua");
|
||||
std::fs::write(
|
||||
@@ -895,11 +987,11 @@ fn lua_key_bindings_match_press_release_and_modifiers() {
|
||||
std::fs::write(
|
||||
&script,
|
||||
r#"
|
||||
app:map_key("a", function() app:set_active_text("a pressed") end)
|
||||
app:map_key("!1", function() app:set_active_text("1 released") end)
|
||||
app:map_key("<S-c>", function() app:set_active_text("shift c") end)
|
||||
app:map_key("<C-z>", function() app:set_active_text("ctrl z") end)
|
||||
app:map_key("<A-!r>", function() app:set_active_text("alt r released") end)
|
||||
app:map_key("", "a", function() app.editor:set_active_text("a pressed") end)
|
||||
app:map_key("", "!1", function() app.editor:set_active_text("1 released") end)
|
||||
app:map_key("", "<S-c>", function() app.editor:set_active_text("shift c") end)
|
||||
app:map_key("", "<C-z>", function() app.editor:set_active_text("ctrl z") end)
|
||||
app:map_key("", "<A-!r>", function() app.editor:set_active_text("alt r released") end)
|
||||
"#,
|
||||
)
|
||||
.expect("keys.lua");
|
||||
@@ -997,11 +1089,11 @@ fn lua_keymap_accepts_command_names_and_nil() {
|
||||
std::fs::write(
|
||||
&script,
|
||||
r#"
|
||||
app:map_key("<C-n>", "next_tab")
|
||||
app:map_key("", "<C-n>", "next_tab")
|
||||
app:map_completion_key("<C-n>", "completion_next")
|
||||
|
||||
local ok, err = pcall(function()
|
||||
app:map_key("<C-x>", "no_such_command")
|
||||
app:map_key("", "<C-x>", "no_such_command")
|
||||
end)
|
||||
assert(not ok)
|
||||
assert(tostring(err):find("доступны", 1, true), tostring(err))
|
||||
@@ -1042,7 +1134,7 @@ fn lua_keymap_accepts_command_names_and_nil() {
|
||||
// nil удаляет привязку, не оставляя заглушку.
|
||||
std::fs::write(
|
||||
&script,
|
||||
"app:map_key(\"<C-n>\", nil)\napp:map_completion_key(\"<C-n>\", nil)\n",
|
||||
"app:map_key(\"\", \"<C-n>\", nil)\napp:map_completion_key(\"<C-n>\", nil)\n",
|
||||
)
|
||||
.expect("keys.lua");
|
||||
engine.run_file(&script).expect("привязки должны удалиться");
|
||||
@@ -1106,22 +1198,22 @@ fn highlighting_is_off_by_default_and_enabled_through_api() {
|
||||
let workspace = Rc::new(RefCell::new(Workspace::default()));
|
||||
let (api, _keymap) = install_api(workspace.clone());
|
||||
|
||||
assert_eq!(api.language(0), None);
|
||||
assert_eq!(api.editor().language(0), None);
|
||||
assert_eq!(workspace.borrow().documents()[0].language, None);
|
||||
|
||||
assert!(api.set_language(0, "rs"));
|
||||
assert_eq!(api.language(0), Some("rust".to_owned()));
|
||||
assert!(api.editor().set_language(0, "rs"));
|
||||
assert_eq!(api.editor().language(0), Some("rust".to_owned()));
|
||||
assert_eq!(
|
||||
workspace.borrow().documents()[0].language,
|
||||
Some(Language::Rust)
|
||||
);
|
||||
|
||||
// Неизвестный язык и несуществующая вкладка.
|
||||
assert!(!api.set_language(0, "cobol"));
|
||||
assert!(!api.set_language(99, "rust"));
|
||||
assert!(!api.editor().set_language(0, "cobol"));
|
||||
assert!(!api.editor().set_language(99, "rust"));
|
||||
|
||||
assert!(api.clear_language(0));
|
||||
assert_eq!(api.language(0), None);
|
||||
assert!(api.editor().clear_language(0));
|
||||
assert_eq!(api.editor().language(0), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1144,8 +1236,13 @@ fn keymap_bindings_are_parsed_and_replaced() {
|
||||
.load("return function() end")
|
||||
.eval::<mlua::Function>()
|
||||
.unwrap();
|
||||
keymap.set(KeyContext::Global, plain.clone(), KeyHandler::Lua(first));
|
||||
keymap.set(KeyContext::Global, plain, KeyHandler::Lua(second));
|
||||
keymap.set(
|
||||
KeyContext::Global,
|
||||
"",
|
||||
plain.clone(),
|
||||
KeyHandler::Lua(first),
|
||||
);
|
||||
keymap.set(KeyContext::Global, "", plain, KeyHandler::Lua(second));
|
||||
|
||||
let stroke = Keystroke {
|
||||
modifiers: Modifiers::default(),
|
||||
@@ -1175,7 +1272,7 @@ fn lua_key_bindings_receive_keys_while_editor_is_focused(cx: &mut TestAppContext
|
||||
let script = dir.join("keys.lua");
|
||||
std::fs::write(
|
||||
&script,
|
||||
r#"app:map_key("<A-r>", function() app:set_active_text("alt r pressed") end)"#,
|
||||
r#"app:map_key("", "<A-r>", function() app.editor:set_active_text("alt r pressed") end)"#,
|
||||
)
|
||||
.expect("keys.lua");
|
||||
let engine = ScriptEngine::new(AppApi::global()).expect("движок Lua");
|
||||
@@ -1199,6 +1296,178 @@ fn lua_key_bindings_receive_keys_while_editor_is_focused(cx: &mut TestAppContext
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
#[gpui_kit::test]
|
||||
fn editor_and_block_keymaps_depend_on_focus(cx: &mut TestAppContext) {
|
||||
cx.update(gpui_kit::init);
|
||||
|
||||
let (app, visual) = cx.add_window_view(MyApp::new);
|
||||
|
||||
let dir = temp_script_dir("focus-keys");
|
||||
let script = dir.join("keys.lua");
|
||||
std::fs::write(
|
||||
&script,
|
||||
r#"
|
||||
app:map_key("", "<A-g>", function() app.editor:set_gutter_icon(0, "circle") end)
|
||||
app.editor:map_key("", "<A-r>", function() app.editor:set_gutter_icon(0, "play") end)
|
||||
|
||||
local tree = app.left_panel:add_tree()
|
||||
tree:append({ { id = "a", label = "a" } }, "")
|
||||
tree:map_key("", "<A-t>", function() app.editor:set_gutter_icon(0, "file-code") end)
|
||||
app.left_panel:open()
|
||||
"#,
|
||||
)
|
||||
.expect("keys.lua");
|
||||
let engine = ScriptEngine::new(AppApi::global()).expect("движок Lua");
|
||||
engine.run_file(&script).expect("привязки");
|
||||
|
||||
let tree = {
|
||||
let panel = AppApi::global().left_panel_handle();
|
||||
let panel = panel.borrow();
|
||||
let PanelBlock::Tree { tree, .. } = &panel.blocks()[0];
|
||||
tree.clone()
|
||||
};
|
||||
let icon = || AppApi::global().editor().gutter_icon(0);
|
||||
|
||||
// Фокус в редакторе: работают глобальные и редакторские привязки.
|
||||
visual.update(|window, cx| {
|
||||
app.update(cx, |this, cx| {
|
||||
this.editor_handle()
|
||||
.update(cx, |state, cx| state.focus(window, cx));
|
||||
});
|
||||
});
|
||||
visual.run_until_parked();
|
||||
|
||||
visual.simulate_keystrokes("alt-r");
|
||||
visual.run_until_parked();
|
||||
assert_eq!(icon().as_deref(), Some("play"));
|
||||
|
||||
// Привязка блока при фокусе в редакторе не срабатывает.
|
||||
visual.simulate_keystrokes("alt-t");
|
||||
visual.run_until_parked();
|
||||
assert_eq!(icon().as_deref(), Some("play"));
|
||||
|
||||
visual.simulate_keystrokes("alt-g");
|
||||
visual.run_until_parked();
|
||||
assert_eq!(icon().as_deref(), Some("circle"));
|
||||
|
||||
// Фокус на дереве: работают глобальные и привязки блока, а не редактора.
|
||||
tree.borrow_mut().focus("a");
|
||||
visual.update(|_window, cx| {
|
||||
app.update(cx, |_, cx| cx.notify());
|
||||
});
|
||||
visual.run_until_parked();
|
||||
|
||||
visual.simulate_keystrokes("alt-t");
|
||||
visual.run_until_parked();
|
||||
assert_eq!(icon().as_deref(), Some("file-code"));
|
||||
|
||||
visual.simulate_keystrokes("alt-r");
|
||||
visual.run_until_parked();
|
||||
assert_eq!(icon().as_deref(), Some("file-code"));
|
||||
|
||||
visual.simulate_keystrokes("alt-g");
|
||||
visual.run_until_parked();
|
||||
assert_eq!(icon().as_deref(), Some("circle"));
|
||||
|
||||
drop(engine);
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lua_key_modes_are_per_object_and_dynamic() {
|
||||
let workspace = Rc::new(RefCell::new(Workspace::default()));
|
||||
let (api, keymap) = install_api(workspace.clone());
|
||||
|
||||
let dir = temp_script_dir("key-modes");
|
||||
let script = dir.join("modes.lua");
|
||||
std::fs::write(
|
||||
&script,
|
||||
r#"
|
||||
assert(app:mode() == "-")
|
||||
app:set_mode("i")
|
||||
assert(app:mode() == "i")
|
||||
app:map_key("-", "<A-a>", function() app.editor:set_active_text("default") end)
|
||||
app:map_key("i", "<A-b>", function() app.editor:set_active_text("insert") end)
|
||||
app:map_key("", "<A-c>", function() app.editor:set_active_text("all") end)
|
||||
|
||||
assert(app.editor:mode() == "-")
|
||||
app.editor:set_mode("n")
|
||||
assert(app.editor:mode() == "n")
|
||||
app.editor:map_key("n", "<A-d>", function() app.editor:set_active_text("editor n") end)
|
||||
app.editor:map_key("i", "<A-e>", function() app.editor:set_active_text("editor i") end)
|
||||
|
||||
local tree = app.left_panel:add_tree()
|
||||
assert(tree:mode() == "-")
|
||||
tree:set_mode("v")
|
||||
assert(tree:mode() == "v")
|
||||
tree:map_key("v", "<A-f>", function() app.editor:set_active_text("tree v") end)
|
||||
"#,
|
||||
)
|
||||
.expect("modes.lua");
|
||||
let engine = ScriptEngine::new(api.clone()).expect("движок Lua");
|
||||
engine.run_file(&script).expect("режимы");
|
||||
|
||||
let tree_keymap = {
|
||||
let panel = api.left_panel_handle();
|
||||
let panel = panel.borrow();
|
||||
let PanelBlock::Tree { tree, .. } = &panel.blocks()[0];
|
||||
tree.borrow().keymap_handle()
|
||||
};
|
||||
let editor_keymap = api.editor_keymap_handle();
|
||||
|
||||
let alt = Modifiers {
|
||||
alt: true,
|
||||
..Default::default()
|
||||
};
|
||||
let fire = |keymap: &Rc<RefCell<KeyMap>>, key: &str| {
|
||||
let stroke = Keystroke {
|
||||
modifiers: alt,
|
||||
key: key.to_owned(),
|
||||
key_char: None,
|
||||
};
|
||||
for handler in keymap
|
||||
.borrow()
|
||||
.handlers_for(KeyContext::Global, &stroke, true)
|
||||
{
|
||||
if let KeyHandler::Lua(callback) = handler {
|
||||
callback.call::<()>(()).expect("Lua-обработчик");
|
||||
}
|
||||
}
|
||||
};
|
||||
let text = || workspace.borrow().active().content.clone();
|
||||
|
||||
// Глобальная карта в режиме i: "-" не действует, "i" и "" — действуют.
|
||||
fire(&keymap, "a");
|
||||
assert_eq!(text(), "");
|
||||
fire(&keymap, "b");
|
||||
assert_eq!(text(), "insert");
|
||||
fire(&keymap, "c");
|
||||
assert_eq!(text(), "all");
|
||||
|
||||
// Смена режима не пересоздаёт привязки.
|
||||
keymap.borrow_mut().set_mode("n").unwrap();
|
||||
fire(&keymap, "b");
|
||||
assert_eq!(text(), "all");
|
||||
// Привязка "-" действует только в режиме по умолчанию.
|
||||
fire(&keymap, "a");
|
||||
assert_eq!(text(), "all");
|
||||
keymap.borrow_mut().set_mode("-").unwrap();
|
||||
fire(&keymap, "a");
|
||||
assert_eq!(text(), "default");
|
||||
|
||||
// У редактора и блока свои режимы и карты.
|
||||
fire(&editor_keymap, "d");
|
||||
assert_eq!(text(), "editor n");
|
||||
fire(&editor_keymap, "e");
|
||||
assert_eq!(text(), "editor n");
|
||||
|
||||
fire(&tree_keymap, "f");
|
||||
assert_eq!(text(), "tree v");
|
||||
|
||||
drop(engine);
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
#[gpui_kit::test]
|
||||
fn opened_panels_render_without_panicking(cx: &mut TestAppContext) {
|
||||
cx.update(gpui_kit::init);
|
||||
@@ -1265,6 +1534,35 @@ fn toolbar_items_render_from_api(cx: &mut TestAppContext) {
|
||||
assert_eq!(toolbar.borrow().right().len(), 1);
|
||||
}
|
||||
|
||||
#[gpui_kit::test]
|
||||
fn gutter_icons_render_from_api(cx: &mut TestAppContext) {
|
||||
cx.update(gpui_kit::init);
|
||||
|
||||
let (app, visual) = cx.add_window_view(MyApp::new);
|
||||
|
||||
let api = AppApi::global();
|
||||
assert!(api.editor().set_gutter_icon(0, "circle", Some("#ff0000")));
|
||||
assert!(api.editor().set_gutter_icon(5, "play", None));
|
||||
|
||||
visual.update(|_window, cx| {
|
||||
app.update(cx, |_, cx| cx.notify());
|
||||
});
|
||||
visual.run_until_parked();
|
||||
|
||||
assert_eq!(
|
||||
AppApi::global().editor().gutter_icon(0).as_deref(),
|
||||
Some("circle")
|
||||
);
|
||||
assert_eq!(
|
||||
AppApi::global().editor().gutter_color(0).as_deref(),
|
||||
Some("#ff0000")
|
||||
);
|
||||
assert_eq!(
|
||||
AppApi::global().editor().gutter_icon(5).as_deref(),
|
||||
Some("play")
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui_kit::test]
|
||||
fn tree_block_renders_with_nested_nodes(cx: &mut TestAppContext) {
|
||||
cx.update(gpui_kit::init);
|
||||
|
||||
Reference in New Issue
Block a user