дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 03:05:40 +02:00
* feat(maven): go to declaration for POM properties and parent-managed dependencies
Ctrl/Cmd-click (or Go to Declaration) on a `${property}` reference in a
pom.xml opens the `<properties>` entry that defines it - the POM's own,
else the first one along the parent chain (a child's definition wins; an
active profile's counts as the POM's own) - and a dependency / plugin
whose version a `<dependencyManagement>` / `<pluginManagement>` entry
provides opens that managing declaration. The chain follows
`<relativePath>` in the project and the local repository (Settings >
Maven's, else ~/.m2) for external parents, which open read-only as plain
files; `project.*` built-ins go to their coordinate elements. When
nothing resolves under the caret, nothing navigates: the usual 'Cannot
find declaration to go to' hint shows instead.
The backend answers a caret position (0-based line / UTF-16 column) with
the winner's file, range and kind (maven_goto_declaration), reusing the
build model's POM reader and parent resolver (ide_build::maven) instead
of a new parser. The frontend contribution (build-tools.pomNavigation)
claims only positions that can resolve - inside a `${...}` reference or
a <dependency> / <plugin> element - so the Cmd-hover underline stays out
of the rest of the file.
Refs #1735
* test(maven): normalize expected goto paths like the parent chain does
parent_chain resolves every path it returns through dunce::canonicalize,
but the goto_declaration tests compared those results against raw temp
paths, which differ on the CI runners: on macOS the temp dir sits behind
the /var -> /private/var symlink and on Windows under the RUNNER~1 8.3
short name with backslash separators, so the Rust (macos-latest) and
Rust (windows-latest) jobs failed in the new assertions while the
functionality itself was right.
Feed the expected paths through the same canonicalization call before
comparing, mirroring the production behavior on every platform; on a
plain Linux checkout the canonical form equals the raw path, so the
assertions stay equivalent there.
Refs #1735
91 рядки
3.9 KiB
TypeScript
91 рядки
3.9 KiB
TypeScript
/**
|
|
* Go to declaration in `pom.xml`: ⌘/Ctrl-click (or ⌘B) on a `${property}` reference
|
|
* opens the `<properties>` entry that defines it — the POM's own, else one inherited
|
|
* along the parent chain (a parent POM outside the project opens from the local
|
|
* repository, read-only as a plain file) — and a dependency / plugin whose version a
|
|
* `<dependencyManagement>` / `<pluginManagement>` entry provides opens that managing
|
|
* declaration. The backend resolves both with the build model's POM reader and parent
|
|
* resolver; here only cheap eligibility is computed (a reference span, an artifact
|
|
* element), so the ⌘-hover underline stays out of the rest of the file, and nothing
|
|
* resolves to no jump at all: an unresolvable property or parent never navigates
|
|
* somewhere unrelated.
|
|
*/
|
|
import { EditorState, type Extension } from "@codemirror/state";
|
|
import { CONTRIBUTED_DEFINITION, type ContributedDefinition, type EditorDocContext, whenSynced } from "@/editor/api";
|
|
import { showHint } from "@/editor/lsp/hint";
|
|
import { tx } from "@/platform/i18n";
|
|
import { call, commands } from "@/platform/ipc";
|
|
import { editorService } from "@/workbench/contributions";
|
|
|
|
/** The `${property}` reference covering `at`, if any (clicking the `$` counts). */
|
|
export function propertyRefAt(text: string, at: number): { from: number; to: number } | null {
|
|
const open = text.startsWith("${", at) ? at : text.lastIndexOf("${", at - 1);
|
|
if (open < 0) return null;
|
|
const close = text.indexOf("}", open + 2);
|
|
if (close < 0 || close < at) return null;
|
|
return { from: open, to: close + 1 };
|
|
}
|
|
|
|
/** Whether an opener of `name` before `at` has no closer of it yet (POM elements do not nest). */
|
|
function inside(name: string, text: string, at: number): boolean {
|
|
const before = text.slice(0, at);
|
|
const count = (re: RegExp) => (before.match(re) ?? []).length;
|
|
return count(new RegExp(`<${name}[\\s>]`, "g")) > count(new RegExp(`</${name}\\s*>`, "g"));
|
|
}
|
|
|
|
/**
|
|
* Whether `at` may sit in a `<dependency>` / `<plugin>` element: an eligibility test
|
|
* only (a `<dependency>` inside a comment counts too) — whether the element's version
|
|
* actually comes from a management entry is the backend's answer.
|
|
*/
|
|
export function inArtifactElement(text: string, at: number): boolean {
|
|
return inside("dependency", text, at) || inside("plugin", text, at);
|
|
}
|
|
|
|
/** Go to declaration in a POM of the project (or an opened parent POM of the local repository). */
|
|
export function pomNavigationExtension(ctx: EditorDocContext): Extension {
|
|
if (ctx.virtual || !/(^|\/)pom\.xml$/.test(ctx.path)) return [];
|
|
const eligible = (state: EditorState, pos: number) => {
|
|
const text = state.doc.toString();
|
|
return propertyRefAt(text, pos) !== null || inArtifactElement(text, pos);
|
|
};
|
|
const definition: ContributedDefinition = (view, pos) => {
|
|
if (!eligible(view.state, pos)) return false;
|
|
const doc = view.state.doc;
|
|
const line = doc.lineAt(pos);
|
|
void whenSynced(view)
|
|
.then(() =>
|
|
call(
|
|
commands.mavenGotoDeclaration(ctx.path, {
|
|
line: line.number - 1,
|
|
col: pos - line.from,
|
|
}),
|
|
),
|
|
)
|
|
.then((goto) => {
|
|
if (!goto) {
|
|
showHint(view, tx`Cannot find declaration to go to`);
|
|
return;
|
|
}
|
|
if (view.state.doc !== doc) return; // The user kept typing while the POM resolved.
|
|
void editorService().openFile(goto.path, {
|
|
selection: {
|
|
line: goto.range.start.line,
|
|
col: goto.range.start.col,
|
|
endLine: goto.range.end.line,
|
|
endCol: goto.range.end.col,
|
|
},
|
|
});
|
|
})
|
|
.catch((e: unknown) =>
|
|
showHint(view, e instanceof Error ? e.message : String(e), {
|
|
tone: "error",
|
|
}),
|
|
);
|
|
return true;
|
|
};
|
|
return EditorState.languageData.of((state, pos) =>
|
|
eligible(state, pos) ? [{ [CONTRIBUTED_DEFINITION]: definition }] : [],
|
|
);
|
|
}
|