dsh-git-ui 0.0.2 → 0.1.1
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/README.md +55 -20
- package/README.zh.md +55 -21
- package/cordis.patch.yml +1 -1
- package/lib/client.js +36 -35
- package/lib/client.js.map +4 -4
- package/lib/contracts/host-endpoints.d.ts +27 -0
- package/lib/host/actions.d.ts +22 -2
- package/lib/host/core.d.ts +7 -1
- package/lib/host/index.d.ts +25 -15
- package/lib/host/index.js +419 -53
- package/lib/host/index.js.map +4 -4
- package/lib/host/parser.d.ts +37 -1
- package/lib/host/queries.d.ts +17 -0
- package/lib/host/types.d.ts +131 -1
- package/package.json +1 -1
- package/src/adapters/dsh/client-adapter.ts +120 -0
- package/src/adapters/dsh/types/cordis.d.ts +48 -0
- package/src/adapters/dsh/types/typert-protocol.d.ts +81 -0
- package/src/adapters/dsh/types/ui-primitives.d.ts +45 -0
- package/src/adapters/dsh/ui-primitives.ts +16 -0
- package/src/client/GitCenter.tsx +1414 -149
- package/src/client/GitPill.tsx +282 -67
- package/src/client/changes-diff.ts +63 -0
- package/src/client/controller.ts +34 -31
- package/src/client/error-text.ts +21 -0
- package/src/client/file-tree.ts +101 -0
- package/src/client/git-graph.ts +188 -0
- package/src/client/icons.tsx +292 -0
- package/src/client/index.ts +38 -134
- package/src/client/locales.ts +124 -0
- package/src/client/popup-close.ts +19 -0
- package/src/client/remote.ts +85 -3
- package/src/client/select-menu.tsx +113 -0
- package/src/client/side-by-side.ts +150 -0
- package/src/client/styles.ts +1375 -86
- package/src/client/time-format.ts +32 -0
- package/src/contracts/client-platform.ts +147 -0
- package/src/contracts/host-endpoints.ts +58 -0
- package/src/contracts/plugin-activation.ts +129 -0
- package/src/contracts/ui-context.tsx +28 -0
- package/src/contracts/ui-primitives.ts +48 -0
- package/src/host/actions.ts +66 -15
- package/src/host/core.ts +9 -2
- package/src/host/index.ts +60 -54
- package/src/host/parser.ts +155 -12
- package/src/host/queries.ts +289 -0
- package/src/host/types.ts +104 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* unified diff → 左右对照行序列(IDEA 式并排查看)。
|
|
3
|
+
* 上下文行两侧同号同文;删除/添加块按索引配对成行,
|
|
4
|
+
* 长短不齐处以空位(empty)补齐;行号按 @@ 头起算。
|
|
5
|
+
* 纯函数,可单元测试。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** 单侧单元格。num 为该行在旧/新文件中的行号;empty 侧 num 为 null。 */
|
|
9
|
+
export interface SideCell {
|
|
10
|
+
readonly num: number | null
|
|
11
|
+
readonly text: string
|
|
12
|
+
readonly kind: 'context' | 'add' | 'del' | 'empty'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** 一行对照:左 = 变更前,右 = 变更后。 */
|
|
16
|
+
export interface SideBySideRow {
|
|
17
|
+
readonly left: SideCell
|
|
18
|
+
readonly right: SideCell
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** 由 unified diff 文本构建对照行序列。空输入返回空数组。 */
|
|
22
|
+
export function buildSideBySide(unified: string): readonly SideBySideRow[] {
|
|
23
|
+
if (unified === '') return []
|
|
24
|
+
const rows: SideBySideRow[] = []
|
|
25
|
+
let leftNum = 0
|
|
26
|
+
let rightNum = 0
|
|
27
|
+
let dels: string[] = []
|
|
28
|
+
let adds: string[] = []
|
|
29
|
+
|
|
30
|
+
/** 将积累的删除/添加块按索引配对成对照行。 */
|
|
31
|
+
const flush = (): void => {
|
|
32
|
+
const n = Math.max(dels.length, adds.length)
|
|
33
|
+
for (let i = 0; i < n; i += 1) {
|
|
34
|
+
const d = dels[i]
|
|
35
|
+
const a = adds[i]
|
|
36
|
+
rows.push({
|
|
37
|
+
left: d !== undefined ? { num: (leftNum += 1), text: d, kind: 'del' } : { num: null, text: '', kind: 'empty' },
|
|
38
|
+
right: a !== undefined ? { num: (rightNum += 1), text: a, kind: 'add' } : { num: null, text: '', kind: 'empty' },
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
dels = []
|
|
42
|
+
adds = []
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
for (const line of unified.split('\n')) {
|
|
46
|
+
if (line.startsWith('@@')) {
|
|
47
|
+
flush()
|
|
48
|
+
const m = /^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/.exec(line)
|
|
49
|
+
if (m !== null) {
|
|
50
|
+
leftNum = Number(m[1]) - 1
|
|
51
|
+
rightNum = Number(m[2]) - 1
|
|
52
|
+
}
|
|
53
|
+
continue
|
|
54
|
+
}
|
|
55
|
+
// 元信息行(diff --git / index / --- / +++ / new file 等)跳过。
|
|
56
|
+
if (/^(diff --git|index |--- |\+\+\+ |new file |deleted file |old mode |new mode |similarity index |rename from |rename to |Binary files |GIT binary patch)/.test(line)) {
|
|
57
|
+
flush()
|
|
58
|
+
continue
|
|
59
|
+
}
|
|
60
|
+
if (line.startsWith('-')) {
|
|
61
|
+
if (adds.length > 0) flush()
|
|
62
|
+
dels.push(line.slice(1))
|
|
63
|
+
continue
|
|
64
|
+
}
|
|
65
|
+
if (line.startsWith('+')) {
|
|
66
|
+
adds.push(line.slice(1))
|
|
67
|
+
continue
|
|
68
|
+
}
|
|
69
|
+
// 上下文行(含空行:unified 的上下文行以空格开头;裸空行视为文件尾)。
|
|
70
|
+
flush()
|
|
71
|
+
if (line === '') continue
|
|
72
|
+
const text = line.slice(1)
|
|
73
|
+
rows.push({
|
|
74
|
+
left: { num: (leftNum += 1), text, kind: 'context' },
|
|
75
|
+
right: { num: (rightNum += 1), text, kind: 'context' },
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
flush()
|
|
79
|
+
return rows
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** 渲染截断:超大差异仅保留前 max 行(防万行级 diff 卡死渲染)。 */
|
|
83
|
+
export function capSideBySideRows(rows: readonly SideBySideRow[], max: number): readonly SideBySideRow[] {
|
|
84
|
+
return rows.length > max ? rows.slice(0, max) : rows
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** 判定一行为上下文行(两侧皆 context)。 */
|
|
88
|
+
function isContextRow(row: SideBySideRow): boolean {
|
|
89
|
+
return row.left.kind === 'context' && row.right.kind === 'context'
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** 折叠块:连续上下文行超阈值时折叠为单个可展开标记,保留原行供展开。 */
|
|
93
|
+
export interface DiffFoldBlock {
|
|
94
|
+
readonly kind: 'fold'
|
|
95
|
+
readonly count: number
|
|
96
|
+
readonly rows: readonly SideBySideRow[]
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** 普通行块。 */
|
|
100
|
+
export interface DiffRowBlock {
|
|
101
|
+
readonly kind: 'row'
|
|
102
|
+
readonly row: SideBySideRow
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** 差异渲染块:普通行或折叠块(按 kind 判别)。 */
|
|
106
|
+
export type DiffBlock = DiffRowBlock | DiffFoldBlock
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* 折叠连续上下文行:超 threshold 的连续 context 段折叠为单个 fold 块
|
|
110
|
+
* (保留原行供查看器展开);不超过阈值则原样保留。threshold < 1 视为不折叠。
|
|
111
|
+
* 纯函数,可单测。
|
|
112
|
+
*/
|
|
113
|
+
export function foldContext(rows: readonly SideBySideRow[], threshold = 3): readonly DiffBlock[] {
|
|
114
|
+
if (threshold < 1) return rows.map((row) => ({ kind: 'row' as const, row }))
|
|
115
|
+
const blocks: DiffBlock[] = []
|
|
116
|
+
let i = 0
|
|
117
|
+
while (i < rows.length) {
|
|
118
|
+
if (!isContextRow(rows[i]!)) {
|
|
119
|
+
blocks.push({ kind: 'row', row: rows[i]! })
|
|
120
|
+
i += 1
|
|
121
|
+
continue
|
|
122
|
+
}
|
|
123
|
+
let j = i
|
|
124
|
+
while (j < rows.length && isContextRow(rows[j]!)) j += 1
|
|
125
|
+
const run = rows.slice(i, j)
|
|
126
|
+
if (run.length > threshold) {
|
|
127
|
+
blocks.push({ kind: 'fold', count: run.length, rows: run })
|
|
128
|
+
} else {
|
|
129
|
+
for (const row of run) blocks.push({ kind: 'row', row })
|
|
130
|
+
}
|
|
131
|
+
i = j
|
|
132
|
+
}
|
|
133
|
+
return blocks
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** 统计增删行数(左 del + 右 add)。纯函数。 */
|
|
137
|
+
export function summarizeChanges(rows: readonly SideBySideRow[]): { readonly add: number; readonly del: number } {
|
|
138
|
+
let add = 0
|
|
139
|
+
let del = 0
|
|
140
|
+
for (const row of rows) {
|
|
141
|
+
if (row.left.kind === 'del') del += 1
|
|
142
|
+
if (row.right.kind === 'add') add += 1
|
|
143
|
+
}
|
|
144
|
+
return { add, del }
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** 判定二进制差异(git 输出 "Binary files … differ",buildSideBySide 会跳过→空)。 */
|
|
148
|
+
export function isBinaryDiff(unified: string): boolean {
|
|
149
|
+
return /^Binary files /m.test(unified)
|
|
150
|
+
}
|