Files

12 KiB
Raw Permalink Blame History

kotlin-language-server 使用指南

通过 Serena MCP 工具调用 kotlin-language-server 进行 Kotlin/Android 代码分析

前置条件

1. 确保 kotlin-language-server 已安装

kotlin-language-server 可通过以下方式安装:

# 通过 Homebrew (macOS)
brew install kotlin-language-server

# 或通过 VS Code Kotlin 扩展
# 扩展会自动安装 language server

2. 项目结构要求

kotlin-language-server 需要以下文件来识别 Android/Kotlin 项目:

  • Gradle: build.gradle 或 build.gradle.kts
  • Settings: settings.gradle 或 settings.gradle.kts
  • Android: app/build.gradle.kts (通常包含 android {} 配置)

通过 Serena 使用 kotlin-language-server

激活项目

# Step 1: 激活 Kotlin/Android 项目
tool: mcp__serena__activate_project
params:
  project: "/path/to/android/project"

# 返回: 项目已激活,kotlin-language-server 已初始化

验证配置

# 检查当前配置
tool: mcp__serena__get_current_config

# 确认输出包含:
# - active_project: /path/to/android/project
# - language_server: kotlin-language-server

符号提取操作

获取文件符号概览

# 获取单个文件的符号列表(不含代码体)
tool: mcp__serena__get_symbols_overview
params:
  relative_path: "app/src/main/java/com/example/ui/home/HomeVm.kt"
  depth: 1      # 0=仅顶层, 1=含直接成员, 2=含嵌套成员

# 返回示例:
# Classes:
#   - HomeVm (class) [28-307]
# Properties:
#   - repository (property) [29-29]
#   - listeningState (property) [34-34]
#   - homePolymerization (property) [39-39]
# Methods:
#   - getListeningState (method) [59-74]
#   - changeListeningState (method) [76-102]
#   - getHomeInfoData (method) [116-142]

查找符号

# 按名称模式查找符号
tool: mcp__serena__find_symbol
params:
  name_path_pattern: "HomeVm"
  relative_path: "app/src/main/java/"
  include_body: false
  include_info: true
  depth: 1

# 名称模式规则 (与 Java 相同):
# - "HomeVm"                -> 匹配任何包含此名称的符号
# - "home/HomeVm"           -> 匹配此路径后缀
# - "/com.example.HomeVm"   -> 精确匹配完整路径
# - "HomeVm/getListeningState" -> 匹配类中的方法

获取符号详情(含代码体)

# 获取完整的符号定义(含代码)
tool: mcp__serena__find_symbol
params:
  name_path_pattern: "HomeRepository/getHomeInfoData"
  relative_path: "app/src/main/java/com/example/ui/home/HomeRepository.kt"
  include_body: true
  depth: 0

# 返回包含:
# - 方法签名
# - KDoc 注释
# - 完整方法体
# - 行号范围

引用分析操作

查找符号引用

# 查找某个符号被哪些地方引用
tool: mcp__serena__find_referencing_symbols
params:
  name_path: "HomeRepository/getHomeInfoData"
  relative_path: "app/src/main/java/com/example/ui/home/HomeRepository.kt"
  include_info: true

# 返回示例:
# References (2 found):
#   - HomeVm.getHomeInfoData [117:12-117:42]
#     snippet: "repository.getHomeInfoData()"

模式搜索(补充 LSP)

# 当 LSP 无法找到时,使用模式搜索
tool: mcp__serena__search_for_pattern
params:
  substring_pattern: "repository\\.get"
  paths_include_glob: "**/*.kt"
  restrict_search_to_code_files: true
  context_lines_before: 2
  context_lines_after: 2

# 适用场景:
# - 协程调用 (withContext, launch)
# - 扩展函数调用
# - 动态代理

Kotlin 特有处理

Kotlin 特有关键字与结构

# Kotlin 特有符号类型
data class:
  pattern: "data class \\w+"
  description: "Kotlin 数据类,自动生成 equals/hashCode/copy"

sealed class:
  pattern: "sealed class \\w+"
  description: "密封类,限制继承的类型"

object:
  pattern: "object \\w+"
  description: "单例对象声明"

companion object:
  pattern: "companion object"
  description: "伴生对象,类似静态成员"

suspend fun:
  pattern: "suspend fun \\w+"
  description: "协程挂起函数"

extension function:
  pattern: "fun \\w+\\.\\w+"
  description: "扩展函数"

Android MVVM 架构层识别

# 根据命名约定和基类识别架构层

ViewModel 层:
  naming: "*Vm.kt", "*ViewModel.kt"
  base_classes:
    - "ViewModel"
    - "AndroidViewModel"
    - "BaseViewModel"
  annotations:
    - "@HiltViewModel"
  key_elements:
    - "MutableLiveData"
    - "MutableStateFlow"
    - "viewModelScope"

Repository 层:
  naming: "*Repository.kt"
  base_classes:
    - "BaseRepository"
  key_elements:
    - "suspend fun"
    - "Flow<>"
    - "RetrofitHelper"

Activity 层:
  naming: "*Activity.kt"
  base_classes:
    - "AppCompatActivity"
    - "ComponentActivity"
    - "BaseActivity"
  key_elements:
    - "setContentView"
    - "ViewBinding"

Fragment 层:
  naming: "*Fragment.kt"
  base_classes:
    - "Fragment"
    - "DialogFragment"
    - "BaseFragment"
  key_elements:
    - "onCreateView"
    - "ViewBinding"

Compose UI:
  naming: "*Page.kt", "*Compose.kt", "*Screen.kt"
  annotations:
    - "@Composable"
  key_elements:
    - "remember"
    - "LaunchedEffect"
    - "collectAsState"

Adapter 层:
  naming: "*Adapter.kt"
  base_classes:
    - "RecyclerView.Adapter"
    - "ListAdapter"
    - "BaseAdapter"
  key_elements:
    - "onCreateViewHolder"
    - "onBindViewHolder"

Retrofit API 识别

# Retrofit 注解识别

HTTP 方法注解:
  - "@GET": GET 请求
  - "@POST": POST 请求
  - "@PUT": PUT 请求
  - "@DELETE": DELETE 请求
  - "@PATCH": PATCH 请求
  - "@HEAD": HEAD 请求
  - "@OPTIONS": OPTIONS 请求

参数注解:
  - "@Path": 路径参数
  - "@Query": 查询参数
  - "@QueryMap": 查询参数 Map
  - "@Body": 请求体
  - "@Field": 表单字段
  - "@FieldMap": 表单字段 Map
  - "@Part": Multipart 部分
  - "@Header": 请求头
  - "@HeaderMap": 请求头 Map
  - "@Url": 动态 URL

# 提取示例
tool: mcp__serena__search_for_pattern
params:
  substring_pattern: "@(GET|POST|PUT|DELETE|PATCH)\\s*\\([^)]*\\)"
  paths_include_glob: "**/*Service.kt"

# 解析端点
# @GET("/client-driver/v2/order/getOrderDetail")
# fun getOrderDetail(@Query("orderId") orderId: Long?): Observable<BaseResponse<OrderDetailBean>>
#
# 提取:
#   method: GET
#   path: /client-driver/v2/order/getOrderDetail
#   params: [{name: orderId, type: Long?, annotation: @Query}]
#   return_type: Observable<BaseResponse<OrderDetailBean>>

Jetpack Compose 识别

# Compose 注解和模式

@Composable:
  description: "可组合函数,Compose UI 构建块"
  pattern: "@Composable\\s+(fun|private fun|internal fun)"

@Preview:
  description: "预览注解,用于 Android Studio 预览"

State 管理:
  patterns:
    - "remember\\s*\\{"
    - "mutableStateOf"
    - "collectAsState"
    - "LaunchedEffect"
    - "SideEffect"
    - "DisposableEffect"

# 提取 Composable 函数
tool: mcp__serena__search_for_pattern
params:
  substring_pattern: "@Composable\\s+fun\\s+\\w+"
  paths_include_glob: "**/*.kt"

RxJava/Coroutines 模式

# RxJava 模式(该项目使用)
rx_patterns:
  - "Observable<"
  - "subscribeOn"
  - "observeOn"
  - "subscribe"
  - "Schedulers.io()"
  - "AndroidSchedulers.mainThread()"

# Coroutines 模式
coroutine_patterns:
  - "suspend fun"
  - "viewModelScope.launch"
  - "lifecycleScope.launch"
  - "withContext"
  - "async"
  - "await"
  - "Flow<"
  - "StateFlow<"
  - "SharedFlow<"
  - "collect"
  - "collectLatest"

LSP SymbolKind 映射

# kotlin-language-server 返回的 SymbolKind 数值对照

1: File
2: Module
3: Namespace
4: Package
5: Class           # 类、object
6: Method          # 方法
7: Property        # 属性
8: Field           # 字段
9: Constructor     # 构造函数
10: Enum           # 枚举
11: Interface      # 接口
12: Function       # 顶层函数
13: Variable       # 变量
14: Constant       # 常量 (val)
15: String
16: Number
17: Boolean
18: Array
19: Object         # object 声明
20: Key
21: Null
22: EnumMember     # 枚举值
23: Struct
24: Event
25: Operator
26: TypeParameter  # 泛型参数

调用链构建

ViewModel -> Repository 调用链

# 伪代码: 构建 MVVM 调用链

def build_mvvm_chain(viewmodel_path):
    # Step 1: 获取 ViewModel 符号
    vm_symbols = get_symbols_overview(viewmodel_path, depth=2)

    # Step 2: 识别 Repository 依赖
    # 查找类似: private val repository = HomeRepository()
    repo_deps = extract_repository_deps(vm_symbols)

    # Step 3: 追踪 Repository 方法调用
    for method in vm_symbols.methods:
        # 获取方法体
        method_body = find_symbol(method.name, include_body=True)

        # 查找 repository.xxx() 调用
        repo_calls = extract_repo_calls(method_body)

        for call in repo_calls:
            # 跟踪到 Repository 方法
            repo_method = find_symbol(f"{repo_deps}/{call.method_name}")

            # Repository 通常调用 RetrofitHelper
            api_calls = extract_api_calls(repo_method.body)

    return chain

Repository -> API Service 调用链

# 实际操作步骤

# 1. 获取 Repository 方法
tool: mcp__serena__find_symbol
params:
  name_path_pattern: "HomeRepository/getHomeInfoData"
  relative_path: "app/src/main/java/"
  include_body: true

# 2. 分析方法体中的 API 调用
# 从返回的 body 中提取: RetrofitHelper.getDefaultService().getHomeInfoData()

# 3. 定位 API 接口定义
tool: mcp__serena__search_for_pattern
params:
  substring_pattern: "fun getHomeInfoData"
  paths_include_glob: "**/*Service.kt"

# 4. 提取端点信息
# @GET("/client-driver/v3/info/getHomeInfoData")
# fun getHomeInfoData(): Observable<BaseResponse<HomePolymerizationBean>>

常见问题

Q: kotlin-language-server 启动慢

A: 首次打开项目时,需要构建 Gradle 项目并索引。
   - 大型 Android 项目可能需要几分钟
   - 确保 Gradle Daemon 正在运行
   建议: 先执行 ./gradlew build 预热项目

Q: 找不到 Kotlin 符号

A: 可能原因:
   1. Gradle 配置未正确解析
   2. 使用了 Kotlin DSL 但 settings.gradle.kts 缺失
   3. 符号在 generated 目录中
   4. 项目使用 Kotlin Multiplatform

解决:
   1. 确保项目可以正常 Gradle 编译
   2. 使用 search_for_pattern 作为备选
   3. 检查 exclude_patterns 配置

Q: 协程/Flow 调用追踪不完整

A: 协程的异步特性使得静态分析困难:
   1. viewModelScope.launch {} 内部调用难以追踪
   2. Flow 的 collect 在不同协程作用域
   3. 挂起函数的调用栈可能中断

解决:
   使用 search_for_pattern 搜索特定调用模式
   结合 @Composable 中的 LaunchedEffect 分析

Q: Compose 函数调用关系

A: Compose 函数组合特殊:
   1. @Composable 函数只能被其他 @Composable 调用
   2. 状态提升模式使数据流向不明显
   3. remember/LaunchedEffect 的依赖追踪

解决:
   1. 先识别所有 @Composable 函数
   2. 分析函数参数中的回调 lambda
   3. 追踪 ViewModel 的 StateFlow/LiveData

最佳实践

  1. 按架构层分析 - 先识别 MVVM 各层文件,再逐层深入
  2. Repository 是关键 - Repository 层连接 ViewModel 和 API,是分析重点
  3. 注解驱动识别 - 利用 @Composable, @GET/@POST 等注解快速分类
  4. 命名约定优先 - Kotlin/Android 项目通常遵循严格命名约定 (*Vm, *Repository, *Activity)
  5. 结合 Gradle 分析 - 通过 build.gradle.kts 了解依赖和模块结构
  6. 注意扩展函数 - Kotlin 扩展函数可能分散在不同文件中

项目特定模式 (chefu_driver_app_android)

该项目使用的技术栈和模式:

架构模式: MVVM
  ViewModel: *Vm.kt (如 HomeVm, LoginViewMode)
  Repository: *Repository.kt (如 HomeRepository)
  Activity: *Activity.kt (如 HomeActivity)
  Fragment: *Fragment.kt (如 RankDetailFragment)

网络层: Retrofit + RxJava3
  ApiService: com.dezhong.driverandroid.net.ApiService
  Helper: RetrofitHelper.getDefaultService()
  响应类型: Observable<BaseResponse<T>>

UI 层:
  传统: XML + ViewBinding
  新版: Jetpack Compose (myCompose/ 目录)

状态管理:
  ViewModel: MutableLiveData<T>
  Compose: mutableStateOf, remember

目录结构:
  app/src/main/java/com/dezhong/driverandroid/
    ui/           # 传统 UI (Activity + Fragment + ViewModel)
    myCompose/    # Compose UI
    net/          # 网络层
    base/         # 基类
    service/      # 后台服务
    common/       # 通用类