дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 11:15:40 +02:00
fix(windows): open loose external files in the focused project window (#923)
* fix(windows): route loose external files to active project window * fix(windows): open loose files in the focused window only Loose files join the focused window when it holds a local project or LightEdit. A Welcome or remote window, or a project window in the background, keeps the previous routing: the LightEdit window, else a free window, else a new one. --------- Co-authored-by: l0ng-ai <[email protected]>
Цей коміт міститься в:
1 джерело
cb2f249db5
коміт
4bc08a1084
1 file changed
+54
-25
@@ -946,10 +946,11 @@ enum ProjectTarget {
|
||||
}
|
||||
|
||||
/// Routes canonical `paths` (with whether each is a folder) over `windows` (the preferred
|
||||
/// free one first). A folder focuses the window that has it open, else takes a free window,
|
||||
/// one first). A folder focuses the window that has it open, else takes a free window,
|
||||
/// else a new one. A file inside a local project being opened (the deepest root if nested)
|
||||
/// opens in that project's window; the others all join the LightEdit window, else a free
|
||||
/// window or a new one switches to LightEdit for them.
|
||||
/// opens in that project's window; the others join the preferred window when it holds a local
|
||||
/// project or LightEdit, else the LightEdit window, else a free window or a new one switches to
|
||||
/// LightEdit for them.
|
||||
pub fn plan_open(paths: Vec<(PathBuf, bool)>, windows: &[(String, Holds)]) -> Vec<OpenStep> {
|
||||
let mut free = windows.iter().filter(|(_, h)| *h == Holds::Nothing).map(|(l, _)| l.clone());
|
||||
let (mut dirs, mut files) = (Vec::new(), Vec::new());
|
||||
@@ -1009,13 +1010,23 @@ pub fn plan_open(paths: Vec<(PathBuf, bool)>, windows: &[(String, Holds)]) -> Ve
|
||||
None => loose.push(file),
|
||||
}
|
||||
}
|
||||
let preferred = windows.first().filter(|(_, h)| matches!(h, Holds::Project(_) | Holds::LightEdit));
|
||||
let light = windows.iter().find(|(_, h)| *h == Holds::LightEdit);
|
||||
match preferred.or(light) {
|
||||
Some((label, _)) if !loose.is_empty() => {
|
||||
let files = std::mem::take(&mut loose);
|
||||
match in_windows.iter_mut().find(|(l, _)| l == label) {
|
||||
Some((_, list)) => list.extend(files),
|
||||
None => in_windows.push((label.clone(), files)),
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
steps.extend(in_windows.into_iter().map(|(label, files)| OpenStep::Files(label, files)));
|
||||
if !loose.is_empty() {
|
||||
let light = windows.iter().find(|(_, h)| *h == Holds::LightEdit).map(|(l, _)| l.clone());
|
||||
steps.push(match (light, free.next()) {
|
||||
(Some(label), _) => OpenStep::Files(label, loose),
|
||||
(None, Some(label)) => OpenStep::Launch(label, path_launch_of(None, loose)),
|
||||
(None, None) => OpenStep::New(None, loose),
|
||||
steps.push(match free.next() {
|
||||
Some(label) => OpenStep::Launch(label, path_launch_of(None, loose)),
|
||||
None => OpenStep::New(None, loose),
|
||||
});
|
||||
}
|
||||
steps
|
||||
@@ -2023,11 +2034,13 @@ mod tests {
|
||||
assert_eq!(
|
||||
steps,
|
||||
[
|
||||
OpenStep::Files("main".into(), vec![p("/proj/src/a.rs"), p("/proj/e.rs")]),
|
||||
// `/projects` is not inside `/proj`; loose files join the focused project window.
|
||||
OpenStep::Files(
|
||||
"main".into(),
|
||||
vec![p("/proj/src/a.rs"), p("/proj/e.rs"), p("/x/b.txt"), p("/projects/d.rs")],
|
||||
),
|
||||
// The deepest root wins.
|
||||
OpenStep::Files("project-2".into(), vec![p("/proj/sub/c.rs")]),
|
||||
// `/projects` is not inside `/proj`.
|
||||
OpenStep::Files("project-1".into(), vec![p("/x/b.txt"), p("/projects/d.rs")]),
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -2088,18 +2101,13 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn files_opened_from_a_window_go_to_their_project_window_else_light_edit() {
|
||||
let windows = [
|
||||
w("main", Holds::Nothing),
|
||||
w("project-1", Holds::Project(p("/proj"))),
|
||||
w("project-2", Holds::Nothing),
|
||||
w("project-3", Holds::Other),
|
||||
];
|
||||
let files = vec![p("/proj/src/a.rs"), p("/x/b.txt")];
|
||||
// The Welcome window turns into LightEdit itself; another free window doesn't take the files.
|
||||
let (steps, here) = plan_open_files("main", files.clone(), &windows);
|
||||
assert_eq!(steps, [OpenStep::Files("project-1".into(), vec![p("/proj/src/a.rs")])]);
|
||||
let welcome_only = [w("main", Holds::Nothing), w("project-2", Holds::Nothing), w("project-3", Holds::Other)];
|
||||
// With no local project or LightEdit window, the Welcome window turns into LightEdit itself.
|
||||
let (steps, here) = plan_open_files("main", vec![p("/x/b.txt")], &welcome_only);
|
||||
assert!(steps.is_empty());
|
||||
assert_eq!(here, [p("/x/b.txt")]);
|
||||
// A LightEdit window keeps loose files, even with another one open.
|
||||
let files = vec![p("/proj/src/a.rs"), p("/x/b.txt")];
|
||||
let light =
|
||||
[w("main", Holds::LightEdit), w("project-1", Holds::Project(p("/proj"))), w("project-2", Holds::LightEdit)];
|
||||
let (steps, here) = plan_open_files("main", files.clone(), &light);
|
||||
@@ -2109,8 +2117,20 @@ mod tests {
|
||||
let others = [w("main", Holds::Nothing), w("project-2", Holds::LightEdit)];
|
||||
let (steps, here) = plan_open_files("main", vec![p("/x/b.txt")], &others);
|
||||
assert_eq!((steps, here), (vec![OpenStep::Files("project-2".into(), vec![p("/x/b.txt")])], vec![]));
|
||||
// A project window in the background doesn't take them from a Welcome window.
|
||||
let with_proj = [
|
||||
w("main", Holds::Nothing),
|
||||
w("project-1", Holds::Project(p("/proj"))),
|
||||
w("project-2", Holds::Nothing),
|
||||
w("project-3", Holds::Other),
|
||||
];
|
||||
let (steps, here) = plan_open_files("main", files.clone(), &with_proj);
|
||||
assert_eq!(steps, [OpenStep::Files("project-1".into(), vec![p("/proj/src/a.rs")])]);
|
||||
assert_eq!(here, [p("/x/b.txt")]);
|
||||
// A remote project window gets a new LightEdit window, never a free one or its own.
|
||||
let (steps, here) = plan_open_files("project-3", files, &windows);
|
||||
let remote =
|
||||
[w("project-3", Holds::Other), w("main", Holds::Nothing), w("project-1", Holds::Project(p("/proj")))];
|
||||
let (steps, here) = plan_open_files("project-3", files, &remote);
|
||||
assert_eq!(
|
||||
steps,
|
||||
[OpenStep::Files("project-1".into(), vec![p("/proj/src/a.rs")]), OpenStep::New(None, vec![p("/x/b.txt")])]
|
||||
@@ -2119,13 +2139,22 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn files_opened_while_running_take_a_free_window_else_a_new_one() {
|
||||
let free = [w("main", Holds::Project(p("/proj"))), w("project-1", Holds::Nothing)];
|
||||
fn files_opened_while_running_join_the_focused_window_else_a_free_one_else_a_new_one() {
|
||||
// The focused local project window takes them, even with a LightEdit window open.
|
||||
let project =
|
||||
[w("main", Holds::Project(p("/proj"))), w("project-1", Holds::Nothing), w("project-2", Holds::LightEdit)];
|
||||
assert_eq!(
|
||||
plan_open(vec![(p("/x/a.txt"), false)], &project),
|
||||
[OpenStep::Files("main".into(), vec![p("/x/a.txt")])]
|
||||
);
|
||||
// A project window in the background doesn't.
|
||||
let free =
|
||||
[w("main", Holds::Other), w("project-2", Holds::Project(p("/proj"))), w("project-1", Holds::Nothing)];
|
||||
assert_eq!(
|
||||
plan_open(vec![(p("/x/a.txt"), false)], &free),
|
||||
[OpenStep::Launch("project-1".into(), path_launch_of(None, vec![p("/x/a.txt")]))]
|
||||
);
|
||||
let busy = [w("main", Holds::Project(p("/proj"))), w("project-1", Holds::Other)];
|
||||
let busy = [w("main", Holds::Other)];
|
||||
assert_eq!(plan_open(vec![(p("/x/a.txt"), false)], &busy), [OpenStep::New(None, vec![p("/x/a.txt")])]);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Заблокувати користувача