Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 46 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,26 @@
这是一个为 Koishi 开发者提供支持的 VSCode 扩展,主要功能包括:
- `package.json` 中 `koishi` 字段的 JSON Schema 验证
- `koishi.yml` 配置文件的 YAML Schema 验证
- `koishi.yml` 的智能补全(字段 / 枚举 / 插件名)、悬停文档、跳转定义
- TypeScript/JavaScript 代码片段

## 目录结构
```
├── src/
│ ├── extension.ts # 扩展入口(当前为空)
│ ├── extension.ts # 扩展入口,注册各类语言功能 Provider
│ ├── schema/ # JSON Schema 索引:按配置路径解析 schema
│ │ ├── types.ts # SchemaNode / PathSegment 类型定义
│ │ └── index.ts # SchemaIndex:路径解析、属性枚举、枚举值提取
│ ├── yaml/
│ │ └── document.ts # koishi.yml 解析,光标偏移量 → 配置路径
│ ├── plugins/
│ │ ├── registry.ts # 内置官方插件清单 + 包名 → 配置名转换
│ │ └── resolver.ts # 扫描工作区 node_modules 合并已安装插件
│ ├── providers/
│ │ ├── completion.ts # 补全:字段 / 枚举 / 插件名
│ │ ├── hover.ts # 悬停:schema 文档 + 插件包信息
│ │ ├── definition.ts # 跳转定义:插件名 → package.json
│ │ └── roots.ts # 定位工作区目录
│ └── test/ # 测试文件
├── schemata/
│ ├── koishi-yml.yaml # koishi.yml 的 YAML Schema 定义
Expand All @@ -27,6 +41,26 @@
└── README.md # 文档
```

## 智能补全架构

三个 Provider 共享同一套「光标位置 → 配置路径」解析:

```
KoishiYamlDocument.cursorAt(offset) → { path, parentPath, onKey, word }
↓
SchemaIndex 按 path 查询 schema
```

- `path` — 光标所在的配置路径。光标在**键**上时是该键自身的路径;在**值**上是该值的路径。
- `parentPath` — 光标所在的最内层映射,即「新键可以出现」的层级。
- `onKey` — 区分光标在键上还是值上,决定补全给字段名还是枚举值。

注意事项:
- YAML 解析器无法表达「正在输入但尚未成键」的内容。`plugins:\n ada` 中的 `ada`
会被解析成 `plugins` 的标量值,因此 `cursorAt` 用缩进作为兜底信号来判断键位置。
- 空值槽(`prefixMode: |`)必须判定为值位置,否则枚举补全无法触发。
- 每次请求都会重新解析文档。配置文件规模下开销可忽略,也无需自行跟踪外部编辑。

## 核心工作流

### 1. Schema 更新流程
Expand Down Expand Up @@ -60,6 +94,15 @@ yarn run package # 生成 .vsix 包
## 技术栈
- TypeScript 4.9+
- VSCode Extension API
- js-yaml (YAML 解析)
- yaml (YAML 解析,带 offset 的 AST)
- js-yaml (仅构建期 `scripts/convert.ts` 使用)
- vsce (打包工具)
- ESLint + TypeScript ESLint
- ESLint + TypeScript ESLint

## 测试
```bash
yarn run pretest # compile + lint
yarn test # 启动 VSCode 实例运行 mocha 测试
```
测试分为三层:`schema.test.ts`(纯逻辑)、`plugins.test.ts`(纯逻辑 + 临时目录)、
`providers.test.ts`(在真实 VSCode 中调用各 Provider)。
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,26 @@ All notable changes to the "koishi-dev" extension will be documented in this fil

Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.

## [Unreleased]

### Added

- Add field, enum and plugin name completion for `koishi.yml`. Plugin names come
from a bundled snapshot of the official plugins merged with the plugins
installed in the current workspace.
- Add hover documentation for `koishi.yml` config fields, showing the schema
description, type, allowed values and constraints.
- Add hover for plugin entries in the `plugins` block, mapping a config name to
its npm package and declared services.
- Add go to definition from a plugin name to the installed package's
`package.json`.

### Fixed

- Compile `src/test` as part of `yarn run compile`, so `yarn test` can find the
test files it previously never built.
- Update the Mocha test runner for the current `glob` and `mocha` releases.

## [0.0.4] - 2024-02-23

### Added
Expand Down
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,28 @@ The extension is still **Work in Progress** currently.

- [x] Package.json schema for "koishi" field
- [x] YAML validation for `koishi.yml` file
- [x] Field, enum and plugin name completion in `koishi.yml`
- [x] Hover documentation for config fields and plugins
- [x] Go to definition from a plugin name to its `package.json`
- [x] JavaScript / TypeScript code snippets

### `koishi.yml` IntelliSense

Beyond structural validation, the extension provides language features for
`koishi.yml`:

- **Completion** — field names at the current level, enum values on the value
side of a field (`prefixMode`, `i18n.output`), and plugin names inside the
`plugins` block.
- **Plugin names** — a bundled snapshot of the official plugins is merged with
whatever is installed in your workspace, so third-party plugins appear too.
Hovering shows the npm package name and the services a plugin provides or
requires.
- **Hover** — the description, type, allowed values and numeric constraints
declared in the schema.
- **Go to definition** — from a plugin name to that package's `package.json` in
`node_modules`.

## License

This extension is licensed under the [AGPL-3.0](https://www.gnu.org/licenses/agpl-3.0.en.html) license.
16 changes: 12 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"convert": "tsx ./scripts/convert.ts",
"watch": "tsc -watch -p ./",
"lint": "eslint src",
"test": "yarn run compile && yarn run lint && node ./out/test/runTest.js",
"test": "yarn run compile && yarn run convert && yarn run lint && node ./out/test/runTest.js",
"package": "vsce package --no-yarn"
},
"repository": {
Expand All @@ -33,15 +33,20 @@
"icon": "koishi.png",
"categories": [
"Snippets",
"Linters"
"Linters",
"Programming Languages"
],
"keywords": [
"koishi",
"develop",
"json schema",
"snippets"
"snippets",
"yaml",
"completion"
],
"activationEvents": [
"onLanguage:yaml"
],
"activationEvents": [],
"main": "./out/extension.js",
"contributes": {
"jsonValidation": [
Expand Down Expand Up @@ -75,6 +80,9 @@
}
]
},
"dependencies": {
"yaml": "^2.9.1"
},
"devDependencies": {
"@eslint/js": "^9.25.0",
"@types/glob": "^9.0.0",
Expand Down
36 changes: 33 additions & 3 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,40 @@
import * as vscode from 'vscode'

export function activate(_context: vscode.ExtensionContext): void {
// Extension activated
import { KOISHI_YML_SCHEMA_PATH, SchemaIndex } from './schema'
import { PluginResolver } from './plugins/resolver'
import { KoishiCompletionProvider } from './providers/completion'
import { KoishiHoverProvider } from './providers/hover'
import { KoishiDefinitionProvider } from './providers/definition'

/** Documents this extension contributes language intelligence for. */
export const KOISHI_YML_SELECTOR: vscode.DocumentSelector = [
{ language: 'yaml', scheme: 'file', pattern: '**/koishi.yml' },
{ language: 'yaml', scheme: 'untitled', pattern: '**/koishi.yml' },
]

export function activate(context: vscode.ExtensionContext): void {
const schema = SchemaIndex.fromFile(KOISHI_YML_SCHEMA_PATH)
const plugins = new PluginResolver()

context.subscriptions.push(
vscode.languages.registerCompletionItemProvider(
KOISHI_YML_SELECTOR,
new KoishiCompletionProvider(schema, plugins),
...KoishiCompletionProvider.triggerCharacters,
),
vscode.languages.registerHoverProvider(
KOISHI_YML_SELECTOR,
new KoishiHoverProvider(schema, plugins),
),
vscode.languages.registerDefinitionProvider(
KOISHI_YML_SELECTOR,
new KoishiDefinitionProvider(plugins),
),
)

console.log('Koishi Dev extension is now active')
}

export function deactivate(): void {
// Extension deactivated
// Nothing to tear down: all resources live in the extension context.
}
Loading
Loading