Файли
openidea/src/editor/core/copyReference.ts
T

101 рядки
3.8 KiB
TypeScript
Неформатований Анотація Історія

Цей файл містить неоднозначні символи Юнікоду
Цей файл містить символи Юнікоду, які можна сплутати з іншими символами. Якщо ви вважаєте, що це зроблено навмисно, можете сміливо ігнорувати це попередження. Щоб показати їх, скористайтеся кнопкою Escape.
/**
* 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;
}
}