mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-08 08:52:12 +00:00
101 lines
3.8 KiB
TypeScript
101 lines
3.8 KiB
TypeScript
/**
|
||
* Edit › Copy Reference (⌥⇧⌘C): the fully qualified name of the symbol at
|
||
* the caret, built from the file's outline (symbols enclosing the caret) and
|
||
* the language's module conventions; `path:line` where there is no symbol.
|
||
*/
|
||
|
||
export interface ReferenceInput {
|
||
/** Backend language id (`java`, `rust`…). */
|
||
language: string;
|
||
path: string;
|
||
/** Project root, for project-relative paths and module names. */
|
||
root: string | null;
|
||
text: string;
|
||
/** Names of the outline symbols enclosing the caret, outermost first. */
|
||
chain: string[];
|
||
/** 0-based caret line. */
|
||
line: number;
|
||
}
|
||
|
||
export function relativePath(path: string, root: string | null): string {
|
||
return root && path.startsWith(root + "/") ? path.slice(root.length + 1) : path;
|
||
}
|
||
|
||
const stem = (path: string) => {
|
||
const name = path.slice(path.lastIndexOf("/") + 1);
|
||
const dot = name.lastIndexOf(".");
|
||
return dot > 0 ? name.slice(0, dot) : name;
|
||
};
|
||
|
||
/** `impl Display for Foo<T>` → `Foo`; generic arguments dropped. */
|
||
export function rustImplType(name: string): string {
|
||
const m = /^impl(?:<[^>]*>)?\s+(?:.*?\s+for\s+)?(.+)$/.exec(name);
|
||
const type = (m ? m[1]! : name).trim();
|
||
return type.replace(/<.*$/, "").replace(/^&(mut\s+)?/, "");
|
||
}
|
||
|
||
/** `crate::a::b` for `src/a/b.rs` (or `src/a/b/mod.rs`); `crate` for lib.rs / main.rs. */
|
||
export function rustModulePath(path: string): string {
|
||
const src = path.lastIndexOf("/src/");
|
||
const rel = src >= 0 ? path.slice(src + 5) : path.slice(path.lastIndexOf("/") + 1);
|
||
const parts = rel.replace(/\.rs$/, "").split("/");
|
||
const last = parts[parts.length - 1];
|
||
if (last === "mod" || ((last === "lib" || last === "main") && parts.length === 1)) parts.pop();
|
||
return ["crate", ...parts].join("::");
|
||
}
|
||
|
||
/** Dotted Python module path relative to the project (a leading `src/` dropped, `__init__` folded). */
|
||
export function pythonModulePath(path: string, root: string | null): string {
|
||
const parts = relativePath(path, root)
|
||
.replace(/\.pyi?$/, "")
|
||
.split("/");
|
||
if (parts[0] === "src" && parts.length > 1) parts.shift();
|
||
if (parts[parts.length - 1] === "__init__") parts.pop();
|
||
return parts.join(".");
|
||
}
|
||
|
||
const declared = (text: string, re: RegExp) => re.exec(text)?.[1] ?? null;
|
||
|
||
export function qualifiedReference(r: ReferenceInput): string {
|
||
const fallback = `${relativePath(r.path, r.root)}:${r.line + 1}`;
|
||
const chain = r.chain.filter(Boolean);
|
||
switch (r.language) {
|
||
case "java":
|
||
case "groovy": {
|
||
if (!chain.length) return fallback;
|
||
const pkg = declared(r.text, /^\s*package\s+([\w.]+)\s*;?/m);
|
||
const [type, ...rest] = chain;
|
||
const typePath = [pkg, type].filter(Boolean).join(".");
|
||
// `pkg.Outer.Inner#member`: the last symbol is the member unless the caret is in a type only.
|
||
return rest.length ? `${[typePath, ...rest.slice(0, -1)].join(".")}#${rest[rest.length - 1]}` : typePath;
|
||
}
|
||
case "kotlin":
|
||
case "scala": {
|
||
if (!chain.length) return fallback;
|
||
const pkg = declared(r.text, /^\s*package\s+([\w.]+)/m);
|
||
return [pkg, ...chain].filter(Boolean).join(".");
|
||
}
|
||
case "csharp": {
|
||
if (!chain.length) return fallback;
|
||
return chain.join(".");
|
||
}
|
||
case "rust": {
|
||
if (!chain.length) return rustModulePath(r.path);
|
||
return [rustModulePath(r.path), ...chain.map((n) => (n.startsWith("impl") ? rustImplType(n) : n))].join("::");
|
||
}
|
||
case "python":
|
||
return [pythonModulePath(r.path, r.root), ...chain].join(".");
|
||
case "go": {
|
||
const pkg = declared(r.text, /^\s*package\s+(\w+)/m) ?? stem(r.path);
|
||
return chain.length ? [pkg, ...chain].join(".") : fallback;
|
||
}
|
||
case "cpp":
|
||
case "c":
|
||
case "objective-cpp":
|
||
case "php":
|
||
return chain.length ? chain.join("::") : fallback;
|
||
default:
|
||
return chain.length ? `${relativePath(r.path, r.root)}:${chain.join(".")}` : fallback;
|
||
}
|
||
}
|