From 34c6b752d7dbad00fd4452ade0f17d7e01c95a1d Mon Sep 17 00:00:00 2001 From: click33 <2393584716@qq.com> Date: Mon, 2 Mar 2026 09:21:18 +0800 Subject: [PATCH] =?UTF-8?q?AI:=20=E6=96=B0=E5=A2=9E=20skills/remove-redund?= =?UTF-8?q?ancy-import/SKILL.md=EF=BC=8C=E7=94=A8=E4=BA=8E=E6=A3=80?= =?UTF-8?q?=E6=9F=A5=E9=A1=B9=E7=9B=AE=E4=B8=AD=E7=9A=84java=E7=B1=BB?= =?UTF-8?q?=E6=97=A0=E6=95=88=E5=86=97=E4=BD=99=E5=AF=BC=E5=8C=85=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E5=B9=B6=E7=A7=BB=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../skills/remove-redundancy-import/SKILL.md | 76 +++++++++++++++ .../remove-redundancy-import/reference.md | 63 ++++++++++++ .../scan_redundant_imports.py | 96 +++++++++++++++++++ 3 files changed, 235 insertions(+) create mode 100644 .cursor/skills/remove-redundancy-import/SKILL.md create mode 100644 .cursor/skills/remove-redundancy-import/reference.md create mode 100644 .cursor/skills/remove-redundancy-import/scan_redundant_imports.py diff --git a/.cursor/skills/remove-redundancy-import/SKILL.md b/.cursor/skills/remove-redundancy-import/SKILL.md new file mode 100644 index 00000000..b913336a --- /dev/null +++ b/.cursor/skills/remove-redundancy-import/SKILL.md @@ -0,0 +1,76 @@ +--- +name: remove-redundancy-import +description: 检查 Java 类中未被引用的冗余 import 并移除。先输出待审阅计划,用户确认后执行。适用于用户要求清理冗余导包、优化 import、或执行 remove-redundancy-import 时使用。 +--- + +# 移除冗余 import + +检查项目中所有 Java 类的未使用 import,生成清理计划供用户审阅,确认后执行移除。 + +## 使用时机 + +- 用户要求清理冗余导包 +- 用户要求优化 Java import +- 用户明确执行 `remove-redundancy-import` 或提及本 Skill 名称 + +## 强制流程 + +**必须先输出计划,用户确认后再执行移除。** 不得在未审阅的情况下直接修改文件。 + +## 工作流程 + +### 第一步:扫描与解析 + +**优先使用内置脚本**:在 Skill 目录下的 [scan_redundant_imports.py](scan_redundant_imports.py) 已实现完整扫描逻辑,可直接复用。 + +```bash +# 在项目根目录执行 +python .cursor/skills/remove-redundancy-import/scan_redundant_imports.py +# 或指定扫描根路径 +python .cursor/skills/remove-redundancy-import/scan_redundant_imports.py . +``` + +脚本输出格式:`文件路径 | 冗余import1; import2 | 数量`,末尾两行为 `TOTAL_FILES:N` 和 `TOTAL_IMPORTS:M`。 + +**若无 Python 环境**,可手动执行: +1. 使用 `Glob` 查找项目内所有 `**/*.java` 文件 +2. 对每个文件:提取 `package`、`import`,按 [reference.md](reference.md) 判定是否被使用 +3. 汇总存在冗余 import 的文件及列表 + +### 第二步:输出计划 + +使用下方模板生成计划报告,等待用户确认: + +```markdown +## 冗余 import 清理计划 + +| 文件 | 待移除 import | 数量 | +|------|---------------|------| +| path/to/Foo.java | `java.util.Date`, `java.sql.Timestamp` | 2 | +| ... | ... | ... | + +**共 N 个文件,M 处冗余 import。确认后执行移除。** +``` + +### 第三步:执行移除 + +用户确认后,对计划中的每个文件使用 `StrReplace` 移除对应 import 行: + +- 逐行移除,每行格式为 `import ...;` 或 `import static ...;` +- 若某 import 后紧跟空行,可一并移除空行以保持格式整洁 +- 移除后确认文件无语法错误 + +## 检测规则概要 + +- **普通 import**:取最后一段类名(如 `java.util.List` → `List`),在类体中搜索 `\bList\b` +- **static import**:取方法/字段名,在类体中搜索 +- **同包冗余**:import 的包与当前文件 `package` 相同则视为冗余 +- **通配符**:`import pkg.*` 跳过,不自动处理 + +详见 [reference.md](reference.md)。 + +## 注意事项 + +- 通配符 import 无法可靠判断,一律跳过 +- 注解中的类型引用采用保守策略,宁可漏检不误删 +- 移除后建议用户运行 `mvn compile` 验证 diff --git a/.cursor/skills/remove-redundancy-import/reference.md b/.cursor/skills/remove-redundancy-import/reference.md new file mode 100644 index 00000000..41bb668f --- /dev/null +++ b/.cursor/skills/remove-redundancy-import/reference.md @@ -0,0 +1,63 @@ +# 冗余 import 检测规则 + +## 解析步骤 + +### 1. 提取 package + +匹配 `package\s+([\w.]+)\s*;`,得到当前文件所在包。 + +### 2. 提取 import + +匹配以下模式(每行一条): + +- `import\s+([\w.]+)\s*;` — 普通 import +- `import\s+static\s+([\w.]+)\s*;` — static 导入类 +- `import\s+static\s+([\w.]+)\.(\w+)\s*;` — static 导入成员(方法/字段) +- `import\s+[\w.]+\s*\.\s*\*\s*;` — 通配符,**跳过不处理** + +### 3. 确定简单名(Simple Name) + +| import 类型 | 示例 | 简单名 | +|-------------|------|--------| +| 普通类 | `import java.util.List;` | `List` | +| 内部类 | `import pkg.Outer.Inner;` | `Inner` | +| static 类 | `import static pkg.Utils;` | `Utils` | +| static 成员 | `import static pkg.Utils.foo;` | `foo` | + +### 4. 同包冗余 + +若 `import x.y.Z` 的包 `x.y` 与当前文件 `package x.y` 相同,则该 import 冗余(同包无需导入)。 + +### 5. 使用检测 + +在**类体**(`package` 和所有 `import` 之后)中搜索: + +- 使用正则 `\bSimpleName\b` 匹配整词,避免误匹配子串 +- 排除:注释、字符串字面量中的出现 +- 若未找到匹配,则该 import 视为未使用 + +## 边界情况 + +| 情况 | 处理方式 | +|------|----------| +| `import pkg.*;` | 跳过,不自动移除 | +| 注解中的类型 `@Foo` | 若 `Foo` 为 import 的简单名,视为已使用 | +| 泛型 `List` | `List` 会匹配,视为已使用 | +| 同名类(如 `java.util.Date` 与 `java.sql.Date`) | 两 import 都保留;若仅一个被使用,只移除未使用的 | +| Javadoc `@param` 中的类型 | 保守:若不确定则保留 | + +## 正则参考 + +``` +// package +package\s+([\w.]+)\s*; + +// 普通 import(非通配符) +import\s+(?!static)([\w.]+)\s*; + +// static import 成员 +import\s+static\s+[\w.]+\.(\w+)\s*; + +// static import 类 +import\s+static\s+([\w.]+)\s*; +``` diff --git a/.cursor/skills/remove-redundancy-import/scan_redundant_imports.py b/.cursor/skills/remove-redundancy-import/scan_redundant_imports.py new file mode 100644 index 00000000..25e03950 --- /dev/null +++ b/.cursor/skills/remove-redundancy-import/scan_redundant_imports.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +冗余 import 扫描脚本 +按 reference.md 规则扫描项目内 Java 文件,输出待移除的冗余 import 列表。 +用法:在项目根目录执行 python scan_redundant_imports.py +""" +import os +import re +import sys + + +def get_simple_name(imp: str) -> str | None: + """从 import 行提取简单名(用于类体搜索)""" + m = re.match(r'import\s+static\s+[\w.]+\.(\w+)\s*;', imp) + if m: + return m.group(1) + m = re.match(r'import\s+(?:static\s+)?([\w.]+)\s*;', imp) + if m: + return m.group(1).split('.')[-1] + return None + + +def get_import_full(imp: str) -> str: + """提取 import 的完整限定名""" + m = re.match(r'import\s+(?:static\s+)?([\w.]+)\s*;', imp) + return m.group(1).strip() if m else '' + + +def get_import_package(imp: str) -> str: + """提取 import 所在包(用于同包冗余判断)""" + m = re.match(r'import\s+(?:static\s+)?([\w.]+)\s*;', imp) + if m: + parts = m.group(1).split('.') + return '.'.join(parts[:-1]) if len(parts) > 1 else '' + return '' + + +def find_class_body_start(content: str) -> int: + """找到类体起始位置(最后一个 import 之后)""" + last = 0 + for m in re.finditer(r'import\s+(?:static\s+)?[\w.]+\s*;', content): + last = m.end() + return last + + +def main() -> None: + root = sys.argv[1] if len(sys.argv) > 1 else '.' + skip_dirs = {'target', 'build', '.git', 'node_modules'} + + results = [] + for dirpath, dirnames, filenames in os.walk(root): + dirnames[:] = [d for d in dirnames if d not in skip_dirs] + for f in filenames: + if not f.endswith('.java'): + continue + path = os.path.join(dirpath, f).replace('\\', '/') + try: + with open(path, 'r', encoding='utf-8', errors='ignore') as fp: + content = fp.read() + except OSError: + continue + + pkg_match = re.search(r'package\s+([\w.]+)\s*;', content) + file_pkg = pkg_match.group(1) if pkg_match else '' + + imports = re.findall(r'import\s+(?:static\s+)?[\w.]+\s*;', content) + imports = [i for i in imports if '*;' not in i and '.*' not in i] + + body_start = find_class_body_start(content) + body = content[body_start:] + + redundant = [] + for imp in imports: + simple = get_simple_name(imp) + if not simple: + continue + imp_full = get_import_full(imp) + imp_pkg = get_import_package(imp) + if imp_pkg and imp_pkg == file_pkg: + redundant.append(imp_full) + continue + if not re.search(r'\b' + re.escape(simple) + r'\b', body): + redundant.append(imp_full) + + if redundant: + results.append((path, redundant)) + + for path, red in results: + print(f"{path} | {'; '.join(red)} | {len(red)}") + print("TOTAL_FILES:" + str(len(results))) + print("TOTAL_IMPORTS:" + str(sum(len(r[1]) for r in results))) + + +if __name__ == '__main__': + main()