diff --git a/plugins/official/mybatis/dist/plugin.wasm b/plugins/official/mybatis/dist/plugin.wasm index 5c7d59bc8..4f71ecf9d 100644 Binary files a/plugins/official/mybatis/dist/plugin.wasm and b/plugins/official/mybatis/dist/plugin.wasm differ diff --git a/plugins/official/mybatis/dist/plugin.wasm.sources b/plugins/official/mybatis/dist/plugin.wasm.sources index 26af2f4b7..e59d759a4 100644 --- a/plugins/official/mybatis/dist/plugin.wasm.sources +++ b/plugins/official/mybatis/dist/plugin.wasm.sources @@ -1 +1 @@ -5e7f7adc2c49503d2278ed2a446f18f69561ce51df264100f77f50d69804c277 +c29594e55d321ee5240f8f61223d72c9095695a5b19ff1f8a25c5a6402a4a736 diff --git a/plugins/official/mybatis/src/lib.rs b/plugins/official/mybatis/src/lib.rs index f84cd0188..d10b135a5 100644 --- a/plugins/official/mybatis/src/lib.rs +++ b/plugins/official/mybatis/src/lib.rs @@ -70,51 +70,6 @@ fn ident(c: char) -> bool { c.is_alphanumeric() || c == '_' || c == '$' } -/// Index of the `(` opening a method's parameter list inside a declaration -/// fragment. `(`s that belong to annotation arguments — `@Anno(…)` — are -/// skipped so `@DataPermission(column = "id") int count()` finds the paren -/// after `count`, not the one after `DataPermission`. -fn method_paren(stmt: &str) -> Option { - let mut rest = stmt; - let mut base = 0; - while let Some(i) = rest.find('(') { - // Is the token right before this `(` an annotation name (`@Anno`, `@a.b.Anno`)? - let before = rest[..i].trim_end(); - let annotation = before.rfind('@').is_some_and(|a| before[a + 1..].chars().all(|c| ident(c) || c == '.')); - if !annotation { - return Some(base + i); - } - // Skip the annotation's argument list (it may nest and hold literals). - let mut depth = 1usize; - let mut end = None; - let mut chars = rest[i + 1..].char_indices().peekable(); - while let Some((j, c)) = chars.next() { - match c { - '"' | '\'' => { - for (_, d) in chars.by_ref() { - if d == c { - break; - } - } - } - '(' => depth += 1, - ')' => { - depth -= 1; - if depth == 0 { - end = Some(i + 1 + j + 1); - break; - } - } - _ => {} - } - } - let skip = end?; - base += skip; - rest = &rest[skip..]; - } - None -} - /// The interface's name and methods (with their parameter names). pub fn parse_java(text: &str) -> MapperJava { let package = @@ -140,7 +95,7 @@ pub fn parse_java(text: &str) -> MapperJava { } ';' if depth == 0 => { let stmt = &text[stmt_start..k]; - if let Some(paren) = method_paren(stmt) { + if let Some(paren) = stmt.find('(') { let before = stmt[..paren].trim_end(); let name_start = before.rfind(|c: char| !ident(c)).map_or(0, |p| p + 1); let method = &before[name_start..]; @@ -406,17 +361,6 @@ mod tests { assert_eq!(n.line_markers(&xml)[0].tooltip, "Go to UserMapper.findById()"); } - #[test] - fn method_annotations_do_not_swallow_the_method_name() { - // A method-level annotation with arguments: `stmt.find('(')` would land - // inside `@DataPermission(…)` and read `DataPermission` as the name. - let java = parse_java( - "package com.acme;\n\npublic interface SecuredMapper {\n @DataPermission(column = \"id\", scope = (@Scope(\"self\")))\n List findAll(@Param(\"org\") String org);\n\n @org.acme.Check(value = \"(nested)\")\n int count();\n}\n", - ); - let names: Vec<_> = java.methods.iter().map(|(m, p)| (m.name.as_str(), p.clone())).collect(); - assert_eq!(names, [("findAll", vec!["org".to_owned()]), ("count", Vec::::new())]); - } - #[test] fn completes_ids_and_parameters() { let _host = setup(); diff --git a/src-tauri/src/application_focus.rs b/src-tauri/src/application_focus.rs index be929ce70..292370580 100644 --- a/src-tauri/src/application_focus.rs +++ b/src-tauri/src/application_focus.rs @@ -66,7 +66,7 @@ pub fn observe(app: &tauri::AppHandle) { } } -pub(crate) fn application_active(app: &tauri::AppHandle) -> bool { +fn application_active(app: &tauri::AppHandle) -> bool { // A settings window, utility window or native dialog is still this application. #[cfg(target_os = "macos")] { diff --git a/src-tauri/src/events.rs b/src-tauri/src/events.rs index 2fbdc44a9..ef2576f85 100644 --- a/src-tauri/src/events.rs +++ b/src-tauri/src/events.rs @@ -1,5 +1,5 @@ use ide_core::{EventSink, IdeEvent}; -use tauri::{AppHandle, Emitter, Manager, UserAttentionType}; +use tauri::{AppHandle, Emitter}; /// Name of the single backend → UI event. Payload: [`IdeEvent`]. pub const IDE_EVENT: &str = "ide://event"; @@ -38,31 +38,8 @@ pub struct TauriSink { pub window: Option, } -/// A debug session stopped on a breakpoint: bounce/flash the taskbar icon -/// (IDEA) unless the user is already looking at an OpenIDEA window. The focus -/// check and the request both touch the windowing system, so they run on the -/// main thread; `emit` itself can come from any debug worker thread. -fn flash_taskbar_unless_focused(app: &AppHandle) { - let handle = app.clone(); - let _ = app.run_on_main_thread(move || { - if crate::application_focus::application_active(&handle) { - return; - } - for window in handle.webview_windows().values() { - let _ = window.request_user_attention(Some(UserAttentionType::Informational)); - } - }); -} - impl EventSink for TauriSink { fn emit(&self, event: IdeEvent) { - if matches!( - &event, - IdeEvent::Debug { event: ide_core::event::DebugEvent::Stopped { reason, .. }, .. } - if matches!(reason.as_str(), "breakpoint" | "function breakpoint") - ) { - flash_taskbar_unless_focused(&self.app); - } let result = match &self.window { Some(label) => { for target in crate::windows::document_windows(&self.app, label) { diff --git a/src/editor/core/cm/theme.ts b/src/editor/core/cm/theme.ts index 3ae068345..23eb911c7 100644 --- a/src/editor/core/cm/theme.ts +++ b/src/editor/core/cm/theme.ts @@ -18,7 +18,6 @@ export const findLocals = { export const findStyles = { ".cm-openidea-findMatch": { backgroundColor: "var(--editor-search)", - color: "var(--fg-search-match, inherit)", borderRadius: "2px", }, ".cm-openidea-findExcluded": { @@ -30,7 +29,6 @@ export const findStyles = { }, ".cm-openidea-findMatch-current": { backgroundColor: "var(--editor-search-current)", - color: "var(--fg-search-match, inherit)", outline: "1px solid color-mix(in srgb, var(--editor-search-current) 60%, var(--fg-strong))", }, }; diff --git a/src/features/run/producers.test.ts b/src/features/run/producers.test.ts index f7c45827c..6a9a42b21 100644 --- a/src/features/run/producers.test.ts +++ b/src/features/run/producers.test.ts @@ -813,15 +813,6 @@ describe("project producers", () => { expect( produceDirectory({ path: `${ROOT}/src/test/java/com/acme`, root: ROOT, configs: gradle })!.config!.args, ).toEqual(["test", "--tests", "com.acme.*"]); - // A module's tests run from the build root so `./gradlew` resolves, - // qualified by the module's Gradle path. - const dir = produceDirectory({ path: `${ROOT}/services/api/src/test/java`, root: ROOT, configs: gradle })!; - expect(dir.config).toMatchObject({ cwd: null }); - expect(dir.config!.args).toEqual([":services:api:test"]); - expect( - produceDirectory({ path: `${ROOT}/services/api/src/test/java/com/acme`, root: ROOT, configs: gradle })!.config! - .args, - ).toEqual([":services:api:test", "--tests", "com.acme.*"]); expect(produceDirectory({ path: `${ROOT}/src/main`, root: ROOT, configs })).toBeNull(); expect(produceDirectory({ path: ROOT, root: ROOT, configs })).toBeNull(); }); diff --git a/src/features/run/producers.ts b/src/features/run/producers.ts index df973f223..a0a90bf61 100644 --- a/src/features/run/producers.ts +++ b/src/features/run/producers.ts @@ -14,7 +14,6 @@ import { ipcPath, isSameOrInside } from "@/platform/paths"; import { isTestPath } from "@/platform/languageSettings"; import { currentRoots } from "@/platform/projectRoots"; import { applicationConfig, jvmModuleDir } from "./application"; -import { useBuildModel } from "@/platform/buildModel"; import { formatArgs } from "./configs"; import { goGenerateConfig } from "./goGenerate"; import { goTestParts } from "./goArguments"; @@ -1147,27 +1146,6 @@ function rubyDirectory( return make(minitest, label, [...args, relToCwd(minitest)]); } -/** - * The Gradle build and project path a module directory belongs to, so a module - * test run can stay in the build root (where `./gradlew` resolves) and qualify - * its task (`:services:api:test`). `null` for non-Gradle bases and modules the - * base configuration already runs. Without a loaded model, the Gradle path is - * guessed from the directory's segments — right for the standard layout the - * wrapper itself implies. - */ -function gradleOwnerFor(module: string, base: RunConfig): { dir: string | null; path: string } | null { - if (base.kind !== "gradle" || !module || module === base.cwd) return null; - const dir = module.replace(/\\/g, "/"); - const gradle = useBuildModel - .getState() - .model?.gradle?.find((g) => g.projects.some((p) => p.dir.replace(/\\/g, "/") === dir)); - const project = gradle?.projects.find((p) => p.dir.replace(/\\/g, "/") === dir); - return { - dir: gradle?.dir ?? base.cwd, - path: project?.path ?? `:${module.split("/").join(":")}`, - }; -} - export function produceDirectory(input: ContextInput): Produced | null { input = scopedInput(input); const { root } = input; @@ -1203,20 +1181,11 @@ export function produceDirectory(input: ContextInput): Produced | null { const base = closest(["gradle", "maven"]); if (base) { const module = jvmModuleDir(rel + "/x.java", input.configs); - // Gradle in a multi-module project: run from the build root so the - // wrapper (`./gradlew`) resolves, qualifying the task with the module's - // Gradle path (`services/api` → `:services:api:test`). Other builds run - // in the module directory as before. - const owner = gradleOwnerFor(module, base); - const moduleBase = { ...base, cwd: owner ? owner.dir : module || base.cwd }; - const moduleArgs = (base: RunConfig): string[] => { - if (!owner) return [...base.args]; - return base.args.map((a) => (a === "test" ? `${owner.path}:test` : a)); - }; - if (!jvmPackage) return make(moduleBase, "All Tests", moduleArgs(base)); + const moduleBase = { ...base, cwd: module || base.cwd }; + if (!jvmPackage) return make(moduleBase, "All Tests", [...base.args]); const pkg = jvmPackage[2]!.replace(/\//g, "."); return base.kind === "gradle" - ? make(moduleBase, `Tests in '${pkg}'`, [...moduleArgs(base), "--tests", `${pkg}.*`]) + ? make(moduleBase, `Tests in '${pkg}'`, [...base.args, "--tests", `${pkg}.*`]) : make(moduleBase, `Tests in '${pkg}'`, [ ...base.args, `-Dtest=${pkg}.**.*`, diff --git a/src/ui/Menu.tsx b/src/ui/Menu.tsx index 3dcb46738..0b083979c 100644 --- a/src/ui/Menu.tsx +++ b/src/ui/Menu.tsx @@ -273,9 +273,7 @@ export function MenuList({ items, onDone, onCloseLevel, autoFocusFirst = true, . {r.checked ? : r.icon ? : null} - - {r.label} - + {r.label} {r.shortcut && {r.shortcut}} {hasSub && } diff --git a/src/ui/Tabs.module.css b/src/ui/Tabs.module.css index 7601bf4f7..4f5180c8e 100644 --- a/src/ui/Tabs.module.css +++ b/src/ui/Tabs.module.css @@ -140,13 +140,6 @@ flex: 0 1 auto; min-width: 40px; } -/* A closable tab keeps room for its close slot: only the label shrinks. */ -.squeeze .closable { - min-width: 64px; -} -.squeeze .closeSlot { - flex: none; -} .squeeze .tabButton { flex: 1 1 auto; min-width: 0;