@spaceflow/review 4.0.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/README.md +219 -0
- package/package.json +3 -3
- package/src/coverage/base.css +224 -0
- package/src/coverage/block-navigation.js +87 -0
- package/src/coverage/clover.xml +1942 -0
- package/src/coverage/coverage-final.json +7 -0
- package/src/coverage/favicon.png +0 -0
- package/src/coverage/index.html +131 -0
- package/src/coverage/prettify.css +1 -0
- package/src/coverage/prettify.js +2 -0
- package/src/coverage/sort-arrow-sprite.png +0 -0
- package/src/coverage/sorter.js +210 -0
- package/src/coverage/src/deletion-impact.service.ts.html +2716 -0
- package/src/coverage/src/index.html +161 -0
- package/src/coverage/src/issue-verify.service.ts.html +1006 -0
- package/src/coverage/src/parse-title-options.ts.html +640 -0
- package/src/coverage/src/review-spec/index.html +131 -0
- package/src/coverage/src/review-spec/review-spec.service.ts.html +2782 -0
- package/src/coverage/src/review-spec/types.ts.html +535 -0
- package/src/coverage/src/review.service.ts.html +8911 -0
- package/src/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [4.0.1](https://github.com/Lydanne/spaceflow/compare/@spaceflow/review@4.0.0...@spaceflow/review@4.0.1) (2026-04-13)
|
|
4
|
+
|
|
5
|
+
### 新特性
|
|
6
|
+
|
|
7
|
+
* **publish:** 限制 Bumper 仅分析当前包目录的 commit ([f01166e](https://github.com/Lydanne/spaceflow/commit/f01166e8da38c836660e61e8456958df4e0cffef))
|
|
8
|
+
|
|
9
|
+
### 修复BUG
|
|
10
|
+
|
|
11
|
+
* **review:** 补充审查规范格式、流程、问题生命周期和输出格式文档 ([0c6987e](https://github.com/Lydanne/spaceflow/commit/0c6987ee7eb7f3c5996a5eeb50f6d672cc5f328a))
|
|
12
|
+
|
|
13
|
+
### 其他修改
|
|
14
|
+
|
|
15
|
+
* **publish:** released version 1.1.0 [no ci] ([5542404](https://github.com/Lydanne/spaceflow/commit/5542404cb3bb3b3bb57ac9eabeed5f442b41e005))
|
|
16
|
+
* **review-summary:** released version 4.0.0 [no ci] ([29a05d1](https://github.com/Lydanne/spaceflow/commit/29a05d101a16fc6de20263b795f85b1d836e60cf))
|
|
17
|
+
|
|
18
|
+
## [4.0.0](https://github.com/Lydanne/spaceflow/compare/@spaceflow/review@3.0.0...@spaceflow/review@4.0.0) (2026-04-13)
|
|
19
|
+
|
|
20
|
+
### ⚠ BREAKING CHANGES
|
|
21
|
+
|
|
22
|
+
* **publish:** 修复 monorepo 格式 tag 识别问题,避免错误的 major bump
|
|
23
|
+
|
|
24
|
+
### 新特性
|
|
25
|
+
|
|
26
|
+
* **publish:** 修复 monorepo 格式 tag 识别问题,避免错误的 major bump ([6379b71](https://github.com/Lydanne/spaceflow/commit/6379b71f5694a814f40866020bc8dd7614399e83))
|
|
27
|
+
|
|
28
|
+
### 修复BUG
|
|
29
|
+
|
|
30
|
+
* **review:** 更新 README,补充本地审查、MCP 工具、系统规则等新功能说明 ([ee6942f](https://github.com/Lydanne/spaceflow/commit/ee6942fe9043aff479f0ae4d6cff1623cc8df80f))
|
|
31
|
+
|
|
32
|
+
### 其他修改
|
|
33
|
+
|
|
34
|
+
* **publish:** released version 1.0.0 [no ci] ([ef8afbf](https://github.com/Lydanne/spaceflow/commit/ef8afbf82aae414555b8923e068ae3f7702d30e8))
|
|
35
|
+
* **review-summary:** released version 3.0.0 [no ci] ([b344696](https://github.com/Lydanne/spaceflow/commit/b344696ffaaef7957cf04e34fec5ae838982aeb8))
|
|
36
|
+
|
|
3
37
|
## [3.0.0](https://github.com/Lydanne/spaceflow/compare/@spaceflow/review@2.0.0...@spaceflow/review@3.0.0) (2026-04-13)
|
|
4
38
|
|
|
5
39
|
### 代码重构
|
package/README.md
CHANGED
|
@@ -159,6 +159,225 @@ spaceflow review -p 123 --fail-on-issues -l openai
|
|
|
159
159
|
|
|
160
160
|
规则搜索目录包括:`review.references` 配置路径、`.claude/skills`、`.cursor/skills`、`review-specs`。
|
|
161
161
|
|
|
162
|
+
## 审查规范格式
|
|
163
|
+
|
|
164
|
+
审查规范使用 Markdown 文件定义,文件名格式为 `<extensions>.<type>.md`,放置在 `references/` 目录或远程仓库中。
|
|
165
|
+
|
|
166
|
+
### 文件名约定
|
|
167
|
+
|
|
168
|
+
```
|
|
169
|
+
<extensions>.<type>.md
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
- **extensions** — 适用的文件扩展名,多个用 `&` 连接(如 `js&ts`、`vue`)
|
|
173
|
+
- **type** — 规范类别(如 `base`、`file-name`、`nest`)
|
|
174
|
+
|
|
175
|
+
示例:`js&ts.base.md` → 适用于 `.js` 和 `.ts` 文件的基础规范
|
|
176
|
+
|
|
177
|
+
### 规则定义格式
|
|
178
|
+
|
|
179
|
+
每条规则使用 `##` 或 `###` 标题,后跟规则 ID(用反引号方括号包裹):
|
|
180
|
+
|
|
181
|
+
```markdown
|
|
182
|
+
## 规则标题 `[RuleId]`
|
|
183
|
+
|
|
184
|
+
规则描述文本...
|
|
185
|
+
|
|
186
|
+
> - severity `warn`
|
|
187
|
+
> - override `[OverriddenRuleId]`
|
|
188
|
+
> - includes `*.service.ts` `*.controller.ts`
|
|
189
|
+
|
|
190
|
+
### Example: 示例标题
|
|
191
|
+
|
|
192
|
+
#### Good: 正确示例
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
const MAX_COUNT = 100;
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
#### Bad: 错误示例
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
const maxCount = 100;
|
|
202
|
+
```
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### 规则配置项
|
|
206
|
+
|
|
207
|
+
在规则描述中使用引用块(`>`)声明配置:
|
|
208
|
+
|
|
209
|
+
| 配置项 | 格式 | 说明 |
|
|
210
|
+
| ------------ | ----------------------------- | ------------------------------------------------------------ |
|
|
211
|
+
| `severity` | `> - severity \`warn\`` | 严重级别(`off` / `warn` / `error`),默认 `error` |
|
|
212
|
+
| `override` | `> - override \`[RuleId]\`` | 覆盖指定规则(前缀匹配),被覆盖的规则问题会被过滤 |
|
|
213
|
+
| `includes` | `> - includes \`*.ts\` \`*.js\`` | 文件匹配模式,仅对匹配的文件生效,支持 `status|glob` 前缀 |
|
|
214
|
+
|
|
215
|
+
### Override 机制
|
|
216
|
+
|
|
217
|
+
Override 允许高优先级规则覆盖低优先级规则,避免重复报告:
|
|
218
|
+
|
|
219
|
+
- 使用前缀匹配:`override [JsTs.FileName]` 会覆盖 `JsTs.FileName` 及其子规则(如 `JsTs.FileName.UpperCamel`)
|
|
220
|
+
- 作用域感知:只有当 issue 文件匹配 override 所在 spec 的 `includes` 时才生效
|
|
221
|
+
- 文件级 override 写在规范文件头部(第一个 `##` 规则之前),对所有规则生效
|
|
222
|
+
|
|
223
|
+
### 远程规范引用
|
|
224
|
+
|
|
225
|
+
`references` 支持以下格式:
|
|
226
|
+
|
|
227
|
+
```json
|
|
228
|
+
{
|
|
229
|
+
"review": {
|
|
230
|
+
"references": [
|
|
231
|
+
"./references", // 本地目录
|
|
232
|
+
"https://github.com/org/repo/tree/main/specs", // GitHub 仓库目录
|
|
233
|
+
"https://gitea.example.com/org/repo/src/branch/main/specs" // Gitea 仓库目录
|
|
234
|
+
]
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
拉取优先级:Git Provider API → tea CLI → git clone 回退 → 本地缓存
|
|
240
|
+
|
|
241
|
+
## 审查流程
|
|
242
|
+
|
|
243
|
+
```
|
|
244
|
+
1. 解析上下文(PR/分支/本地模式) → ReviewContext
|
|
245
|
+
2. 加载审查规范(本地 + 远程) → ReviewSpec[]
|
|
246
|
+
3. 获取变更文件和代码内容
|
|
247
|
+
4. 系统规则检查(静态,不依赖 LLM)
|
|
248
|
+
5. LLM 并行审查(按文件分发,注入匹配的规范)
|
|
249
|
+
6. 问题过滤(去重、includes、override、非变更行)
|
|
250
|
+
7. 历史问题验证(可选,verifyFixes)
|
|
251
|
+
8. 删除代码分析(可选,analyzeDeletions)
|
|
252
|
+
9. AI 生成 PR 描述(可选,generateDescription)
|
|
253
|
+
10. 输出结果(PR 评论 / 终端 / JSON)
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### 参数优先级
|
|
257
|
+
|
|
258
|
+
命令行 > PR 标题参数 > 配置文件 > 默认值
|
|
259
|
+
|
|
260
|
+
## 问题生命周期
|
|
261
|
+
|
|
262
|
+
每个审查问题(Issue)有以下状态流转:
|
|
263
|
+
|
|
264
|
+
```
|
|
265
|
+
发现 → 待处理 → 已修复 / 已解决 / 无效
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
| 状态 | 标记 | 说明 |
|
|
269
|
+
| ---------- | ----------- | -------------------------------------- |
|
|
270
|
+
| 待处理 | 🔴/🟡 | 新发现的问题,尚未处理 |
|
|
271
|
+
| 已修复 | 🟢 | AI 验证代码已修改,问题不再存在 |
|
|
272
|
+
| 已解决 | ⚪ | 用户手动点击 resolve |
|
|
273
|
+
| 无效 | ❌ | AI 验证或用户标记为误报 |
|
|
274
|
+
|
|
275
|
+
严重级别对应的 Emoji:
|
|
276
|
+
|
|
277
|
+
| 级别 | Emoji | 说明 |
|
|
278
|
+
| ------ | ----- | ------------ |
|
|
279
|
+
| error | 🔴 | 必须修复 |
|
|
280
|
+
| warn | 🟡 | 建议修复 |
|
|
281
|
+
| off | ⚪ | 规则已关闭 |
|
|
282
|
+
|
|
283
|
+
### 增量审查
|
|
284
|
+
|
|
285
|
+
多次运行 review 时自动追踪问题状态:
|
|
286
|
+
|
|
287
|
+
- **轮次(round)** — 每次审查递增,记录问题在哪一轮发现
|
|
288
|
+
- **去重** — 相同文件+行号+规则的问题不会重复报告
|
|
289
|
+
- **行号追踪** — 代码变更导致行号移动时自动更新 `originalLine`
|
|
290
|
+
- **变更文件处理** — `invalidateChangedFiles` 控制变更文件的历史问题是否标记为无效
|
|
291
|
+
|
|
292
|
+
## Includes 模式语法
|
|
293
|
+
|
|
294
|
+
`includes` 配置支持 glob 模式和变更类型前缀:
|
|
295
|
+
|
|
296
|
+
### 基本语法
|
|
297
|
+
|
|
298
|
+
```json
|
|
299
|
+
{
|
|
300
|
+
"includes": ["**/*.ts", "!**/*.spec.ts", "!**/*.config.ts"]
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
- `!` 前缀表示排除模式
|
|
305
|
+
- 使用 [micromatch](https://github.com/micromatch/micromatch) 匹配
|
|
306
|
+
|
|
307
|
+
### 变更类型前缀
|
|
308
|
+
|
|
309
|
+
`<status>|<glob>` 语法,仅匹配指定变更类型的文件:
|
|
310
|
+
|
|
311
|
+
| 前缀 | 说明 | 示例 |
|
|
312
|
+
| ----------- | ------ | ------------------------- |
|
|
313
|
+
| `added\|` | 新增文件 | `added\|**/*.ts` |
|
|
314
|
+
| `modified\|` | 修改文件 | `modified\|**/*.ts` |
|
|
315
|
+
| `deleted\|` | 删除文件 | `deleted\|**/*.ts` |
|
|
316
|
+
|
|
317
|
+
无前缀则不限变更类型。
|
|
318
|
+
|
|
319
|
+
> 注意:文件的 status 是相对 base 分支的全量 diff 结果。例如在当前分支首次引入的文件,无论后续多少次 commit 修改,其 status 始终为 `added`。
|
|
320
|
+
|
|
321
|
+
### whenModifiedCode 过滤
|
|
322
|
+
|
|
323
|
+
`whenModifiedCode` 配置可进一步缩小审查范围,只关注特定代码结构:
|
|
324
|
+
|
|
325
|
+
```json
|
|
326
|
+
{
|
|
327
|
+
"whenModifiedCode": ["function", "class"]
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
支持的类型:`function`、`class`、`interface`、`type`、`method`
|
|
332
|
+
|
|
333
|
+
配置后,LLM 只会收到匹配代码块范围内的代码,其余代码被裁剪。
|
|
334
|
+
|
|
335
|
+
## 输出格式
|
|
336
|
+
|
|
337
|
+
### markdown(默认 PR 模式)
|
|
338
|
+
|
|
339
|
+
发布为 PR Review Comment,包含:
|
|
340
|
+
|
|
341
|
+
- 按文件分组的问题列表(含严重级别 Emoji)
|
|
342
|
+
- 每个问题的规则 ID、发现时间、开发人员
|
|
343
|
+
- 修复建议代码片段
|
|
344
|
+
- 文件摘要和统计信息
|
|
345
|
+
- 隐藏的 JSON 数据区(用于增量审查状态追踪)
|
|
346
|
+
|
|
347
|
+
### terminal
|
|
348
|
+
|
|
349
|
+
终端彩色输出,适合本地审查:
|
|
350
|
+
|
|
351
|
+
- 🟢 已修复 / 🔴 待处理 error / 🟡 待处理 warn / ⚪ 已解决
|
|
352
|
+
- 按文件分组的摘要和问题详情
|
|
353
|
+
|
|
354
|
+
### json
|
|
355
|
+
|
|
356
|
+
原始 `ReviewResult` JSON 结构化输出,适合程序化处理:
|
|
357
|
+
|
|
358
|
+
```json
|
|
359
|
+
{
|
|
360
|
+
"success": true,
|
|
361
|
+
"title": "AI 生成的 PR 标题",
|
|
362
|
+
"description": "PR 功能总结",
|
|
363
|
+
"issues": [...],
|
|
364
|
+
"summary": [...],
|
|
365
|
+
"deletionImpact": {...},
|
|
366
|
+
"round": 2,
|
|
367
|
+
"headSha": "abc1234",
|
|
368
|
+
"stats": {
|
|
369
|
+
"total": 10,
|
|
370
|
+
"validTotal": 8,
|
|
371
|
+
"fixed": 3,
|
|
372
|
+
"resolved": 2,
|
|
373
|
+
"invalid": 2,
|
|
374
|
+
"pending": 3,
|
|
375
|
+
"fixRate": 37.5,
|
|
376
|
+
"resolveRate": 25
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
162
381
|
## PR 标题参数
|
|
163
382
|
|
|
164
383
|
支持在 PR 标题末尾添加参数覆盖默认配置:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spaceflow/review",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"description": "Spaceflow 代码审查插件,使用 LLM 对 PR 代码进行自动审查",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Lydanne",
|
|
@@ -25,10 +25,10 @@
|
|
|
25
25
|
"@vitest/coverage-v8": "^4.0.18",
|
|
26
26
|
"unplugin-swc": "^1.5.9",
|
|
27
27
|
"vitest": "^4.0.18",
|
|
28
|
-
"@spaceflow/cli": "0.
|
|
28
|
+
"@spaceflow/cli": "5.0.0"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
|
-
"@spaceflow/core": "0.
|
|
31
|
+
"@spaceflow/core": "5.0.0"
|
|
32
32
|
},
|
|
33
33
|
"spaceflow": {
|
|
34
34
|
"type": "flow",
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
body, html {
|
|
2
|
+
margin:0; padding: 0;
|
|
3
|
+
height: 100%;
|
|
4
|
+
}
|
|
5
|
+
body {
|
|
6
|
+
font-family: Helvetica Neue, Helvetica, Arial;
|
|
7
|
+
font-size: 14px;
|
|
8
|
+
color:#333;
|
|
9
|
+
}
|
|
10
|
+
.small { font-size: 12px; }
|
|
11
|
+
*, *:after, *:before {
|
|
12
|
+
-webkit-box-sizing:border-box;
|
|
13
|
+
-moz-box-sizing:border-box;
|
|
14
|
+
box-sizing:border-box;
|
|
15
|
+
}
|
|
16
|
+
h1 { font-size: 20px; margin: 0;}
|
|
17
|
+
h2 { font-size: 14px; }
|
|
18
|
+
pre {
|
|
19
|
+
font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
|
20
|
+
margin: 0;
|
|
21
|
+
padding: 0;
|
|
22
|
+
-moz-tab-size: 2;
|
|
23
|
+
-o-tab-size: 2;
|
|
24
|
+
tab-size: 2;
|
|
25
|
+
}
|
|
26
|
+
a { color:#0074D9; text-decoration:none; }
|
|
27
|
+
a:hover { text-decoration:underline; }
|
|
28
|
+
.strong { font-weight: bold; }
|
|
29
|
+
.space-top1 { padding: 10px 0 0 0; }
|
|
30
|
+
.pad2y { padding: 20px 0; }
|
|
31
|
+
.pad1y { padding: 10px 0; }
|
|
32
|
+
.pad2x { padding: 0 20px; }
|
|
33
|
+
.pad2 { padding: 20px; }
|
|
34
|
+
.pad1 { padding: 10px; }
|
|
35
|
+
.space-left2 { padding-left:55px; }
|
|
36
|
+
.space-right2 { padding-right:20px; }
|
|
37
|
+
.center { text-align:center; }
|
|
38
|
+
.clearfix { display:block; }
|
|
39
|
+
.clearfix:after {
|
|
40
|
+
content:'';
|
|
41
|
+
display:block;
|
|
42
|
+
height:0;
|
|
43
|
+
clear:both;
|
|
44
|
+
visibility:hidden;
|
|
45
|
+
}
|
|
46
|
+
.fl { float: left; }
|
|
47
|
+
@media only screen and (max-width:640px) {
|
|
48
|
+
.col3 { width:100%; max-width:100%; }
|
|
49
|
+
.hide-mobile { display:none!important; }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.quiet {
|
|
53
|
+
color: #7f7f7f;
|
|
54
|
+
color: rgba(0,0,0,0.5);
|
|
55
|
+
}
|
|
56
|
+
.quiet a { opacity: 0.7; }
|
|
57
|
+
|
|
58
|
+
.fraction {
|
|
59
|
+
font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
|
|
60
|
+
font-size: 10px;
|
|
61
|
+
color: #555;
|
|
62
|
+
background: #E8E8E8;
|
|
63
|
+
padding: 4px 5px;
|
|
64
|
+
border-radius: 3px;
|
|
65
|
+
vertical-align: middle;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
div.path a:link, div.path a:visited { color: #333; }
|
|
69
|
+
table.coverage {
|
|
70
|
+
border-collapse: collapse;
|
|
71
|
+
margin: 10px 0 0 0;
|
|
72
|
+
padding: 0;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
table.coverage td {
|
|
76
|
+
margin: 0;
|
|
77
|
+
padding: 0;
|
|
78
|
+
vertical-align: top;
|
|
79
|
+
}
|
|
80
|
+
table.coverage td.line-count {
|
|
81
|
+
text-align: right;
|
|
82
|
+
padding: 0 5px 0 20px;
|
|
83
|
+
}
|
|
84
|
+
table.coverage td.line-coverage {
|
|
85
|
+
text-align: right;
|
|
86
|
+
padding-right: 10px;
|
|
87
|
+
min-width:20px;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
table.coverage td span.cline-any {
|
|
91
|
+
display: inline-block;
|
|
92
|
+
padding: 0 5px;
|
|
93
|
+
width: 100%;
|
|
94
|
+
}
|
|
95
|
+
.missing-if-branch {
|
|
96
|
+
display: inline-block;
|
|
97
|
+
margin-right: 5px;
|
|
98
|
+
border-radius: 3px;
|
|
99
|
+
position: relative;
|
|
100
|
+
padding: 0 4px;
|
|
101
|
+
background: #333;
|
|
102
|
+
color: yellow;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.skip-if-branch {
|
|
106
|
+
display: none;
|
|
107
|
+
margin-right: 10px;
|
|
108
|
+
position: relative;
|
|
109
|
+
padding: 0 4px;
|
|
110
|
+
background: #ccc;
|
|
111
|
+
color: white;
|
|
112
|
+
}
|
|
113
|
+
.missing-if-branch .typ, .skip-if-branch .typ {
|
|
114
|
+
color: inherit !important;
|
|
115
|
+
}
|
|
116
|
+
.coverage-summary {
|
|
117
|
+
border-collapse: collapse;
|
|
118
|
+
width: 100%;
|
|
119
|
+
}
|
|
120
|
+
.coverage-summary tr { border-bottom: 1px solid #bbb; }
|
|
121
|
+
.keyline-all { border: 1px solid #ddd; }
|
|
122
|
+
.coverage-summary td, .coverage-summary th { padding: 10px; }
|
|
123
|
+
.coverage-summary tbody { border: 1px solid #bbb; }
|
|
124
|
+
.coverage-summary td { border-right: 1px solid #bbb; }
|
|
125
|
+
.coverage-summary td:last-child { border-right: none; }
|
|
126
|
+
.coverage-summary th {
|
|
127
|
+
text-align: left;
|
|
128
|
+
font-weight: normal;
|
|
129
|
+
white-space: nowrap;
|
|
130
|
+
}
|
|
131
|
+
.coverage-summary th.file { border-right: none !important; }
|
|
132
|
+
.coverage-summary th.pct { }
|
|
133
|
+
.coverage-summary th.pic,
|
|
134
|
+
.coverage-summary th.abs,
|
|
135
|
+
.coverage-summary td.pct,
|
|
136
|
+
.coverage-summary td.abs { text-align: right; }
|
|
137
|
+
.coverage-summary td.file { white-space: nowrap; }
|
|
138
|
+
.coverage-summary td.pic { min-width: 120px !important; }
|
|
139
|
+
.coverage-summary tfoot td { }
|
|
140
|
+
|
|
141
|
+
.coverage-summary .sorter {
|
|
142
|
+
height: 10px;
|
|
143
|
+
width: 7px;
|
|
144
|
+
display: inline-block;
|
|
145
|
+
margin-left: 0.5em;
|
|
146
|
+
background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
|
|
147
|
+
}
|
|
148
|
+
.coverage-summary .sorted .sorter {
|
|
149
|
+
background-position: 0 -20px;
|
|
150
|
+
}
|
|
151
|
+
.coverage-summary .sorted-desc .sorter {
|
|
152
|
+
background-position: 0 -10px;
|
|
153
|
+
}
|
|
154
|
+
.status-line { height: 10px; }
|
|
155
|
+
/* yellow */
|
|
156
|
+
.cbranch-no { background: yellow !important; color: #111; }
|
|
157
|
+
/* dark red */
|
|
158
|
+
.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 }
|
|
159
|
+
.low .chart { border:1px solid #C21F39 }
|
|
160
|
+
.highlighted,
|
|
161
|
+
.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{
|
|
162
|
+
background: #C21F39 !important;
|
|
163
|
+
}
|
|
164
|
+
/* medium red */
|
|
165
|
+
.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE }
|
|
166
|
+
/* light red */
|
|
167
|
+
.low, .cline-no { background:#FCE1E5 }
|
|
168
|
+
/* light green */
|
|
169
|
+
.high, .cline-yes { background:rgb(230,245,208) }
|
|
170
|
+
/* medium green */
|
|
171
|
+
.cstat-yes { background:rgb(161,215,106) }
|
|
172
|
+
/* dark green */
|
|
173
|
+
.status-line.high, .high .cover-fill { background:rgb(77,146,33) }
|
|
174
|
+
.high .chart { border:1px solid rgb(77,146,33) }
|
|
175
|
+
/* dark yellow (gold) */
|
|
176
|
+
.status-line.medium, .medium .cover-fill { background: #f9cd0b; }
|
|
177
|
+
.medium .chart { border:1px solid #f9cd0b; }
|
|
178
|
+
/* light yellow */
|
|
179
|
+
.medium { background: #fff4c2; }
|
|
180
|
+
|
|
181
|
+
.cstat-skip { background: #ddd; color: #111; }
|
|
182
|
+
.fstat-skip { background: #ddd; color: #111 !important; }
|
|
183
|
+
.cbranch-skip { background: #ddd !important; color: #111; }
|
|
184
|
+
|
|
185
|
+
span.cline-neutral { background: #eaeaea; }
|
|
186
|
+
|
|
187
|
+
.coverage-summary td.empty {
|
|
188
|
+
opacity: .5;
|
|
189
|
+
padding-top: 4px;
|
|
190
|
+
padding-bottom: 4px;
|
|
191
|
+
line-height: 1;
|
|
192
|
+
color: #888;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.cover-fill, .cover-empty {
|
|
196
|
+
display:inline-block;
|
|
197
|
+
height: 12px;
|
|
198
|
+
}
|
|
199
|
+
.chart {
|
|
200
|
+
line-height: 0;
|
|
201
|
+
}
|
|
202
|
+
.cover-empty {
|
|
203
|
+
background: white;
|
|
204
|
+
}
|
|
205
|
+
.cover-full {
|
|
206
|
+
border-right: none !important;
|
|
207
|
+
}
|
|
208
|
+
pre.prettyprint {
|
|
209
|
+
border: none !important;
|
|
210
|
+
padding: 0 !important;
|
|
211
|
+
margin: 0 !important;
|
|
212
|
+
}
|
|
213
|
+
.com { color: #999 !important; }
|
|
214
|
+
.ignore-none { color: #999; font-weight: normal; }
|
|
215
|
+
|
|
216
|
+
.wrapper {
|
|
217
|
+
min-height: 100%;
|
|
218
|
+
height: auto !important;
|
|
219
|
+
height: 100%;
|
|
220
|
+
margin: 0 auto -48px;
|
|
221
|
+
}
|
|
222
|
+
.footer, .push {
|
|
223
|
+
height: 48px;
|
|
224
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
var jumpToCode = (function init() {
|
|
3
|
+
// Classes of code we would like to highlight in the file view
|
|
4
|
+
var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no'];
|
|
5
|
+
|
|
6
|
+
// Elements to highlight in the file listing view
|
|
7
|
+
var fileListingElements = ['td.pct.low'];
|
|
8
|
+
|
|
9
|
+
// We don't want to select elements that are direct descendants of another match
|
|
10
|
+
var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > `
|
|
11
|
+
|
|
12
|
+
// Selector that finds elements on the page to which we can jump
|
|
13
|
+
var selector =
|
|
14
|
+
fileListingElements.join(', ') +
|
|
15
|
+
', ' +
|
|
16
|
+
notSelector +
|
|
17
|
+
missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b`
|
|
18
|
+
|
|
19
|
+
// The NodeList of matching elements
|
|
20
|
+
var missingCoverageElements = document.querySelectorAll(selector);
|
|
21
|
+
|
|
22
|
+
var currentIndex;
|
|
23
|
+
|
|
24
|
+
function toggleClass(index) {
|
|
25
|
+
missingCoverageElements
|
|
26
|
+
.item(currentIndex)
|
|
27
|
+
.classList.remove('highlighted');
|
|
28
|
+
missingCoverageElements.item(index).classList.add('highlighted');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function makeCurrent(index) {
|
|
32
|
+
toggleClass(index);
|
|
33
|
+
currentIndex = index;
|
|
34
|
+
missingCoverageElements.item(index).scrollIntoView({
|
|
35
|
+
behavior: 'smooth',
|
|
36
|
+
block: 'center',
|
|
37
|
+
inline: 'center'
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function goToPrevious() {
|
|
42
|
+
var nextIndex = 0;
|
|
43
|
+
if (typeof currentIndex !== 'number' || currentIndex === 0) {
|
|
44
|
+
nextIndex = missingCoverageElements.length - 1;
|
|
45
|
+
} else if (missingCoverageElements.length > 1) {
|
|
46
|
+
nextIndex = currentIndex - 1;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
makeCurrent(nextIndex);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function goToNext() {
|
|
53
|
+
var nextIndex = 0;
|
|
54
|
+
|
|
55
|
+
if (
|
|
56
|
+
typeof currentIndex === 'number' &&
|
|
57
|
+
currentIndex < missingCoverageElements.length - 1
|
|
58
|
+
) {
|
|
59
|
+
nextIndex = currentIndex + 1;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
makeCurrent(nextIndex);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return function jump(event) {
|
|
66
|
+
if (
|
|
67
|
+
document.getElementById('fileSearch') === document.activeElement &&
|
|
68
|
+
document.activeElement != null
|
|
69
|
+
) {
|
|
70
|
+
// if we're currently focused on the search input, we don't want to navigate
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
switch (event.which) {
|
|
75
|
+
case 78: // n
|
|
76
|
+
case 74: // j
|
|
77
|
+
goToNext();
|
|
78
|
+
break;
|
|
79
|
+
case 66: // b
|
|
80
|
+
case 75: // k
|
|
81
|
+
case 80: // p
|
|
82
|
+
goToPrevious();
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
})();
|
|
87
|
+
window.addEventListener('keydown', jumpToCode);
|