diffwatch 1.0.1 → 1.0.2
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/dist/index.js +88 -14
- package/dist/index.js.map +1 -1
- package/dist/utils/diff-formatter.d.ts +1 -1
- package/dist/utils/diff-formatter.js +10 -2
- package/dist/utils/diff-formatter.js.map +1 -1
- package/dist/utils/git.d.ts +3 -1
- package/dist/utils/git.js +67 -0
- package/dist/utils/git.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +218 -137
- package/src/utils/diff-formatter.ts +11 -2
- package/src/utils/git.ts +66 -1
- package/tests/git.test.ts +41 -1
package/dist/index.js
CHANGED
|
@@ -70,8 +70,29 @@ function main() {
|
|
|
70
70
|
border: { type: 'line' },
|
|
71
71
|
tags: false,
|
|
72
72
|
});
|
|
73
|
+
const searchBox = blessed.box({
|
|
74
|
+
top: 'center',
|
|
75
|
+
left: 'center',
|
|
76
|
+
width: '50%',
|
|
77
|
+
height: 3,
|
|
78
|
+
label: ' Search ',
|
|
79
|
+
border: { type: 'line' },
|
|
80
|
+
style: { border: { fg: 'yellow' } },
|
|
81
|
+
hidden: true,
|
|
82
|
+
});
|
|
83
|
+
const searchInput = blessed.textbox({
|
|
84
|
+
parent: searchBox,
|
|
85
|
+
top: 0,
|
|
86
|
+
left: 0,
|
|
87
|
+
width: '100%-2',
|
|
88
|
+
height: 1,
|
|
89
|
+
keys: true,
|
|
90
|
+
inputOnFocus: true,
|
|
91
|
+
style: { fg: 'white', bg: 'black' },
|
|
92
|
+
});
|
|
73
93
|
screen.append(fileList);
|
|
74
94
|
screen.append(diffView);
|
|
95
|
+
screen.append(searchBox);
|
|
75
96
|
const updateBorders = () => {
|
|
76
97
|
fileList.style.border.fg = screen.focused === fileList ? 'yellow' : 'white';
|
|
77
98
|
diffView.style.border.fg = screen.focused === diffView ? 'yellow' : 'white';
|
|
@@ -80,6 +101,7 @@ function main() {
|
|
|
80
101
|
let currentFiles = [];
|
|
81
102
|
let lastSelectedPath = null;
|
|
82
103
|
let diffUpdateTimeout = null;
|
|
104
|
+
let currentSearchTerm = '';
|
|
83
105
|
const scheduleDiffUpdate = () => {
|
|
84
106
|
if (diffUpdateTimeout)
|
|
85
107
|
clearTimeout(diffUpdateTimeout);
|
|
@@ -107,17 +129,29 @@ function main() {
|
|
|
107
129
|
const selectedIndex = fileList.selected;
|
|
108
130
|
const selectedFile = currentFiles[selectedIndex];
|
|
109
131
|
if (selectedFile) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
132
|
+
let content = '';
|
|
133
|
+
let label = ` Diff (${selectedFile.path}) `;
|
|
134
|
+
if (selectedFile.status === 'unchanged') {
|
|
135
|
+
content = yield gitHandler.getFileContent(selectedFile.path);
|
|
136
|
+
// Highlight search term in full content
|
|
137
|
+
if (currentSearchTerm) {
|
|
138
|
+
const regex = new RegExp(`(${currentSearchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
|
|
139
|
+
content = content.replace(regex, `\x1b[43m\x1b[30m$1\x1b[0m`);
|
|
140
|
+
}
|
|
141
|
+
label = ` File (${selectedFile.path}) `;
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
const diff = yield gitHandler.getDiff(selectedFile.path);
|
|
145
|
+
content = (0, diff_formatter_1.formatDiffWithDiff2Html)(diff, currentSearchTerm);
|
|
146
|
+
}
|
|
113
147
|
const currentContent = diffView.content;
|
|
114
148
|
const currentLabel = diffView.label;
|
|
115
149
|
// Only update if content or label changed to reduce flickering
|
|
116
|
-
if (
|
|
150
|
+
if (content !== currentContent || label !== currentLabel) {
|
|
117
151
|
const savedScroll = diffView.scrollTop;
|
|
118
152
|
const isNewFile = selectedFile.path !== lastSelectedPath;
|
|
119
|
-
diffView.setContent(
|
|
120
|
-
diffView.setLabel(
|
|
153
|
+
diffView.setContent(content);
|
|
154
|
+
diffView.setLabel(label);
|
|
121
155
|
if (isNewFile) {
|
|
122
156
|
diffView.scrollTo(0);
|
|
123
157
|
}
|
|
@@ -143,9 +177,14 @@ function main() {
|
|
|
143
177
|
var _a;
|
|
144
178
|
// Preserve selected file path and scroll positions
|
|
145
179
|
const selectedPath = (_a = currentFiles[fileList.selected]) === null || _a === void 0 ? void 0 : _a.path;
|
|
146
|
-
const fileListScroll = fileList.scroll;
|
|
147
180
|
const diffScroll = diffView.scrollTop;
|
|
148
|
-
|
|
181
|
+
let files;
|
|
182
|
+
if (currentSearchTerm) {
|
|
183
|
+
files = yield gitHandler.searchFiles(currentSearchTerm);
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
files = yield gitHandler.getStatus();
|
|
187
|
+
}
|
|
149
188
|
currentFiles = files;
|
|
150
189
|
const items = files.map(f => {
|
|
151
190
|
let color = '{white-fg}';
|
|
@@ -157,10 +196,13 @@ function main() {
|
|
|
157
196
|
color = '{blue-fg}';
|
|
158
197
|
else if (f.status === 'unstaged')
|
|
159
198
|
color = '{white-fg}';
|
|
199
|
+
else if (f.status === 'unchanged')
|
|
200
|
+
color = '{grey-fg}';
|
|
160
201
|
return `${color}${f.path}{/}`;
|
|
161
202
|
});
|
|
162
203
|
fileList.setItems(items);
|
|
163
|
-
|
|
204
|
+
const labelTitle = currentSearchTerm ? ` Files (${files.length}) - Searching: "${currentSearchTerm}" ` : ` Files (${files.length}) `;
|
|
205
|
+
fileList.setLabel(labelTitle);
|
|
164
206
|
if (items.length > 0) {
|
|
165
207
|
// Restore selection by path if possible
|
|
166
208
|
const newSelectedIndex = selectedPath ? currentFiles.findIndex(f => f.path === selectedPath) : -1;
|
|
@@ -173,11 +215,17 @@ function main() {
|
|
|
173
215
|
yield updateDiff();
|
|
174
216
|
}
|
|
175
217
|
else {
|
|
176
|
-
diffView.setContent('No changes detected.');
|
|
218
|
+
diffView.setContent(currentSearchTerm ? `No files match "${currentSearchTerm}".` : 'No changes detected.');
|
|
177
219
|
diffView.setLabel(' Diff () ');
|
|
178
220
|
}
|
|
179
|
-
// Restore scroll positions
|
|
180
|
-
|
|
221
|
+
// Restore scroll positions if reasonably possible (reset if list changed drastically)
|
|
222
|
+
// Actually, if we filter, the scroll position might be invalid.
|
|
223
|
+
// Ideally we keep it 0 if it was 0 or just let the select() call handle scrolling to the item.
|
|
224
|
+
// The previous implementation blindly restored scrollTop.
|
|
225
|
+
// If the list shrunk, select() should have brought it into view.
|
|
226
|
+
// We only explicitly restore if items.length > 0
|
|
227
|
+
// But setting scroll to previous value might be wrong if the list is now shorter.
|
|
228
|
+
// Safe to only restore diffView scroll as it depends on content, fileList is handled by select.
|
|
181
229
|
diffView.scrollTop = diffScroll;
|
|
182
230
|
screen.render();
|
|
183
231
|
});
|
|
@@ -188,8 +236,34 @@ function main() {
|
|
|
188
236
|
scheduleDiffUpdate();
|
|
189
237
|
});
|
|
190
238
|
screen.key(['escape', 'q', 'C-c'], () => {
|
|
191
|
-
|
|
192
|
-
|
|
239
|
+
if (!searchBox.hidden) {
|
|
240
|
+
searchBox.hide();
|
|
241
|
+
screen.render();
|
|
242
|
+
fileList.focus();
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
screen.destroy();
|
|
246
|
+
process.exit(0);
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
screen.key(['s'], () => {
|
|
250
|
+
searchBox.show();
|
|
251
|
+
searchBox.setFront();
|
|
252
|
+
searchInput.setValue(currentSearchTerm);
|
|
253
|
+
searchInput.focus();
|
|
254
|
+
screen.render();
|
|
255
|
+
});
|
|
256
|
+
searchInput.on('submit', (value) => __awaiter(this, void 0, void 0, function* () {
|
|
257
|
+
currentSearchTerm = (value || '').trim();
|
|
258
|
+
searchBox.hide();
|
|
259
|
+
fileList.focus();
|
|
260
|
+
// Force immediate update
|
|
261
|
+
yield updateFileList();
|
|
262
|
+
}));
|
|
263
|
+
searchInput.on('cancel', () => {
|
|
264
|
+
searchBox.hide();
|
|
265
|
+
fileList.focus();
|
|
266
|
+
screen.render();
|
|
193
267
|
});
|
|
194
268
|
screen.key(['tab'], () => {
|
|
195
269
|
if (screen.focused === fileList) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AACA,MAAM,OAAO,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAC3C,kDAA0B;AAC1B,iDAAsC;AACtC,qCAAqD;AACrD,2DAAiE;AAEjE,SAAe,IAAI;;QACjB,MAAM,UAAU,GAAG,IAAI,gBAAU,EAAE,CAAC;QAEpC,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC;YACjC,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,GAAG,CAAC,mDAAmD,CAAC,CAAC,CAAC;YAC5E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAC5B,QAAQ,EAAE,IAAI;YACd,KAAK,EAAE,WAAW;SACnB,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;YAC5B,GAAG,EAAE,CAAC;YACN,IAAI,EAAE,CAAC;YACP,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,MAAM;YACd,KAAK,EAAE,aAAa;YACpB,IAAI,EAAE,IAAI;YACV,EAAE,EAAE,IAAI;YACR,KAAK,EAAE,IAAI;YACX,IAAI,EAAE,IAAI;YACV,SAAS,EAAE;gBACT,EAAE,EAAE,GAAG;gBACP,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;gBACtB,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE;aACtB;YACD,KAAK,EAAE;gBACL,QAAQ,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE;gBACtC,MAAM,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;aACxB;YACD,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;SACzB,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC;YACtC,GAAG,EAAE,CAAC;YACN,IAAI,EAAE,KAAK;YACX,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,MAAM;YACd,KAAK,EAAE,WAAW;YAClB,IAAI,EAAE,IAAI;YACV,EAAE,EAAE,IAAI;YACR,KAAK,EAAE,IAAI;YACX,SAAS,EAAE;gBACT,EAAE,EAAE,GAAG;gBACP,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;gBACtB,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE;aACtB;YACD,KAAK,EAAE;gBACL,MAAM,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;aACxB;YACD,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;YACxB,IAAI,EAAE,KAAK;SACZ,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACxB,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AACA,MAAM,OAAO,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAC3C,kDAA0B;AAC1B,iDAAsC;AACtC,qCAAqD;AACrD,2DAAiE;AAEjE,SAAe,IAAI;;QACjB,MAAM,UAAU,GAAG,IAAI,gBAAU,EAAE,CAAC;QAEpC,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC;YACjC,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,GAAG,CAAC,mDAAmD,CAAC,CAAC,CAAC;YAC5E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAC5B,QAAQ,EAAE,IAAI;YACd,KAAK,EAAE,WAAW;SACnB,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;YAC5B,GAAG,EAAE,CAAC;YACN,IAAI,EAAE,CAAC;YACP,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,MAAM;YACd,KAAK,EAAE,aAAa;YACpB,IAAI,EAAE,IAAI;YACV,EAAE,EAAE,IAAI;YACR,KAAK,EAAE,IAAI;YACX,IAAI,EAAE,IAAI;YACV,SAAS,EAAE;gBACT,EAAE,EAAE,GAAG;gBACP,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;gBACtB,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE;aACtB;YACD,KAAK,EAAE;gBACL,QAAQ,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE;gBACtC,MAAM,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;aACxB;YACD,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;SACzB,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC;YACtC,GAAG,EAAE,CAAC;YACN,IAAI,EAAE,KAAK;YACX,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,MAAM;YACd,KAAK,EAAE,WAAW;YAClB,IAAI,EAAE,IAAI;YACV,EAAE,EAAE,IAAI;YACR,KAAK,EAAE,IAAI;YACX,SAAS,EAAE;gBACT,EAAE,EAAE,GAAG;gBACP,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;gBACtB,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE;aACtB;YACD,KAAK,EAAE;gBACL,MAAM,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;aACxB;YACD,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;YACxB,IAAI,EAAE,KAAK;SACZ,CAAC,CAAC;QAEH,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC;YAC5B,GAAG,EAAE,QAAQ;YACb,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,CAAC;YACT,KAAK,EAAE,UAAU;YACjB,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;YACxB,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE;YACnC,MAAM,EAAE,IAAI;SACb,CAAC,CAAC;QAEH,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;YAClC,MAAM,EAAE,SAAS;YACjB,GAAG,EAAE,CAAC;YACN,IAAI,EAAE,CAAC;YACP,KAAK,EAAE,QAAQ;YACf,MAAM,EAAE,CAAC;YACT,IAAI,EAAE,IAAI;YACV,YAAY,EAAE,IAAI;YAClB,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE;SACpC,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACxB,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACxB,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAEzB,MAAM,aAAa,GAAG,GAAG,EAAE;YACzB,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,GAAG,MAAM,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC;YAC5E,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,GAAG,MAAM,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC;YAC5E,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,CAAC,CAAC;QAEF,IAAI,YAAY,GAAiB,EAAE,CAAC;QACpC,IAAI,gBAAgB,GAAkB,IAAI,CAAC;QAC3C,IAAI,iBAAiB,GAA0B,IAAI,CAAC;QACpD,IAAI,iBAAiB,GAAW,EAAE,CAAC;QAEnC,MAAM,kBAAkB,GAAG,GAAG,EAAE;YAC9B,IAAI,iBAAiB;gBAAE,YAAY,CAAC,iBAAiB,CAAC,CAAC;YACvD,iBAAiB,GAAG,UAAU,CAAC,GAAS,EAAE;gBACxC,MAAM,UAAU,EAAE,CAAC;YACrB,CAAC,CAAA,EAAE,GAAG,CAAC,CAAC,CAAC,iBAAiB;QAC5B,CAAC,CAAC;QAEF,MAAM,YAAY,GAAG,CAAC,QAAgB,EAAE,EAAE;YACxC,IAAI,CAAC;gBACH,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;oBACjC,uDAAuD;oBACvD,IAAA,qBAAK,EAAC,KAAK,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;gBAC3F,CAAC;qBAAM,CAAC;oBACN,yDAAyD;oBACzD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,UAAU,CAAC;oBACtE,IAAA,qBAAK,EAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;gBACzE,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,KAAK,CAAC,kBAAkB,QAAQ,KAAK,KAAK,EAAE,CAAC,CAAC;YACxD,CAAC;QACH,CAAC,CAAC;QAEF,MAAM,UAAU,GAAG,GAAS,EAAE;YAC5B,MAAM,aAAa,GAAG,QAAQ,CAAC,QAAQ,CAAC;YACxC,MAAM,YAAY,GAAG,YAAY,CAAC,aAAa,CAAC,CAAC;YACjD,IAAI,YAAY,EAAE,CAAC;gBACjB,IAAI,OAAO,GAAG,EAAE,CAAC;gBACjB,IAAI,KAAK,GAAG,UAAU,YAAY,CAAC,IAAI,IAAI,CAAC;gBAE5C,IAAI,YAAY,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;oBACxC,OAAO,GAAG,MAAM,UAAU,CAAC,cAAc,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;oBAC7D,wCAAwC;oBACxC,IAAI,iBAAiB,EAAE,CAAC;wBACtB,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,IAAI,iBAAiB,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;wBAChG,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,2BAA2B,CAAC,CAAC;oBAChE,CAAC;oBACD,KAAK,GAAG,UAAU,YAAY,CAAC,IAAI,IAAI,CAAC;gBAC1C,CAAC;qBAAM,CAAC;oBACN,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;oBACzD,OAAO,GAAG,IAAA,wCAAuB,EAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC;gBAC7D,CAAC;gBAED,MAAM,cAAc,GAAG,QAAQ,CAAC,OAAO,CAAC;gBACxC,MAAM,YAAY,GAAG,QAAQ,CAAC,KAAK,CAAC;gBAEpC,+DAA+D;gBAC/D,IAAI,OAAO,KAAK,cAAc,IAAI,KAAK,KAAK,YAAY,EAAE,CAAC;oBACzD,MAAM,WAAW,GAAG,QAAQ,CAAC,SAAS,CAAC;oBACvC,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,KAAK,gBAAgB,CAAC;oBAEzD,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;oBAC7B,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;oBAEzB,IAAI,SAAS,EAAE,CAAC;wBACd,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;oBACvB,CAAC;yBAAM,CAAC;wBACN,QAAQ,CAAC,SAAS,GAAG,WAAW,CAAC;oBACnC,CAAC;gBACH,CAAC;gBACD,gBAAgB,GAAG,YAAY,CAAC,IAAI,CAAC;YACvC,CAAC;iBAAM,CAAC;gBACN,MAAM,UAAU,GAAG,6BAA6B,CAAC;gBACjD,MAAM,QAAQ,GAAG,WAAW,CAAC;gBAC7B,IAAI,QAAQ,CAAC,OAAO,KAAK,UAAU,IAAI,QAAQ,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;oBACnE,QAAQ,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;oBAChC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;oBAC5B,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;gBACvB,CAAC;gBACD,gBAAgB,GAAG,IAAI,CAAC;YAC1B,CAAC;YACD,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,CAAC,CAAA,CAAC;QAEF,MAAM,cAAc,GAAG,GAAS,EAAE;;YAChC,mDAAmD;YACnD,MAAM,YAAY,GAAG,MAAA,YAAY,CAAC,QAAQ,CAAC,QAAQ,CAAC,0CAAE,IAAI,CAAC;YAC3D,MAAM,UAAU,GAAG,QAAQ,CAAC,SAAS,CAAC;YAEtC,IAAI,KAAmB,CAAC;YAExB,IAAI,iBAAiB,EAAE,CAAC;gBACtB,KAAK,GAAG,MAAM,UAAU,CAAC,WAAW,CAAC,iBAAiB,CAAC,CAAC;YAC1D,CAAC;iBAAM,CAAC;gBACN,KAAK,GAAG,MAAM,UAAU,CAAC,SAAS,EAAE,CAAC;YACvC,CAAC;YAED,YAAY,GAAG,KAAK,CAAC;YAErB,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;gBAC1B,IAAI,KAAK,GAAG,YAAY,CAAC;gBACzB,IAAI,CAAC,CAAC,MAAM,KAAK,OAAO;oBAAE,KAAK,GAAG,YAAY,CAAC;qBAC1C,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS;oBAAE,KAAK,GAAG,UAAU,CAAC;qBAC/C,IAAI,CAAC,CAAC,MAAM,KAAK,UAAU;oBAAE,KAAK,GAAG,WAAW,CAAC;qBACjD,IAAI,CAAC,CAAC,MAAM,KAAK,UAAU;oBAAE,KAAK,GAAG,YAAY,CAAC;qBAClD,IAAI,CAAC,CAAC,MAAM,KAAK,WAAW;oBAAE,KAAK,GAAG,WAAW,CAAC;gBAEvD,OAAO,GAAG,KAAK,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC;YAChC,CAAC,CAAC,CAAC;YAEH,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YAEzB,MAAM,UAAU,GAAG,iBAAiB,CAAC,CAAC,CAAC,WAAW,KAAK,CAAC,MAAM,mBAAmB,iBAAiB,IAAI,CAAC,CAAC,CAAC,WAAW,KAAK,CAAC,MAAM,IAAI,CAAC;YACrI,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;YAE9B,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACrB,wCAAwC;gBACxC,MAAM,gBAAgB,GAAG,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBAClG,QAAQ,CAAC,MAAM,CAAC,gBAAgB,IAAI,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC9D,wDAAwD;gBACxD,IAAI,iBAAiB,EAAE,CAAC;oBACtB,YAAY,CAAC,iBAAiB,CAAC,CAAC;oBAChC,iBAAiB,GAAG,IAAI,CAAC;gBAC3B,CAAC;gBACD,MAAM,UAAU,EAAE,CAAC;YACrB,CAAC;iBAAM,CAAC;gBACN,QAAQ,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,mBAAmB,iBAAiB,IAAI,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC;gBAC3G,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;YACjC,CAAC;YAED,sFAAsF;YACtF,gEAAgE;YAChE,+FAA+F;YAC/F,0DAA0D;YAC1D,iEAAiE;YACjE,iDAAiD;YACjD,kFAAkF;YAClF,gGAAgG;YAEhG,QAAQ,CAAC,SAAS,GAAG,UAAU,CAAC;YAEhC,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,CAAC,CAAA,CAAC;QAEF,QAAQ,CAAC,EAAE,CAAC,aAAa,EAAE,GAAG,EAAE;YAC9B,kBAAkB,EAAE,CAAC;QACvB,CAAC,CAAC,CAAC;QAEH,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE;YAChC,kBAAkB,EAAE,CAAC;QACvB,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,GAAG,EAAE;YACtC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;gBACtB,SAAS,CAAC,IAAI,EAAE,CAAC;gBACjB,MAAM,CAAC,MAAM,EAAE,CAAC;gBAChB,QAAQ,CAAC,KAAK,EAAE,CAAC;YACnB,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,OAAO,EAAE,CAAC;gBACjB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE;YACrB,SAAS,CAAC,IAAI,EAAE,CAAC;YACjB,SAAS,CAAC,QAAQ,EAAE,CAAC;YACrB,WAAW,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC;YACxC,WAAW,CAAC,KAAK,EAAE,CAAC;YACpB,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,CAAC,CAAC,CAAC;QAEH,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAO,KAAa,EAAE,EAAE;YAC/C,iBAAiB,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;YACzC,SAAS,CAAC,IAAI,EAAE,CAAC;YACjB,QAAQ,CAAC,KAAK,EAAE,CAAC;YACjB,yBAAyB;YACzB,MAAM,cAAc,EAAE,CAAC;QACzB,CAAC,CAAA,CAAC,CAAC;QAEH,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;YAC5B,SAAS,CAAC,IAAI,EAAE,CAAC;YACjB,QAAQ,CAAC,KAAK,EAAE,CAAC;YACjB,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE;YACvB,IAAI,MAAM,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;gBAChC,QAAQ,CAAC,KAAK,EAAE,CAAC;YACnB,CAAC;iBAAM,CAAC;gBACN,QAAQ,CAAC,KAAK,EAAE,CAAC;YACnB,CAAC;YACD,aAAa,EAAE,CAAC;QAClB,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE;YACxB,QAAQ,CAAC,KAAK,EAAE,CAAC;YACjB,aAAa,EAAE,CAAC;QAClB,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE;YACzB,QAAQ,CAAC,KAAK,EAAE,CAAC;YACjB,aAAa,EAAE,CAAC;QAClB,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE;YACzB,MAAM,aAAa,GAAG,QAAQ,CAAC,QAAQ,CAAC;YACxC,MAAM,YAAY,GAAG,YAAY,CAAC,aAAa,CAAC,CAAC;YACjD,IAAI,YAAY,EAAE,CAAC;gBACjB,YAAY,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;YAClC,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,WAAW,CAAC,GAAS,EAAE;YACrB,MAAM,cAAc,EAAE,CAAC;QACzB,CAAC,CAAA,EAAE,IAAI,CAAC,CAAC;QAET,MAAM,cAAc,EAAE,CAAC;QACvB,QAAQ,CAAC,KAAK,EAAE,CAAC;QACjB,aAAa,EAAE,CAAC;IAClB,CAAC;CAAA;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;IACjB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function formatDiffWithDiff2Html(diffString: string): string;
|
|
1
|
+
export declare function formatDiffWithDiff2Html(diffString: string, searchTerm?: string): string;
|
|
@@ -36,7 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.formatDiffWithDiff2Html = formatDiffWithDiff2Html;
|
|
37
37
|
const cheerio = __importStar(require("cheerio"));
|
|
38
38
|
const Diff2Html = require('diff2html');
|
|
39
|
-
function formatDiffWithDiff2Html(diffString) {
|
|
39
|
+
function formatDiffWithDiff2Html(diffString, searchTerm) {
|
|
40
40
|
if (!diffString || diffString.trim() === '') {
|
|
41
41
|
return 'No changes detected.';
|
|
42
42
|
}
|
|
@@ -69,7 +69,15 @@ function formatDiffWithDiff2Html(diffString) {
|
|
|
69
69
|
else {
|
|
70
70
|
content = $lineWrapper.text().trim();
|
|
71
71
|
}
|
|
72
|
-
|
|
72
|
+
let fullLine = prefix + content;
|
|
73
|
+
// Highlight search term if present
|
|
74
|
+
if (searchTerm && fullLine.toLowerCase().includes(searchTerm.toLowerCase())) {
|
|
75
|
+
const regex = new RegExp(`(${searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
|
|
76
|
+
// We need to know the base color to restore it.
|
|
77
|
+
// Added: 32, Deleted: 31, Normal: 37
|
|
78
|
+
const baseColor = isAdded ? '32' : (isDeleted ? '31' : '37');
|
|
79
|
+
fullLine = fullLine.replace(regex, `\x1b[43m\x1b[30m$1\x1b[0m\x1b[${baseColor}m`);
|
|
80
|
+
}
|
|
73
81
|
if (isAdded) {
|
|
74
82
|
blessedText += `\x1b[32m${fullLine}\x1b[0m\n`;
|
|
75
83
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff-formatter.js","sourceRoot":"","sources":["../../src/utils/diff-formatter.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGA,
|
|
1
|
+
{"version":3,"file":"diff-formatter.js","sourceRoot":"","sources":["../../src/utils/diff-formatter.ts"],"names":[],"mappings":"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"}
|
package/dist/utils/git.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export interface FileStatus {
|
|
2
2
|
path: string;
|
|
3
|
-
status: 'modified' | 'added' | 'deleted' | 'unstaged' | 'unknown';
|
|
3
|
+
status: 'modified' | 'added' | 'deleted' | 'unstaged' | 'unknown' | 'unchanged';
|
|
4
4
|
mtime?: Date;
|
|
5
5
|
}
|
|
6
6
|
export declare class GitHandler {
|
|
@@ -8,5 +8,7 @@ export declare class GitHandler {
|
|
|
8
8
|
constructor(workingDir?: string);
|
|
9
9
|
isRepo(): Promise<boolean>;
|
|
10
10
|
getStatus(): Promise<FileStatus[]>;
|
|
11
|
+
searchFiles(term: string): Promise<FileStatus[]>;
|
|
11
12
|
getDiff(filePath: string): Promise<string>;
|
|
13
|
+
getFileContent(filePath: string): Promise<string>;
|
|
12
14
|
}
|
package/dist/utils/git.js
CHANGED
|
@@ -74,6 +74,63 @@ class GitHandler {
|
|
|
74
74
|
});
|
|
75
75
|
});
|
|
76
76
|
}
|
|
77
|
+
searchFiles(term) {
|
|
78
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
79
|
+
if (!term || !term.trim())
|
|
80
|
+
return [];
|
|
81
|
+
try {
|
|
82
|
+
// Use git grep to find files containing the term
|
|
83
|
+
// -i: ignore case
|
|
84
|
+
// -l: list filenames only
|
|
85
|
+
// -F: interpret pattern as a fixed string
|
|
86
|
+
// --untracked: include untracked files
|
|
87
|
+
const result = yield this.git.raw(['grep', '-i', '-l', '-F', '--untracked', term.trim()]);
|
|
88
|
+
const matchedPaths = [...new Set(result.split('\n').map(p => p.trim()).filter(p => p !== ''))];
|
|
89
|
+
if (matchedPaths.length === 0)
|
|
90
|
+
return [];
|
|
91
|
+
// Get current status to identify which matched files are changed
|
|
92
|
+
const changedFiles = yield this.getStatus();
|
|
93
|
+
const changedMap = new Map();
|
|
94
|
+
changedFiles.forEach(f => changedMap.set(f.path, f));
|
|
95
|
+
const finalResults = [];
|
|
96
|
+
for (const path of matchedPaths) {
|
|
97
|
+
if (changedMap.has(path)) {
|
|
98
|
+
finalResults.push(changedMap.get(path));
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
// It's an unchanged file
|
|
102
|
+
let mtime = new Date(0);
|
|
103
|
+
try {
|
|
104
|
+
const stat = yield promises_1.default.stat(path);
|
|
105
|
+
mtime = stat.mtime;
|
|
106
|
+
}
|
|
107
|
+
catch (_a) { }
|
|
108
|
+
finalResults.push({
|
|
109
|
+
path,
|
|
110
|
+
status: 'unchanged',
|
|
111
|
+
mtime
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// Sort results by mtime descending
|
|
116
|
+
return finalResults.sort((a, b) => {
|
|
117
|
+
const mtimeA = a.mtime || new Date(0);
|
|
118
|
+
const mtimeB = b.mtime || new Date(0);
|
|
119
|
+
const timeDiff = mtimeB.getTime() - mtimeA.getTime();
|
|
120
|
+
if (timeDiff !== 0)
|
|
121
|
+
return timeDiff;
|
|
122
|
+
return a.path.localeCompare(b.path);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
// git grep returns exit code 1 if no matches are found, which simple-git might throw as an error
|
|
127
|
+
if (error.message && (error.message.includes('exit code 1') || error.exitCode === 1)) {
|
|
128
|
+
return [];
|
|
129
|
+
}
|
|
130
|
+
throw error;
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
77
134
|
getDiff(filePath) {
|
|
78
135
|
return __awaiter(this, void 0, void 0, function* () {
|
|
79
136
|
try {
|
|
@@ -91,6 +148,16 @@ class GitHandler {
|
|
|
91
148
|
}
|
|
92
149
|
});
|
|
93
150
|
}
|
|
151
|
+
getFileContent(filePath) {
|
|
152
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
153
|
+
try {
|
|
154
|
+
return yield promises_1.default.readFile(filePath, 'utf8');
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
return `Error reading file: ${error}`;
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
}
|
|
94
161
|
}
|
|
95
162
|
exports.GitHandler = GitHandler;
|
|
96
163
|
//# sourceMappingURL=git.js.map
|
package/dist/utils/git.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const blessed = require('neo-neo-blessed');
|
|
3
|
-
import chalk from 'chalk';
|
|
4
|
-
import { spawn } from 'child_process';
|
|
5
|
-
import { GitHandler, FileStatus } from './utils/git';
|
|
6
|
-
import { formatDiffWithDiff2Html } from './utils/diff-formatter';
|
|
2
|
+
const blessed = require('neo-neo-blessed');
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { spawn } from 'child_process';
|
|
5
|
+
import { GitHandler, FileStatus } from './utils/git';
|
|
6
|
+
import { formatDiffWithDiff2Html } from './utils/diff-formatter';
|
|
7
7
|
|
|
8
8
|
async function main() {
|
|
9
9
|
const gitHandler = new GitHandler();
|
|
@@ -61,8 +61,31 @@ async function main() {
|
|
|
61
61
|
tags: false,
|
|
62
62
|
});
|
|
63
63
|
|
|
64
|
+
const searchBox = blessed.box({
|
|
65
|
+
top: 'center',
|
|
66
|
+
left: 'center',
|
|
67
|
+
width: '50%',
|
|
68
|
+
height: 3,
|
|
69
|
+
label: ' Search ',
|
|
70
|
+
border: { type: 'line' },
|
|
71
|
+
style: { border: { fg: 'yellow' } },
|
|
72
|
+
hidden: true,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const searchInput = blessed.textbox({
|
|
76
|
+
parent: searchBox,
|
|
77
|
+
top: 0,
|
|
78
|
+
left: 0,
|
|
79
|
+
width: '100%-2',
|
|
80
|
+
height: 1,
|
|
81
|
+
keys: true,
|
|
82
|
+
inputOnFocus: true,
|
|
83
|
+
style: { fg: 'white', bg: 'black' },
|
|
84
|
+
});
|
|
85
|
+
|
|
64
86
|
screen.append(fileList);
|
|
65
87
|
screen.append(diffView);
|
|
88
|
+
screen.append(searchBox);
|
|
66
89
|
|
|
67
90
|
const updateBorders = () => {
|
|
68
91
|
fileList.style.border.fg = screen.focused === fileList ? 'yellow' : 'white';
|
|
@@ -70,125 +93,183 @@ async function main() {
|
|
|
70
93
|
screen.render();
|
|
71
94
|
};
|
|
72
95
|
|
|
73
|
-
let currentFiles: FileStatus[] = [];
|
|
74
|
-
let lastSelectedPath: string | null = null;
|
|
75
|
-
let diffUpdateTimeout: NodeJS.Timeout | null = null;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
let currentFiles: FileStatus[] = [];
|
|
97
|
+
let lastSelectedPath: string | null = null;
|
|
98
|
+
let diffUpdateTimeout: NodeJS.Timeout | null = null;
|
|
99
|
+
let currentSearchTerm: string = '';
|
|
100
|
+
|
|
101
|
+
const scheduleDiffUpdate = () => {
|
|
102
|
+
if (diffUpdateTimeout) clearTimeout(diffUpdateTimeout);
|
|
103
|
+
diffUpdateTimeout = setTimeout(async () => {
|
|
104
|
+
await updateDiff();
|
|
105
|
+
}, 150); // 150ms debounce
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const openInEditor = (filePath: string) => {
|
|
109
|
+
try {
|
|
110
|
+
if (process.platform === 'win32') {
|
|
111
|
+
// On Windows, use 'start' to open with default program
|
|
112
|
+
spawn('cmd', ['/c', 'start', '', filePath], { stdio: 'ignore', detached: true }).unref();
|
|
113
|
+
} else {
|
|
114
|
+
// On Unix-like systems, try EDITOR, fallback to xdg-open
|
|
115
|
+
const editor = process.env.EDITOR || process.env.VISUAL || 'xdg-open';
|
|
116
|
+
spawn(editor, [filePath], { stdio: 'ignore', detached: true }).unref();
|
|
117
|
+
}
|
|
118
|
+
} catch (error) {
|
|
119
|
+
console.error(`Failed to open ${filePath}: ${error}`);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const updateDiff = async () => {
|
|
124
|
+
const selectedIndex = fileList.selected;
|
|
125
|
+
const selectedFile = currentFiles[selectedIndex];
|
|
126
|
+
if (selectedFile) {
|
|
127
|
+
let content = '';
|
|
128
|
+
let label = ` Diff (${selectedFile.path}) `;
|
|
129
|
+
|
|
130
|
+
if (selectedFile.status === 'unchanged') {
|
|
131
|
+
content = await gitHandler.getFileContent(selectedFile.path);
|
|
132
|
+
// Highlight search term in full content
|
|
133
|
+
if (currentSearchTerm) {
|
|
134
|
+
const regex = new RegExp(`(${currentSearchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
|
|
135
|
+
content = content.replace(regex, `\x1b[43m\x1b[30m$1\x1b[0m`);
|
|
136
|
+
}
|
|
137
|
+
label = ` File (${selectedFile.path}) `;
|
|
138
|
+
} else {
|
|
139
|
+
const diff = await gitHandler.getDiff(selectedFile.path);
|
|
140
|
+
content = formatDiffWithDiff2Html(diff, currentSearchTerm);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const currentContent = diffView.content;
|
|
144
|
+
const currentLabel = diffView.label;
|
|
145
|
+
|
|
146
|
+
// Only update if content or label changed to reduce flickering
|
|
147
|
+
if (content !== currentContent || label !== currentLabel) {
|
|
148
|
+
const savedScroll = diffView.scrollTop;
|
|
149
|
+
const isNewFile = selectedFile.path !== lastSelectedPath;
|
|
150
|
+
|
|
151
|
+
diffView.setContent(content);
|
|
152
|
+
diffView.setLabel(label);
|
|
153
|
+
|
|
154
|
+
if (isNewFile) {
|
|
155
|
+
diffView.scrollTo(0);
|
|
156
|
+
} else {
|
|
157
|
+
diffView.scrollTop = savedScroll;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
lastSelectedPath = selectedFile.path;
|
|
161
|
+
} else {
|
|
162
|
+
const newContent = 'Select a file to view diff.';
|
|
163
|
+
const newLabel = ' Diff () ';
|
|
164
|
+
if (diffView.content !== newContent || diffView.label !== newLabel) {
|
|
165
|
+
diffView.setContent(newContent);
|
|
166
|
+
diffView.setLabel(newLabel);
|
|
167
|
+
diffView.scrollTo(0);
|
|
168
|
+
}
|
|
169
|
+
lastSelectedPath = null;
|
|
170
|
+
}
|
|
171
|
+
screen.render();
|
|
97
172
|
};
|
|
98
173
|
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
diffUpdateTimeout = null;
|
|
167
|
-
}
|
|
168
|
-
await updateDiff();
|
|
169
|
-
} else {
|
|
170
|
-
diffView.setContent('No changes detected.');
|
|
171
|
-
diffView.setLabel(' Diff () ');
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Restore scroll positions
|
|
175
|
-
fileList.scroll = fileListScroll;
|
|
176
|
-
diffView.scrollTop = diffScroll;
|
|
177
|
-
|
|
178
|
-
screen.render();
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
fileList.on('select item', () => {
|
|
182
|
-
scheduleDiffUpdate();
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
fileList.key(['up', 'down'], () => {
|
|
186
|
-
scheduleDiffUpdate();
|
|
187
|
-
});
|
|
174
|
+
const updateFileList = async () => {
|
|
175
|
+
// Preserve selected file path and scroll positions
|
|
176
|
+
const selectedPath = currentFiles[fileList.selected]?.path;
|
|
177
|
+
const diffScroll = diffView.scrollTop;
|
|
178
|
+
|
|
179
|
+
let files: FileStatus[];
|
|
180
|
+
|
|
181
|
+
if (currentSearchTerm) {
|
|
182
|
+
files = await gitHandler.searchFiles(currentSearchTerm);
|
|
183
|
+
} else {
|
|
184
|
+
files = await gitHandler.getStatus();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
currentFiles = files;
|
|
188
|
+
|
|
189
|
+
const items = files.map(f => {
|
|
190
|
+
let color = '{white-fg}';
|
|
191
|
+
if (f.status === 'added') color = '{green-fg}';
|
|
192
|
+
else if (f.status === 'deleted') color = '{red-fg}';
|
|
193
|
+
else if (f.status === 'modified') color = '{blue-fg}';
|
|
194
|
+
else if (f.status === 'unstaged') color = '{white-fg}';
|
|
195
|
+
else if (f.status === 'unchanged') color = '{grey-fg}';
|
|
196
|
+
|
|
197
|
+
return `${color}${f.path}{/}`;
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
fileList.setItems(items);
|
|
201
|
+
|
|
202
|
+
const labelTitle = currentSearchTerm ? ` Files (${files.length}) - Searching: "${currentSearchTerm}" ` : ` Files (${files.length}) `;
|
|
203
|
+
fileList.setLabel(labelTitle);
|
|
204
|
+
|
|
205
|
+
if (items.length > 0) {
|
|
206
|
+
// Restore selection by path if possible
|
|
207
|
+
const newSelectedIndex = selectedPath ? currentFiles.findIndex(f => f.path === selectedPath) : -1;
|
|
208
|
+
fileList.select(newSelectedIndex >= 0 ? newSelectedIndex : 0);
|
|
209
|
+
// Cancel any pending diff update and update immediately
|
|
210
|
+
if (diffUpdateTimeout) {
|
|
211
|
+
clearTimeout(diffUpdateTimeout);
|
|
212
|
+
diffUpdateTimeout = null;
|
|
213
|
+
}
|
|
214
|
+
await updateDiff();
|
|
215
|
+
} else {
|
|
216
|
+
diffView.setContent(currentSearchTerm ? `No files match "${currentSearchTerm}".` : 'No changes detected.');
|
|
217
|
+
diffView.setLabel(' Diff () ');
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Restore scroll positions if reasonably possible (reset if list changed drastically)
|
|
221
|
+
// Actually, if we filter, the scroll position might be invalid.
|
|
222
|
+
// Ideally we keep it 0 if it was 0 or just let the select() call handle scrolling to the item.
|
|
223
|
+
// The previous implementation blindly restored scrollTop.
|
|
224
|
+
// If the list shrunk, select() should have brought it into view.
|
|
225
|
+
// We only explicitly restore if items.length > 0
|
|
226
|
+
// But setting scroll to previous value might be wrong if the list is now shorter.
|
|
227
|
+
// Safe to only restore diffView scroll as it depends on content, fileList is handled by select.
|
|
228
|
+
|
|
229
|
+
diffView.scrollTop = diffScroll;
|
|
230
|
+
|
|
231
|
+
screen.render();
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
fileList.on('select item', () => {
|
|
235
|
+
scheduleDiffUpdate();
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
fileList.key(['up', 'down'], () => {
|
|
239
|
+
scheduleDiffUpdate();
|
|
240
|
+
});
|
|
188
241
|
|
|
189
242
|
screen.key(['escape', 'q', 'C-c'], () => {
|
|
190
|
-
|
|
191
|
-
|
|
243
|
+
if (!searchBox.hidden) {
|
|
244
|
+
searchBox.hide();
|
|
245
|
+
screen.render();
|
|
246
|
+
fileList.focus();
|
|
247
|
+
} else {
|
|
248
|
+
screen.destroy();
|
|
249
|
+
process.exit(0);
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
screen.key(['s'], () => {
|
|
254
|
+
searchBox.show();
|
|
255
|
+
searchBox.setFront();
|
|
256
|
+
searchInput.setValue(currentSearchTerm);
|
|
257
|
+
searchInput.focus();
|
|
258
|
+
screen.render();
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
searchInput.on('submit', async (value: string) => {
|
|
262
|
+
currentSearchTerm = (value || '').trim();
|
|
263
|
+
searchBox.hide();
|
|
264
|
+
fileList.focus();
|
|
265
|
+
// Force immediate update
|
|
266
|
+
await updateFileList();
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
searchInput.on('cancel', () => {
|
|
270
|
+
searchBox.hide();
|
|
271
|
+
fileList.focus();
|
|
272
|
+
screen.render();
|
|
192
273
|
});
|
|
193
274
|
|
|
194
275
|
screen.key(['tab'], () => {
|
|
@@ -200,23 +281,23 @@ async function main() {
|
|
|
200
281
|
updateBorders();
|
|
201
282
|
});
|
|
202
283
|
|
|
203
|
-
screen.key(['left'], () => {
|
|
204
|
-
fileList.focus();
|
|
205
|
-
updateBorders();
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
screen.key(['right'], () => {
|
|
209
|
-
diffView.focus();
|
|
210
|
-
updateBorders();
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
screen.key(['enter'], () => {
|
|
214
|
-
const selectedIndex = fileList.selected;
|
|
215
|
-
const selectedFile = currentFiles[selectedIndex];
|
|
216
|
-
if (selectedFile) {
|
|
217
|
-
openInEditor(selectedFile.path);
|
|
218
|
-
}
|
|
219
|
-
});
|
|
284
|
+
screen.key(['left'], () => {
|
|
285
|
+
fileList.focus();
|
|
286
|
+
updateBorders();
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
screen.key(['right'], () => {
|
|
290
|
+
diffView.focus();
|
|
291
|
+
updateBorders();
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
screen.key(['enter'], () => {
|
|
295
|
+
const selectedIndex = fileList.selected;
|
|
296
|
+
const selectedFile = currentFiles[selectedIndex];
|
|
297
|
+
if (selectedFile) {
|
|
298
|
+
openInEditor(selectedFile.path);
|
|
299
|
+
}
|
|
300
|
+
});
|
|
220
301
|
|
|
221
302
|
setInterval(async () => {
|
|
222
303
|
await updateFileList();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as cheerio from 'cheerio';
|
|
2
2
|
const Diff2Html = require('diff2html');
|
|
3
3
|
|
|
4
|
-
export function formatDiffWithDiff2Html(diffString: string): string {
|
|
4
|
+
export function formatDiffWithDiff2Html(diffString: string, searchTerm?: string): string {
|
|
5
5
|
if (!diffString || diffString.trim() === '') {
|
|
6
6
|
return 'No changes detected.';
|
|
7
7
|
}
|
|
@@ -41,7 +41,16 @@ export function formatDiffWithDiff2Html(diffString: string): string {
|
|
|
41
41
|
content = $lineWrapper.text().trim();
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
let fullLine = prefix + content;
|
|
45
|
+
|
|
46
|
+
// Highlight search term if present
|
|
47
|
+
if (searchTerm && fullLine.toLowerCase().includes(searchTerm.toLowerCase())) {
|
|
48
|
+
const regex = new RegExp(`(${searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
|
|
49
|
+
// We need to know the base color to restore it.
|
|
50
|
+
// Added: 32, Deleted: 31, Normal: 37
|
|
51
|
+
const baseColor = isAdded ? '32' : (isDeleted ? '31' : '37');
|
|
52
|
+
fullLine = fullLine.replace(regex, `\x1b[43m\x1b[30m$1\x1b[0m\x1b[${baseColor}m`);
|
|
53
|
+
}
|
|
45
54
|
|
|
46
55
|
if (isAdded) {
|
|
47
56
|
blessedText += `\x1b[32m${fullLine}\x1b[0m\n`;
|
package/src/utils/git.ts
CHANGED
|
@@ -3,7 +3,7 @@ import fs from 'fs/promises';
|
|
|
3
3
|
|
|
4
4
|
export interface FileStatus {
|
|
5
5
|
path: string;
|
|
6
|
-
status: 'modified' | 'added' | 'deleted' | 'unstaged' | 'unknown';
|
|
6
|
+
status: 'modified' | 'added' | 'deleted' | 'unstaged' | 'unknown' | 'unchanged';
|
|
7
7
|
mtime?: Date;
|
|
8
8
|
}
|
|
9
9
|
|
|
@@ -73,6 +73,63 @@ export class GitHandler {
|
|
|
73
73
|
});
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
async searchFiles(term: string): Promise<FileStatus[]> {
|
|
77
|
+
if (!term || !term.trim()) return [];
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
// Use git grep to find files containing the term
|
|
81
|
+
// -i: ignore case
|
|
82
|
+
// -l: list filenames only
|
|
83
|
+
// -F: interpret pattern as a fixed string
|
|
84
|
+
// --untracked: include untracked files
|
|
85
|
+
const result = await this.git.raw(['grep', '-i', '-l', '-F', '--untracked', term.trim()]);
|
|
86
|
+
const matchedPaths = [...new Set(result.split('\n').map(p => p.trim()).filter(p => p !== ''))];
|
|
87
|
+
|
|
88
|
+
if (matchedPaths.length === 0) return [];
|
|
89
|
+
|
|
90
|
+
// Get current status to identify which matched files are changed
|
|
91
|
+
const changedFiles = await this.getStatus();
|
|
92
|
+
const changedMap = new Map<string, FileStatus>();
|
|
93
|
+
changedFiles.forEach(f => changedMap.set(f.path, f));
|
|
94
|
+
|
|
95
|
+
const finalResults: FileStatus[] = [];
|
|
96
|
+
|
|
97
|
+
for (const path of matchedPaths) {
|
|
98
|
+
if (changedMap.has(path)) {
|
|
99
|
+
finalResults.push(changedMap.get(path)!);
|
|
100
|
+
} else {
|
|
101
|
+
// It's an unchanged file
|
|
102
|
+
let mtime = new Date(0);
|
|
103
|
+
try {
|
|
104
|
+
const stat = await fs.stat(path);
|
|
105
|
+
mtime = stat.mtime;
|
|
106
|
+
} catch {}
|
|
107
|
+
|
|
108
|
+
finalResults.push({
|
|
109
|
+
path,
|
|
110
|
+
status: 'unchanged',
|
|
111
|
+
mtime
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Sort results by mtime descending
|
|
117
|
+
return finalResults.sort((a, b) => {
|
|
118
|
+
const mtimeA = a.mtime || new Date(0);
|
|
119
|
+
const mtimeB = b.mtime || new Date(0);
|
|
120
|
+
const timeDiff = mtimeB.getTime() - mtimeA.getTime();
|
|
121
|
+
if (timeDiff !== 0) return timeDiff;
|
|
122
|
+
return a.path.localeCompare(b.path);
|
|
123
|
+
});
|
|
124
|
+
} catch (error: any) {
|
|
125
|
+
// git grep returns exit code 1 if no matches are found, which simple-git might throw as an error
|
|
126
|
+
if (error.message && (error.message.includes('exit code 1') || error.exitCode === 1)) {
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
76
133
|
async getDiff(filePath: string): Promise<string> {
|
|
77
134
|
try {
|
|
78
135
|
const isUntracked = (await this.git.status()).not_added.includes(filePath);
|
|
@@ -89,4 +146,12 @@ export class GitHandler {
|
|
|
89
146
|
return `Error getting diff: ${error}`;
|
|
90
147
|
}
|
|
91
148
|
}
|
|
149
|
+
|
|
150
|
+
async getFileContent(filePath: string): Promise<string> {
|
|
151
|
+
try {
|
|
152
|
+
return await fs.readFile(filePath, 'utf8');
|
|
153
|
+
} catch (error) {
|
|
154
|
+
return `Error reading file: ${error}`;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
92
157
|
}
|
package/tests/git.test.ts
CHANGED
|
@@ -3,11 +3,12 @@ jest.mock('simple-git', () => ({
|
|
|
3
3
|
}));
|
|
4
4
|
jest.mock('fs/promises', () => ({
|
|
5
5
|
stat: jest.fn(),
|
|
6
|
+
readFile: jest.fn(),
|
|
6
7
|
}));
|
|
7
8
|
|
|
8
9
|
import { simpleGit } from 'simple-git';
|
|
9
10
|
import fs from 'fs/promises';
|
|
10
|
-
import { GitHandler } from '../src/utils/git';
|
|
11
|
+
import { GitHandler, FileStatus } from '../src/utils/git';
|
|
11
12
|
|
|
12
13
|
describe('GitHandler', () => {
|
|
13
14
|
let gitHandler: GitHandler;
|
|
@@ -22,6 +23,7 @@ describe('GitHandler', () => {
|
|
|
22
23
|
};
|
|
23
24
|
(simpleGit as jest.Mock).mockReturnValue(mockGit);
|
|
24
25
|
(fs.stat as jest.Mock).mockResolvedValue({ mtime: new Date() });
|
|
26
|
+
(fs.readFile as jest.Mock).mockResolvedValue('');
|
|
25
27
|
gitHandler = new GitHandler();
|
|
26
28
|
});
|
|
27
29
|
|
|
@@ -98,4 +100,42 @@ describe('GitHandler', () => {
|
|
|
98
100
|
'a-file.ts', // oldest
|
|
99
101
|
]);
|
|
100
102
|
});
|
|
103
|
+
|
|
104
|
+
it('should search files correctly', async () => {
|
|
105
|
+
mockGit.raw = jest.fn().mockResolvedValue('match.ts\nunchanged.ts');
|
|
106
|
+
mockGit.status.mockResolvedValue({
|
|
107
|
+
modified: ['match.ts'],
|
|
108
|
+
deleted: [],
|
|
109
|
+
created: [],
|
|
110
|
+
not_added: [],
|
|
111
|
+
renamed: [],
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const results = await gitHandler.searchFiles('match');
|
|
115
|
+
|
|
116
|
+
expect(results).toHaveLength(2);
|
|
117
|
+
expect(results).toEqual(expect.arrayContaining([
|
|
118
|
+
expect.objectContaining({ path: 'match.ts', status: 'modified' }),
|
|
119
|
+
expect.objectContaining({ path: 'unchanged.ts', status: 'unchanged' }),
|
|
120
|
+
]));
|
|
121
|
+
expect(mockGit.raw).toHaveBeenCalledWith(['grep', '-i', '-l', '-F', '--untracked', 'match']);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('should deduplicate search results', async () => {
|
|
125
|
+
// Return duplicate paths
|
|
126
|
+
mockGit.raw = jest.fn().mockResolvedValue('match.ts\nmatch.ts\nother.ts\nother.ts');
|
|
127
|
+
mockGit.status.mockResolvedValue({
|
|
128
|
+
modified: [],
|
|
129
|
+
deleted: [],
|
|
130
|
+
created: [],
|
|
131
|
+
not_added: [],
|
|
132
|
+
renamed: [],
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const results = await gitHandler.searchFiles('term');
|
|
136
|
+
|
|
137
|
+
expect(results).toHaveLength(2);
|
|
138
|
+
const paths = results.map(r => r.path).sort();
|
|
139
|
+
expect(paths).toEqual(['match.ts', 'other.ts']);
|
|
140
|
+
});
|
|
101
141
|
});
|