reslop 0.1.4 → 0.1.6
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 +19 -1
- package/README.md +19 -20
- package/lib/cli.js +20 -16
- package/lib/detect.js +2 -40
- package/lib/diff.js +71 -2
- package/lib/files.js +5 -1
- package/lib/git.js +322 -12
- package/lib/highlight/bash.js +228 -0
- package/lib/highlight/core.js +194 -0
- package/lib/highlight/css.js +76 -0
- package/lib/highlight/csv.js +66 -0
- package/lib/highlight/dart.js +261 -0
- package/lib/highlight/dot.js +54 -0
- package/lib/highlight/html.js +90 -0
- package/lib/highlight/js.js +571 -0
- package/lib/highlight/json.js +54 -0
- package/lib/highlight/log.js +60 -0
- package/lib/highlight/txt.js +41 -0
- package/lib/highlight.js +32 -1299
- package/lib/keys.js +98 -26
- package/lib/render.js +574 -84
- package/lib/review.js +145 -3
- package/lib/select.js +7 -12
- package/lib/session.js +759 -50
- package/lib/sys.js +4 -0
- package/lib/update.js +302 -0
- package/package.json +1 -1
package/lib/render.js
CHANGED
|
@@ -10,8 +10,8 @@ const { itemPath } = require('./files.js');
|
|
|
10
10
|
const wrap = require('./wrap.js');
|
|
11
11
|
|
|
12
12
|
const { tokenize, overlayTokens } = highlight;
|
|
13
|
-
const {
|
|
14
|
-
const {
|
|
13
|
+
const { ACTION_IDS, BRANCH_ACTIONS, actionLetter, buttonWord } = keys;
|
|
14
|
+
const { disabledActions } = keys;
|
|
15
15
|
const { wrapPlain, wrapMultiline, cursorInWrap } = wrap;
|
|
16
16
|
const { THEME, CODE_FG, codeFg, paint, visibleWidth } = ansi;
|
|
17
17
|
const { truncateVisible, fg, RESET, ESC, EL, seq } = ansi;
|
|
@@ -113,32 +113,117 @@ const FILE_COL_ALIGN = {
|
|
|
113
113
|
m: 'end',
|
|
114
114
|
};
|
|
115
115
|
|
|
116
|
+
const BRANCH_COL_KEYS = ['sha', 'ahead', 'behind', 'gone', 'date'];
|
|
117
|
+
|
|
118
|
+
const BRANCH_COL_ALIGN = {
|
|
119
|
+
sha: 'end',
|
|
120
|
+
ahead: 'start',
|
|
121
|
+
behind: 'start',
|
|
122
|
+
gone: 'end',
|
|
123
|
+
date: 'end',
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const BRANCH_COL_FG = {
|
|
127
|
+
sha: THEME.mutedFg,
|
|
128
|
+
ahead: THEME.addLineFg,
|
|
129
|
+
behind: THEME.delLineFg,
|
|
130
|
+
gone: THEME.delLineFg,
|
|
131
|
+
date: THEME.mutedFg,
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const BRANCH_NAME_MAX = 24;
|
|
135
|
+
|
|
136
|
+
const BUSY_BAR = ['▰▰▱▱▱▱', '▱▰▰▱▱▱', '▱▱▰▰▱▱', '▱▱▱▰▰▱', '▱▱▱▱▰▰', '▰▱▱▱▱▰'];
|
|
137
|
+
const BUSY_PROGRESS = [
|
|
138
|
+
'pulling',
|
|
139
|
+
'pushing',
|
|
140
|
+
'loading',
|
|
141
|
+
'checking npm',
|
|
142
|
+
'updating reslop',
|
|
143
|
+
'npm i',
|
|
144
|
+
'npm uninstall',
|
|
145
|
+
'npm audit fix',
|
|
146
|
+
'checking out',
|
|
147
|
+
'creating branch',
|
|
148
|
+
'committing',
|
|
149
|
+
'force pushing',
|
|
150
|
+
];
|
|
151
|
+
|
|
152
|
+
const formatBusyStatus = (status, frame = 0) => {
|
|
153
|
+
const kind = `${status ?? ''}`;
|
|
154
|
+
if (!BUSY_PROGRESS.includes(kind)) return kind;
|
|
155
|
+
const i = Math.abs(frame) % BUSY_BAR.length;
|
|
156
|
+
return `${kind} ${BUSY_BAR[i]}`;
|
|
157
|
+
};
|
|
158
|
+
|
|
116
159
|
const INFO_STATUS = [
|
|
117
160
|
'copied',
|
|
118
161
|
'staged',
|
|
119
162
|
'unstaged',
|
|
120
163
|
'not staged',
|
|
121
|
-
'
|
|
164
|
+
'dropped',
|
|
122
165
|
'already staged',
|
|
123
166
|
'read only',
|
|
124
|
-
'last block',
|
|
125
|
-
'first block',
|
|
126
|
-
'first file',
|
|
127
|
-
'last file',
|
|
128
167
|
'nothing to review',
|
|
168
|
+
'nothing to commit',
|
|
129
169
|
'unified',
|
|
130
170
|
'mixed',
|
|
131
171
|
'side-by-side',
|
|
132
172
|
'saved',
|
|
173
|
+
'reloaded',
|
|
133
174
|
'not a diff block',
|
|
175
|
+
'committed',
|
|
176
|
+
'amended',
|
|
177
|
+
'fixup',
|
|
178
|
+
'updated',
|
|
179
|
+
'updating reslop',
|
|
180
|
+
'loading',
|
|
181
|
+
'checking npm',
|
|
182
|
+
'npm i',
|
|
183
|
+
'npm uninstall',
|
|
184
|
+
'npm audit fix',
|
|
185
|
+
'pulling',
|
|
186
|
+
'pushing',
|
|
187
|
+
'pulled',
|
|
188
|
+
'pushed',
|
|
189
|
+
'force pushing',
|
|
190
|
+
'force pushed',
|
|
191
|
+
'checking out',
|
|
192
|
+
'creating branch',
|
|
193
|
+
'committing',
|
|
194
|
+
'checked out',
|
|
195
|
+
'created',
|
|
196
|
+
'empty branch name',
|
|
134
197
|
];
|
|
198
|
+
const INFO_PREFIX = ['checked out ', 'created '];
|
|
135
199
|
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
200
|
+
const QUIT_CHOICES = [
|
|
201
|
+
{ letter: 'f', label: 'finish as ready', lead: ' ' },
|
|
202
|
+
{ letter: 'c', label: 'continue next time', lead: ' ' },
|
|
203
|
+
{ letter: 'd', label: 'discard', lead: ' ' },
|
|
204
|
+
];
|
|
205
|
+
const QUIT_PROMPT = QUIT_CHOICES.map(
|
|
206
|
+
(choice) => `${choice.lead}${choice.label}`,
|
|
207
|
+
).join('');
|
|
208
|
+
const COMMIT_CHOICES = [
|
|
209
|
+
{ letter: 'c', label: 'commit', lead: ' ' },
|
|
210
|
+
{ letter: 'a', label: 'amend', lead: ' ' },
|
|
211
|
+
{ letter: 'f', label: 'fixup', lead: ' ' },
|
|
212
|
+
];
|
|
213
|
+
const COMMIT_PROMPT = COMMIT_CHOICES.map(
|
|
214
|
+
(choice) => `${choice.lead}${choice.label}`,
|
|
215
|
+
).join('');
|
|
216
|
+
const UPDATE_CHOICES = [
|
|
217
|
+
{ letter: 'y', label: 'y', lead: ' ' },
|
|
218
|
+
{ letter: 'n', label: 'n', lead: ' ' },
|
|
219
|
+
];
|
|
220
|
+
const updatePrompt = (from, to) => ` update reslop ${from} → ${to}? y n`;
|
|
221
|
+
const PUSH_CHOICES = [{ letter: 'f', label: 'force push', lead: ' ' }];
|
|
222
|
+
const PUSH_REJECT_PREFIX = ' Need force-push?';
|
|
223
|
+
const PUSH_PROMPT = `${PUSH_REJECT_PREFIX} force push`;
|
|
141
224
|
|
|
225
|
+
const NOTE_COMPOSE = ['feedback', 'commit'];
|
|
226
|
+
const LIST_PANES = ['files', 'branches'];
|
|
142
227
|
const GUTTER_W = 2;
|
|
143
228
|
const BUTTON_GAP = ' ';
|
|
144
229
|
const LOGO = '👁️';
|
|
@@ -340,6 +425,21 @@ const headerTarget = (view) => {
|
|
|
340
425
|
if (!entry) return { path: '', origin: '', n, total };
|
|
341
426
|
return { path: entry.path, origin: entry.status, n, total };
|
|
342
427
|
}
|
|
428
|
+
if (pane === 'branches') {
|
|
429
|
+
const compose = view.compose;
|
|
430
|
+
const branches = view.branches ?? [];
|
|
431
|
+
if (compose && compose.kind === 'branch') {
|
|
432
|
+
const total = branches.length + 1;
|
|
433
|
+
return { path: compose.text || '', origin: 'new', n: total, total };
|
|
434
|
+
}
|
|
435
|
+
const cursor = view.branchCursor ?? 0;
|
|
436
|
+
const entry = branches[cursor];
|
|
437
|
+
const total = branches.length;
|
|
438
|
+
const n = total ? cursor + 1 : 0;
|
|
439
|
+
if (!entry) return { path: '', origin: 'branch', n, total };
|
|
440
|
+
const origin = entry.current ? 'current' : 'branch';
|
|
441
|
+
return { path: entry.name, origin, n, total };
|
|
442
|
+
}
|
|
343
443
|
if (!view.item) return { path: '', origin: '', n: 0, total: view.total ?? 0 };
|
|
344
444
|
return {
|
|
345
445
|
path: itemPath(view.item),
|
|
@@ -414,17 +514,21 @@ const paintHeader = (view, width, color) => {
|
|
|
414
514
|
return `${out}${RESET}`;
|
|
415
515
|
};
|
|
416
516
|
|
|
417
|
-
const bodyLines = (item, layout) => {
|
|
517
|
+
const bodyLines = (item, layout, overlay) => {
|
|
418
518
|
if (item.file.isBinary) {
|
|
419
519
|
const text = '(binary)';
|
|
420
520
|
return [{ type: 'ctx', text, spans: [{ text, changed: false }] }];
|
|
421
521
|
}
|
|
422
522
|
if (!item.hunk) return [];
|
|
423
523
|
const radius = item.dep ? Number.MAX_SAFE_INTEGER : undefined;
|
|
424
|
-
return displayLines(item.hunk, item.blockId, layout, radius);
|
|
524
|
+
return displayLines(item.hunk, item.blockId, layout, radius, overlay);
|
|
425
525
|
};
|
|
426
526
|
|
|
427
|
-
const sourceEmpty = (line) =>
|
|
527
|
+
const sourceEmpty = (line) => {
|
|
528
|
+
if (!line) return true;
|
|
529
|
+
if (line.editStart !== undefined) return false;
|
|
530
|
+
return !(line.text ?? '').trim();
|
|
531
|
+
};
|
|
428
532
|
|
|
429
533
|
const displayRowEmpty = (row, split) => {
|
|
430
534
|
if (!split) return sourceEmpty(row);
|
|
@@ -470,29 +574,37 @@ const paintMarkedWord = (label, letter, color, mark) => {
|
|
|
470
574
|
return out;
|
|
471
575
|
};
|
|
472
576
|
|
|
473
|
-
const layoutButtons = (width, compact = false,
|
|
577
|
+
const layoutButtons = (width, compact = false, hidden = [], extra = []) => {
|
|
578
|
+
const ids = [];
|
|
579
|
+
for (const id of ACTION_IDS) {
|
|
580
|
+
if (id === 'quit') {
|
|
581
|
+
for (const add of extra) {
|
|
582
|
+
if (!hidden.includes(add)) ids.push(add);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
if (hidden.includes(id)) continue;
|
|
586
|
+
ids.push(id);
|
|
587
|
+
}
|
|
474
588
|
const build = (short) => {
|
|
475
589
|
const hits = [];
|
|
476
590
|
const parts = [];
|
|
477
591
|
let x = 0;
|
|
478
|
-
for (const id of
|
|
479
|
-
const
|
|
480
|
-
const
|
|
481
|
-
const word = buttonWord(action);
|
|
592
|
+
for (const id of ids) {
|
|
593
|
+
const letter = actionLetter(id);
|
|
594
|
+
const word = buttonWord(id);
|
|
482
595
|
const label = short ? letter : word;
|
|
483
596
|
const piece = `${BUTTON_GAP}${label}`;
|
|
484
597
|
const w = visibleWidth(piece);
|
|
485
598
|
if (x + w > width && parts.length) break;
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
parts.push({ action, piece, letter, label, disabled: isDisabled });
|
|
599
|
+
hits.push({ id, x0: x, x1: x + w });
|
|
600
|
+
parts.push({ action: { id }, piece, letter, label });
|
|
489
601
|
x += w;
|
|
490
602
|
}
|
|
491
603
|
return { hits, parts, width: x };
|
|
492
604
|
};
|
|
493
605
|
if (compact) return build(true);
|
|
494
606
|
const words = build(false);
|
|
495
|
-
if (words.parts.length ===
|
|
607
|
+
if (words.parts.length === ids.length) return words;
|
|
496
608
|
return build(true);
|
|
497
609
|
};
|
|
498
610
|
|
|
@@ -501,11 +613,7 @@ const paintButtons = (layout, width, color) => {
|
|
|
501
613
|
let used = 0;
|
|
502
614
|
for (const part of layout.parts) {
|
|
503
615
|
out += paint(BUTTON_GAP, THEME.buttonFg, THEME.buttonBg, color);
|
|
504
|
-
|
|
505
|
-
out += paint(part.label, THEME.buttonFg, THEME.buttonBg, color);
|
|
506
|
-
} else {
|
|
507
|
-
out += paintMarkedWord(part.label, part.letter, color, BUTTON_MARK);
|
|
508
|
-
}
|
|
616
|
+
out += paintMarkedWord(part.label, part.letter, color, BUTTON_MARK);
|
|
509
617
|
used += visibleWidth(part.piece);
|
|
510
618
|
}
|
|
511
619
|
if (used < width) {
|
|
@@ -514,6 +622,30 @@ const paintButtons = (layout, width, color) => {
|
|
|
514
622
|
return out;
|
|
515
623
|
};
|
|
516
624
|
|
|
625
|
+
const branchLabel = (name) => {
|
|
626
|
+
const branch = `${name ?? ''}`.trim();
|
|
627
|
+
if (!branch) return '';
|
|
628
|
+
return `[${branch}]`;
|
|
629
|
+
};
|
|
630
|
+
|
|
631
|
+
const splitBracketName = (label) => {
|
|
632
|
+
const text = `${label ?? ''}`;
|
|
633
|
+
const open = text.startsWith('[') ? '[' : '';
|
|
634
|
+
const rest = open ? text.slice(1) : text;
|
|
635
|
+
const close = rest.endsWith(']') ? ']' : '';
|
|
636
|
+
const name = close ? rest.slice(0, -1) : rest;
|
|
637
|
+
return { open, name, close };
|
|
638
|
+
};
|
|
639
|
+
|
|
640
|
+
const paintBracketName = (label, nameFg, wrapFg, bgRgb, color) => {
|
|
641
|
+
const { open, name, close } = splitBracketName(label);
|
|
642
|
+
return (
|
|
643
|
+
paint(open, wrapFg, bgRgb, color, true) +
|
|
644
|
+
paint(name, nameFg, bgRgb, color) +
|
|
645
|
+
paint(close, wrapFg, bgRgb, color, true)
|
|
646
|
+
);
|
|
647
|
+
};
|
|
648
|
+
|
|
517
649
|
const remoteCounts = (kind, { counts, notes, sourceLabel }) => {
|
|
518
650
|
const n = counts.pr ?? 0;
|
|
519
651
|
const label = sourceLabel || kind;
|
|
@@ -527,12 +659,14 @@ const COUNTS_KIND = {
|
|
|
527
659
|
const n = counts.commit ?? 0;
|
|
528
660
|
return ` commit ${revShort} ${n} ${notes}`;
|
|
529
661
|
},
|
|
530
|
-
worktree: ({ counts, notes }) => {
|
|
662
|
+
worktree: ({ counts, notes, branch }) => {
|
|
531
663
|
const staged = counts.staged ?? 0;
|
|
532
664
|
const unstaged = counts.unstaged ?? 0;
|
|
533
665
|
const untracked = counts.untracked ?? 0;
|
|
534
666
|
const gitCounts = `unstaged ${unstaged} untracked ${untracked}`;
|
|
535
667
|
const git = `staged ${staged} ${gitCounts}`;
|
|
668
|
+
const mark = branchLabel(branch);
|
|
669
|
+
if (mark) return ` ${mark} ${git} ${notes}`;
|
|
536
670
|
return ` ${git} ${notes}`;
|
|
537
671
|
},
|
|
538
672
|
};
|
|
@@ -543,37 +677,92 @@ const countsKind = (sourceKind, revShort) => {
|
|
|
543
677
|
return 'worktree';
|
|
544
678
|
};
|
|
545
679
|
|
|
546
|
-
const countsLine = (counts, revShort, sourceKind, sourceLabel) => {
|
|
680
|
+
const countsLine = (counts, revShort, sourceKind, sourceLabel, branch) => {
|
|
547
681
|
const feedback = counts.feedback ?? 0;
|
|
548
682
|
const todo = counts.todo ?? 0;
|
|
549
|
-
const
|
|
683
|
+
const code = counts.code ?? 0;
|
|
684
|
+
const notes = `feedback ${feedback} todo ${todo} code ${code}`;
|
|
550
685
|
const kind = countsKind(sourceKind, revShort);
|
|
551
|
-
return COUNTS_KIND[kind]({ counts, notes, sourceLabel, revShort });
|
|
686
|
+
return COUNTS_KIND[kind]({ counts, notes, sourceLabel, revShort, branch });
|
|
552
687
|
};
|
|
553
688
|
|
|
554
|
-
const
|
|
689
|
+
const paintChoicePrompt = (choices, width, color, prefix = '') => {
|
|
555
690
|
const bgRgb = THEME.chromeBg;
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
body
|
|
691
|
+
const labels = choices
|
|
692
|
+
.map((choice) => `${choice.lead}${choice.label}`)
|
|
693
|
+
.join('');
|
|
694
|
+
const body = `${prefix}${labels}`;
|
|
695
|
+
if (!color) {
|
|
696
|
+
const pad = Math.max(0, width - visibleWidth(body));
|
|
697
|
+
return body + ' '.repeat(pad);
|
|
698
|
+
}
|
|
699
|
+
let out = '';
|
|
700
|
+
if (prefix) out += paint(prefix, THEME.mutedFg, bgRgb, color);
|
|
701
|
+
for (const choice of choices) {
|
|
702
|
+
const label = `${choice.lead}${choice.label}`;
|
|
703
|
+
out += paintMarkedWord(label, choice.letter, color, QUIT_MARK);
|
|
563
704
|
}
|
|
564
705
|
const pad = Math.max(0, width - visibleWidth(body));
|
|
565
|
-
return
|
|
706
|
+
return out + fill(pad, THEME.mutedFg, bgRgb, color);
|
|
707
|
+
};
|
|
708
|
+
|
|
709
|
+
const isInfoStatus = (status) => {
|
|
710
|
+
if (INFO_STATUS.includes(status)) return true;
|
|
711
|
+
for (const prefix of INFO_PREFIX) {
|
|
712
|
+
if (status.startsWith(prefix)) return true;
|
|
713
|
+
}
|
|
714
|
+
return false;
|
|
715
|
+
};
|
|
716
|
+
|
|
717
|
+
const paintStatusLeft = (text, color) => {
|
|
718
|
+
const bgRgb = THEME.chromeBg;
|
|
719
|
+
const muted = THEME.mutedFg;
|
|
720
|
+
if (!color) return text;
|
|
721
|
+
const open = text.indexOf('[');
|
|
722
|
+
const close = text.indexOf(']');
|
|
723
|
+
if (open < 0 || close <= open) return paint(text, muted, bgRgb, color);
|
|
724
|
+
const before = text.slice(0, open);
|
|
725
|
+
const name = text.slice(open + 1, close);
|
|
726
|
+
const after = text.slice(close + 1);
|
|
727
|
+
const label = `[${name}]`;
|
|
728
|
+
return (
|
|
729
|
+
paint(before, muted, bgRgb, color) +
|
|
730
|
+
paintBracketName(label, THEME.buttonHotFg, muted, bgRgb, color) +
|
|
731
|
+
paint(after, muted, bgRgb, color)
|
|
732
|
+
);
|
|
566
733
|
};
|
|
567
734
|
|
|
568
735
|
const paintStatusLine = (view, status, width, color) => {
|
|
569
736
|
const { mode, counts, revShort, sourceKind, sourceLabel } = view;
|
|
570
|
-
if (mode === 'confirmQuit')
|
|
571
|
-
|
|
737
|
+
if (mode === 'confirmQuit') {
|
|
738
|
+
return paintChoicePrompt(QUIT_CHOICES, width, color);
|
|
739
|
+
}
|
|
740
|
+
if (mode === 'confirmCommit') {
|
|
741
|
+
return paintChoicePrompt(COMMIT_CHOICES, width, color);
|
|
742
|
+
}
|
|
743
|
+
if (mode === 'confirmUpdate') {
|
|
744
|
+
const from = view.updateFrom ?? '';
|
|
745
|
+
const to = view.updateTo ?? '';
|
|
746
|
+
const prefix = ` update reslop ${from} → ${to}?`;
|
|
747
|
+
return paintChoicePrompt(UPDATE_CHOICES, width, color, prefix);
|
|
748
|
+
}
|
|
749
|
+
if (mode === 'confirmPush') {
|
|
750
|
+
return paintChoicePrompt(PUSH_CHOICES, width, color, PUSH_REJECT_PREFIX);
|
|
751
|
+
}
|
|
752
|
+
const branch = view.branch ?? '';
|
|
753
|
+
const left = countsLine(
|
|
754
|
+
counts ?? {},
|
|
755
|
+
revShort,
|
|
756
|
+
sourceKind,
|
|
757
|
+
sourceLabel,
|
|
758
|
+
branch,
|
|
759
|
+
);
|
|
760
|
+
const shown = formatBusyStatus(status, view.progressFrame ?? 0);
|
|
572
761
|
let right = '';
|
|
573
762
|
let msgFg = THEME.chromeFg;
|
|
574
|
-
if (
|
|
575
|
-
right = ` ${
|
|
576
|
-
if (!
|
|
763
|
+
if (shown) {
|
|
764
|
+
right = ` ${shown} `;
|
|
765
|
+
if (!isInfoStatus(status)) msgFg = THEME.errorFg;
|
|
577
766
|
}
|
|
578
767
|
const rightW = visibleWidth(right);
|
|
579
768
|
const room = Math.max(0, width - rightW);
|
|
@@ -582,12 +771,39 @@ const paintStatusLine = (view, status, width, color) => {
|
|
|
582
771
|
const gap = ' '.repeat(pad);
|
|
583
772
|
const paintedRight = paint(right, msgFg, THEME.chromeBg, color);
|
|
584
773
|
return (
|
|
585
|
-
|
|
774
|
+
paintStatusLeft(clipped, color) +
|
|
586
775
|
paint(gap, THEME.mutedFg, THEME.chromeBg, color) +
|
|
587
776
|
paintedRight
|
|
588
777
|
);
|
|
589
778
|
};
|
|
590
779
|
|
|
780
|
+
const statusBranchHit = (view, width, y) => {
|
|
781
|
+
const mode = view.mode;
|
|
782
|
+
if (mode === 'confirmQuit') return null;
|
|
783
|
+
if (mode === 'confirmCommit') return null;
|
|
784
|
+
if (mode === 'confirmUpdate') return null;
|
|
785
|
+
if (mode === 'confirmPush') return null;
|
|
786
|
+
const branch = view.branch ?? '';
|
|
787
|
+
if (!branchLabel(branch)) return null;
|
|
788
|
+
if (countsKind(view.sourceKind, view.revShort) !== 'worktree') return null;
|
|
789
|
+
const left = countsLine(
|
|
790
|
+
view.counts ?? {},
|
|
791
|
+
view.revShort,
|
|
792
|
+
view.sourceKind,
|
|
793
|
+
view.sourceLabel,
|
|
794
|
+
branch,
|
|
795
|
+
);
|
|
796
|
+
const status = view.status ?? '';
|
|
797
|
+
const shown = formatBusyStatus(status, view.progressFrame ?? 0);
|
|
798
|
+
const right = shown ? ` ${shown} ` : '';
|
|
799
|
+
const room = Math.max(0, width - visibleWidth(right));
|
|
800
|
+
const clipped = truncateVisible(left, room);
|
|
801
|
+
const open = clipped.indexOf('[');
|
|
802
|
+
const close = clipped.indexOf(']');
|
|
803
|
+
if (open < 0 || close <= open) return null;
|
|
804
|
+
return { id: 'branch', y, x0: open, x1: close + 1 };
|
|
805
|
+
};
|
|
806
|
+
|
|
591
807
|
const signedCount = (sign, n) => `${sign}${n ?? 0}`;
|
|
592
808
|
|
|
593
809
|
const fileRowFields = (entry) => {
|
|
@@ -680,6 +896,12 @@ const paintFileRow = (entry, width, color, selected, cols) => {
|
|
|
680
896
|
|
|
681
897
|
const todoInnerWidth = (width) => Math.max(1, width - 2);
|
|
682
898
|
|
|
899
|
+
const codeInnerWidth = (width, layout = 'unified') => {
|
|
900
|
+
const lineW = layout === 'side' ? splitWidths(width).rightW : width;
|
|
901
|
+
const gutterW = Math.min(GUTTER_W, Math.max(0, lineW));
|
|
902
|
+
return Math.max(1, lineW - gutterW);
|
|
903
|
+
};
|
|
904
|
+
|
|
683
905
|
const checkLeadLen = (text) => {
|
|
684
906
|
const match = CHECK_LEAD.exec(text);
|
|
685
907
|
return match ? match[0].length : 0;
|
|
@@ -792,36 +1014,219 @@ const paintIdleNote = (view, width, color) => {
|
|
|
792
1014
|
|
|
793
1015
|
const paintNotePanel = (view, width, color, cap) => {
|
|
794
1016
|
const empty = { rows: [], cursor: null, templateHits: [] };
|
|
795
|
-
if ((view.pane ?? 'diff') === 'files') return empty;
|
|
796
1017
|
const compose = view.compose;
|
|
797
|
-
if (compose && compose.kind
|
|
1018
|
+
if (compose && NOTE_COMPOSE.includes(compose.kind)) {
|
|
798
1019
|
return paintComposeNote(view, width, color, cap);
|
|
799
1020
|
}
|
|
1021
|
+
const pane = view.pane ?? 'diff';
|
|
1022
|
+
if (LIST_PANES.includes(pane)) return empty;
|
|
800
1023
|
return paintIdleNote(view, width, color);
|
|
801
1024
|
};
|
|
802
1025
|
|
|
803
|
-
const
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
1026
|
+
const paintCursorList = (
|
|
1027
|
+
items,
|
|
1028
|
+
cursor,
|
|
1029
|
+
width,
|
|
1030
|
+
color,
|
|
1031
|
+
bodyH,
|
|
1032
|
+
headerLines,
|
|
1033
|
+
paintRow,
|
|
1034
|
+
tail,
|
|
1035
|
+
) => {
|
|
807
1036
|
const pad = bodyH >= FILE_LIST_PAD * 2 + 1 ? FILE_LIST_PAD : 0;
|
|
808
1037
|
const listH = bodyH - pad * 2;
|
|
809
|
-
const
|
|
1038
|
+
const extra = tail ? 1 : 0;
|
|
1039
|
+
const last = items.length + extra - 1;
|
|
1040
|
+
const focus = extra ? last : cursor;
|
|
1041
|
+
const start = focus >= listH ? focus - listH + 1 : 0;
|
|
810
1042
|
const blank = paintBodyFill(width, color, 'files');
|
|
811
1043
|
const body = [];
|
|
812
1044
|
const fileHits = [];
|
|
1045
|
+
let textCursor = null;
|
|
813
1046
|
if (pad) body.push(blank);
|
|
814
1047
|
for (let i = 0; i < listH; i++) {
|
|
815
1048
|
const idx = start + i;
|
|
816
|
-
|
|
1049
|
+
if (tail && idx === items.length) {
|
|
1050
|
+
body.push(tail.row);
|
|
1051
|
+
if (tail.cursor) {
|
|
1052
|
+
textCursor = { x: tail.cursor.x, row: body.length - 1 };
|
|
1053
|
+
}
|
|
1054
|
+
continue;
|
|
1055
|
+
}
|
|
1056
|
+
const entry = items[idx];
|
|
817
1057
|
if (!entry) break;
|
|
818
|
-
const selected = idx === cursor;
|
|
819
|
-
body.push(
|
|
1058
|
+
const selected = extra ? false : idx === cursor;
|
|
1059
|
+
body.push(paintRow(entry, width, color, selected));
|
|
820
1060
|
const y = headerLines + pad + i + 1;
|
|
821
1061
|
fileHits.push({ y, cursor: idx });
|
|
822
1062
|
}
|
|
823
1063
|
if (pad) body.push(blank);
|
|
824
|
-
return { body, fileHits };
|
|
1064
|
+
return { body, fileHits, cursor: textCursor };
|
|
1065
|
+
};
|
|
1066
|
+
|
|
1067
|
+
const paintBodyFiles = (view, width, color, bodyH, headerLines) => {
|
|
1068
|
+
const files = view.files ?? [];
|
|
1069
|
+
const cols = fileColumns(files);
|
|
1070
|
+
const cursor = view.fileCursor ?? 0;
|
|
1071
|
+
const paintRow = (entry, rowWidth, rowColor, selected) =>
|
|
1072
|
+
paintFileRow(entry, rowWidth, rowColor, selected, cols);
|
|
1073
|
+
return paintCursorList(
|
|
1074
|
+
files,
|
|
1075
|
+
cursor,
|
|
1076
|
+
width,
|
|
1077
|
+
color,
|
|
1078
|
+
bodyH,
|
|
1079
|
+
headerLines,
|
|
1080
|
+
paintRow,
|
|
1081
|
+
);
|
|
1082
|
+
};
|
|
1083
|
+
|
|
1084
|
+
const branchRowFg = (entry, selected) => {
|
|
1085
|
+
if (selected) return THEME.chromeFg;
|
|
1086
|
+
return THEME.mutedFg;
|
|
1087
|
+
};
|
|
1088
|
+
|
|
1089
|
+
const branchRowFields = (entry) => {
|
|
1090
|
+
const sha = entry.sha ?? '';
|
|
1091
|
+
const date = entry.date ?? '';
|
|
1092
|
+
const gone = entry.gone ? 'gone' : '';
|
|
1093
|
+
const ahead = entry.ahead ? `⇡${entry.ahead}` : '';
|
|
1094
|
+
const behind = entry.behind ? `⇣${entry.behind}` : '';
|
|
1095
|
+
return { sha, ahead, behind, gone, date };
|
|
1096
|
+
};
|
|
1097
|
+
|
|
1098
|
+
const branchColumns = (branches) => {
|
|
1099
|
+
const widths = { name: 0 };
|
|
1100
|
+
for (const key of BRANCH_COL_KEYS) widths[key] = 0;
|
|
1101
|
+
for (const entry of branches) {
|
|
1102
|
+
const nameW = visibleWidth(branchLabel(entry.name));
|
|
1103
|
+
if (nameW > widths.name) widths.name = nameW;
|
|
1104
|
+
const fields = branchRowFields(entry);
|
|
1105
|
+
for (const key of BRANCH_COL_KEYS) {
|
|
1106
|
+
const w = visibleWidth(fields[key]);
|
|
1107
|
+
if (w > widths[key]) widths[key] = w;
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
widths.name = Math.min(BRANCH_NAME_MAX, widths.name);
|
|
1111
|
+
return widths;
|
|
1112
|
+
};
|
|
1113
|
+
|
|
1114
|
+
const branchRowParts = (entry, cols) => {
|
|
1115
|
+
const fields = branchRowFields(entry);
|
|
1116
|
+
const parts = {};
|
|
1117
|
+
for (const key of BRANCH_COL_KEYS) {
|
|
1118
|
+
const width = cols?.[key] ?? visibleWidth(fields[key]);
|
|
1119
|
+
const align = BRANCH_COL_ALIGN[key];
|
|
1120
|
+
parts[key] = padVisible(fields[key], width, align);
|
|
1121
|
+
}
|
|
1122
|
+
return parts;
|
|
1123
|
+
};
|
|
1124
|
+
|
|
1125
|
+
const joinBranchSuffix = (parts, cols) => {
|
|
1126
|
+
const bits = [];
|
|
1127
|
+
for (const key of BRANCH_COL_KEYS) {
|
|
1128
|
+
if (!cols[key]) continue;
|
|
1129
|
+
bits.push(parts[key]);
|
|
1130
|
+
}
|
|
1131
|
+
if (!bits.length) return '';
|
|
1132
|
+
return ` ${bits.join(' ')}`;
|
|
1133
|
+
};
|
|
1134
|
+
|
|
1135
|
+
const paintBranchSuffix = (parts, cols, fgRgb, bgRgb, color) => {
|
|
1136
|
+
const gap = paint(' ', fgRgb, bgRgb, color);
|
|
1137
|
+
const bits = [];
|
|
1138
|
+
for (const key of BRANCH_COL_KEYS) {
|
|
1139
|
+
if (!cols[key]) continue;
|
|
1140
|
+
const tone = BRANCH_COL_FG[key] ?? fgRgb;
|
|
1141
|
+
bits.push(paint(parts[key], tone, bgRgb, color));
|
|
1142
|
+
}
|
|
1143
|
+
if (!bits.length) return '';
|
|
1144
|
+
return gap + bits.join(gap);
|
|
1145
|
+
};
|
|
1146
|
+
|
|
1147
|
+
const paintBranchRow = (entry, width, color, selected, cols) => {
|
|
1148
|
+
const inner = Math.max(1, width - 1);
|
|
1149
|
+
const mark = selected ? FILE_MARK : ' ';
|
|
1150
|
+
const prefix = ` ${mark} `;
|
|
1151
|
+
const parts = branchRowParts(entry, cols);
|
|
1152
|
+
const suffix = joinBranchSuffix(parts, cols);
|
|
1153
|
+
const rest = Math.max(1, inner - visibleWidth(prefix + suffix));
|
|
1154
|
+
const nameW = Math.min(cols.name, rest);
|
|
1155
|
+
const label = truncateVisible(branchLabel(entry.name), nameW);
|
|
1156
|
+
const namePad = Math.max(0, nameW - visibleWidth(label));
|
|
1157
|
+
const canSubject = rest >= nameW + 3;
|
|
1158
|
+
const gap = canSubject ? ' ' : '';
|
|
1159
|
+
const subjectW = canSubject ? rest - nameW - visibleWidth(gap) : 0;
|
|
1160
|
+
const subject = truncateVisible(entry.subject ?? '', subjectW);
|
|
1161
|
+
const mid = `${label}${' '.repeat(namePad)}${gap}${subject}`;
|
|
1162
|
+
const pad = Math.max(0, rest - visibleWidth(mid));
|
|
1163
|
+
const after = `${' '.repeat(namePad)}${gap}${subject}${' '.repeat(pad)}`;
|
|
1164
|
+
const left = `${prefix}${label}${after}`;
|
|
1165
|
+
const fgRgb = branchRowFg(entry, selected);
|
|
1166
|
+
const bgRgb = selected ? THEME.buttonBg : THEME.ctxBg;
|
|
1167
|
+
const nameFg = entry.isDefault ? THEME.headerFg : THEME.buttonHotFg;
|
|
1168
|
+
const nameBg = entry.isDefault ? THEME.buttonHotFg : bgRgb;
|
|
1169
|
+
const edge = fill(1, fgRgb, bgRgb, color);
|
|
1170
|
+
if (!color) return `${left}${suffix}${edge}`;
|
|
1171
|
+
let out = `${seq(fgRgb, bgRgb)}${EL}`;
|
|
1172
|
+
out += paint(prefix, fgRgb, bgRgb, color);
|
|
1173
|
+
out += paintBracketName(label, nameFg, THEME.mutedFg, nameBg, color);
|
|
1174
|
+
out += paint(after, fgRgb, bgRgb, color);
|
|
1175
|
+
out += paintBranchSuffix(parts, cols, fgRgb, bgRgb, color);
|
|
1176
|
+
return out + edge;
|
|
1177
|
+
};
|
|
1178
|
+
|
|
1179
|
+
const paintBranchEdit = (compose, width, color) => {
|
|
1180
|
+
const inner = Math.max(1, width - 1);
|
|
1181
|
+
const prefix = ` ${FILE_MARK} `;
|
|
1182
|
+
const prefixW = visibleWidth(prefix);
|
|
1183
|
+
const rest = Math.max(1, inner - prefixW);
|
|
1184
|
+
const text = compose.text ?? '';
|
|
1185
|
+
const label = `[${text}]`;
|
|
1186
|
+
const clipped = truncateVisible(label, rest);
|
|
1187
|
+
const pad = Math.max(0, rest - visibleWidth(clipped));
|
|
1188
|
+
const left = `${prefix}${clipped}${' '.repeat(pad)}`;
|
|
1189
|
+
const fgRgb = THEME.chromeFg;
|
|
1190
|
+
const bgRgb = THEME.buttonBg;
|
|
1191
|
+
const edge = fill(1, fgRgb, bgRgb, color);
|
|
1192
|
+
let row = `${left}${edge}`;
|
|
1193
|
+
if (color) {
|
|
1194
|
+
const gap = ' '.repeat(pad);
|
|
1195
|
+
row = `${seq(fgRgb, bgRgb)}${EL}`;
|
|
1196
|
+
row += paint(prefix, fgRgb, bgRgb, color);
|
|
1197
|
+
row += paintBracketName(
|
|
1198
|
+
clipped,
|
|
1199
|
+
THEME.buttonHotFg,
|
|
1200
|
+
THEME.mutedFg,
|
|
1201
|
+
bgRgb,
|
|
1202
|
+
color,
|
|
1203
|
+
);
|
|
1204
|
+
row += paint(gap, fgRgb, bgRgb, color);
|
|
1205
|
+
row += edge;
|
|
1206
|
+
}
|
|
1207
|
+
const col = Math.min((compose.cursor ?? 0) + 1, visibleWidth(clipped));
|
|
1208
|
+
return { row, cursor: { x: prefixW + col + 1 } };
|
|
1209
|
+
};
|
|
1210
|
+
|
|
1211
|
+
const paintBodyBranches = (view, width, color, bodyH, headerLines) => {
|
|
1212
|
+
const branches = view.branches ?? [];
|
|
1213
|
+
const cols = branchColumns(branches);
|
|
1214
|
+
const cursor = view.branchCursor ?? 0;
|
|
1215
|
+
const compose = view.compose;
|
|
1216
|
+
const editing = compose && compose.kind === 'branch';
|
|
1217
|
+
const tail = editing ? paintBranchEdit(compose, width, color) : null;
|
|
1218
|
+
const paintRow = (entry, rowWidth, rowColor, selected) =>
|
|
1219
|
+
paintBranchRow(entry, rowWidth, rowColor, selected, cols);
|
|
1220
|
+
return paintCursorList(
|
|
1221
|
+
branches,
|
|
1222
|
+
cursor,
|
|
1223
|
+
width,
|
|
1224
|
+
color,
|
|
1225
|
+
bodyH,
|
|
1226
|
+
headerLines,
|
|
1227
|
+
paintRow,
|
|
1228
|
+
tail,
|
|
1229
|
+
);
|
|
825
1230
|
};
|
|
826
1231
|
|
|
827
1232
|
const paintTodoCaption = (width, color) =>
|
|
@@ -851,34 +1256,93 @@ const wrapSoftLine = (line, width) => {
|
|
|
851
1256
|
const gutterW = Math.min(GUTTER_W, Math.max(0, width));
|
|
852
1257
|
const budget = Math.max(1, width - gutterW);
|
|
853
1258
|
const rows = wrapPlain(line.text ?? '', budget);
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
1259
|
+
const changed = line.wrap === true;
|
|
1260
|
+
const pieces = [];
|
|
1261
|
+
let used = 0;
|
|
1262
|
+
for (let i = 0; i < rows.length; i++) {
|
|
1263
|
+
const text = rows[i];
|
|
1264
|
+
const last = i === rows.length - 1;
|
|
1265
|
+
const spans = [{ text, changed }];
|
|
1266
|
+
const next = { ...line, text, spans, editLast: last };
|
|
1267
|
+
if (line.editStart !== undefined) {
|
|
1268
|
+
next.editStart = line.editStart + used;
|
|
1269
|
+
if (last) next.editEnd = line.editEnd;
|
|
1270
|
+
else next.editEnd = next.editStart + text.length;
|
|
1271
|
+
}
|
|
1272
|
+
used += text.length;
|
|
1273
|
+
pieces.push(next);
|
|
1274
|
+
}
|
|
1275
|
+
return pieces;
|
|
1276
|
+
};
|
|
1277
|
+
|
|
1278
|
+
const shouldWrap = (line) => {
|
|
1279
|
+
if (!line) return false;
|
|
1280
|
+
if (SOFT_WRAP.includes(line.type)) return true;
|
|
1281
|
+
return line.wrap === true;
|
|
858
1282
|
};
|
|
859
1283
|
|
|
860
1284
|
const expandSoftRows = (lines, width, split) => {
|
|
861
|
-
const
|
|
1285
|
+
const { leftW, rightW } = splitWidths(width);
|
|
862
1286
|
const out = [];
|
|
863
1287
|
for (const row of lines) {
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
1288
|
+
if (!split) {
|
|
1289
|
+
if (!shouldWrap(row)) {
|
|
1290
|
+
out.push(row);
|
|
1291
|
+
continue;
|
|
1292
|
+
}
|
|
1293
|
+
for (const part of wrapSoftLine(row, width)) out.push(part);
|
|
867
1294
|
continue;
|
|
868
1295
|
}
|
|
869
|
-
const
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
1296
|
+
const right = row.right;
|
|
1297
|
+
if (shouldWrap(right)) {
|
|
1298
|
+
const parts = wrapSoftLine(right, rightW);
|
|
1299
|
+
out.push({ left: row.left, right: parts[0] });
|
|
1300
|
+
for (let i = 1; i < parts.length; i++) {
|
|
1301
|
+
out.push({ left: null, right: parts[i] });
|
|
1302
|
+
}
|
|
1303
|
+
continue;
|
|
873
1304
|
}
|
|
1305
|
+
const left = row.left;
|
|
1306
|
+
if (shouldWrap(left)) {
|
|
1307
|
+
const parts = wrapSoftLine(left, leftW);
|
|
1308
|
+
for (const part of parts) out.push({ left: part, right: part });
|
|
1309
|
+
continue;
|
|
1310
|
+
}
|
|
1311
|
+
out.push(row);
|
|
874
1312
|
}
|
|
875
1313
|
return out;
|
|
876
1314
|
};
|
|
877
1315
|
|
|
878
|
-
const
|
|
1316
|
+
const onCodePiece = (line, cursor) => {
|
|
1317
|
+
if (!line || line.editStart === undefined) return false;
|
|
1318
|
+
if (cursor < line.editStart) return false;
|
|
1319
|
+
if (line.editLast) return cursor <= line.editEnd;
|
|
1320
|
+
return cursor < line.editEnd;
|
|
1321
|
+
};
|
|
1322
|
+
|
|
1323
|
+
const codeCursorInRows = (rows, split, width, cursor) => {
|
|
1324
|
+
if (cursor === undefined || cursor === null) return null;
|
|
1325
|
+
const { leftW, rightW } = splitWidths(width);
|
|
1326
|
+
for (let i = 0; i < rows.length; i++) {
|
|
1327
|
+
const line = split ? rows[i].right : rows[i];
|
|
1328
|
+
if (!onCodePiece(line, cursor)) continue;
|
|
1329
|
+
const lineW = split ? rightW : width;
|
|
1330
|
+
const gutterW = Math.min(GUTTER_W, Math.max(0, lineW));
|
|
1331
|
+
const take = Math.min(cursor - line.editStart, line.text.length);
|
|
1332
|
+
const col = visibleWidth(line.text.slice(0, take));
|
|
1333
|
+
const x0 = split ? leftW + 1 : 0;
|
|
1334
|
+
return { x: x0 + gutterW + col + 1, row: i };
|
|
1335
|
+
}
|
|
1336
|
+
return null;
|
|
1337
|
+
};
|
|
1338
|
+
|
|
1339
|
+
const paintBodyDiff = (view, width, color) => {
|
|
1340
|
+
const item = view.item;
|
|
1341
|
+
const layout = view.layout ?? 'unified';
|
|
1342
|
+
const overlay = view.codeOverlay;
|
|
879
1343
|
const lang = detectLang(itemPath(item));
|
|
880
1344
|
const splitBody = layout === 'side' && !item.file.isBinary;
|
|
881
|
-
const raw = dropLeadingEmpty(bodyLines(item, layout), splitBody);
|
|
1345
|
+
const raw = dropLeadingEmpty(bodyLines(item, layout, overlay), splitBody);
|
|
882
1346
|
const lines = expandSoftRows(raw, width, splitBody);
|
|
883
1347
|
const body = [];
|
|
884
1348
|
if (splitBody) {
|
|
@@ -890,11 +1354,14 @@ const paintBodyDiff = (item, layout, width, color) => {
|
|
|
890
1354
|
body.push(paintDiffLine(line, width, color, item.origin, lang));
|
|
891
1355
|
}
|
|
892
1356
|
}
|
|
893
|
-
|
|
1357
|
+
const edit = overlay ? overlay.cursor : null;
|
|
1358
|
+
const cursor = codeCursorInRows(lines, splitBody, width, edit);
|
|
1359
|
+
return { body, splitBody, cursor };
|
|
894
1360
|
};
|
|
895
1361
|
|
|
896
1362
|
const bodyKind = (view) => {
|
|
897
|
-
|
|
1363
|
+
const pane = view.pane ?? 'diff';
|
|
1364
|
+
if (pane === 'files' || pane === 'branches') return pane;
|
|
898
1365
|
if (!view.item) return 'empty';
|
|
899
1366
|
if (view.item.origin === 'todo') return 'todo';
|
|
900
1367
|
return 'diff';
|
|
@@ -905,13 +1372,17 @@ const BODY_PAINT = {
|
|
|
905
1372
|
...paintBodyFiles(view, width, color, bodyH, headerLines),
|
|
906
1373
|
todoOwners: [],
|
|
907
1374
|
}),
|
|
1375
|
+
branches: (view, width, color, bodyH, headerLines) => ({
|
|
1376
|
+
...paintBodyBranches(view, width, color, bodyH, headerLines),
|
|
1377
|
+
todoOwners: [],
|
|
1378
|
+
}),
|
|
908
1379
|
empty: () => ({ body: [], fileHits: [], todoOwners: [] }),
|
|
909
1380
|
todo: (view, width, color) => ({
|
|
910
1381
|
...paintBodyTodo(view, width, color),
|
|
911
1382
|
fileHits: [],
|
|
912
1383
|
}),
|
|
913
1384
|
diff: (view, width, color) => ({
|
|
914
|
-
...paintBodyDiff(view
|
|
1385
|
+
...paintBodyDiff(view, width, color),
|
|
915
1386
|
fileHits: [],
|
|
916
1387
|
todoOwners: [],
|
|
917
1388
|
}),
|
|
@@ -954,7 +1425,7 @@ const windowBody = (view, width, color, bodyH, headerLines, paintedBody) => {
|
|
|
954
1425
|
const todoOwners = paintedBody.todoOwners;
|
|
955
1426
|
const splitBody = paintedBody.splitBody === true;
|
|
956
1427
|
const pane = view.pane ?? 'diff';
|
|
957
|
-
const listPane = pane
|
|
1428
|
+
const listPane = LIST_PANES.includes(pane);
|
|
958
1429
|
const fillKind = bodyFillKind(listPane, splitBody);
|
|
959
1430
|
let shift = 0;
|
|
960
1431
|
if (!listPane && body.length > 0) {
|
|
@@ -1006,7 +1477,15 @@ const stackFrame = (view, width, color, rows, layout, windowed, notes) => {
|
|
|
1006
1477
|
return overlayRows(rows, view.selection);
|
|
1007
1478
|
};
|
|
1008
1479
|
|
|
1009
|
-
const frameResult = (
|
|
1480
|
+
const frameResult = (
|
|
1481
|
+
text,
|
|
1482
|
+
painted,
|
|
1483
|
+
layout,
|
|
1484
|
+
windowed,
|
|
1485
|
+
hits,
|
|
1486
|
+
bodyH,
|
|
1487
|
+
statusHits,
|
|
1488
|
+
) => {
|
|
1010
1489
|
const fileHits = windowed.fileHits;
|
|
1011
1490
|
const todoHits = windowed.todoHits;
|
|
1012
1491
|
const templateHits = hits.templateHits;
|
|
@@ -1020,6 +1499,7 @@ const frameResult = (text, painted, layout, windowed, hits, bodyH) => {
|
|
|
1020
1499
|
fileHits,
|
|
1021
1500
|
todoHits,
|
|
1022
1501
|
templateHits,
|
|
1502
|
+
statusHits,
|
|
1023
1503
|
footerY,
|
|
1024
1504
|
height: painted.length,
|
|
1025
1505
|
scroll,
|
|
@@ -1028,14 +1508,18 @@ const frameResult = (text, painted, layout, windowed, hits, bodyH) => {
|
|
|
1028
1508
|
};
|
|
1029
1509
|
};
|
|
1030
1510
|
|
|
1511
|
+
const hiddenActions = (view) => {
|
|
1512
|
+
const pane = view.pane ?? 'diff';
|
|
1513
|
+
return disabledActions(pane, view.item);
|
|
1514
|
+
};
|
|
1515
|
+
|
|
1031
1516
|
const renderFrame = (view, options = {}) => {
|
|
1032
1517
|
const width = Math.max(20, options.width ?? 80);
|
|
1033
1518
|
const height = Math.max(6, options.height ?? 24);
|
|
1034
1519
|
const color = options.color ?? true;
|
|
1035
1520
|
const rows = [paintHeader(view, width, color)];
|
|
1036
|
-
const
|
|
1037
|
-
const
|
|
1038
|
-
const layout = layoutButtons(width, false, disabled);
|
|
1521
|
+
const extra = view.pane === 'branches' ? Object.keys(BRANCH_ACTIONS) : [];
|
|
1522
|
+
const layout = layoutButtons(width, false, hiddenActions(view), extra);
|
|
1039
1523
|
const notes = frameNotes(view, width, height, color);
|
|
1040
1524
|
const headerLines = rows.length;
|
|
1041
1525
|
const bodyH = notes.bodyH;
|
|
@@ -1056,7 +1540,10 @@ const renderFrame = (view, options = {}) => {
|
|
|
1056
1540
|
notes.noteCursor,
|
|
1057
1541
|
bodyH,
|
|
1058
1542
|
);
|
|
1059
|
-
|
|
1543
|
+
const statusY = painted.length - 1;
|
|
1544
|
+
const branchHit = statusBranchHit(view, width, statusY);
|
|
1545
|
+
const statusHits = branchHit ? [branchHit] : [];
|
|
1546
|
+
return frameResult(text, painted, layout, windowed, hits, bodyH, statusHits);
|
|
1060
1547
|
};
|
|
1061
1548
|
|
|
1062
1549
|
const presentCursor = (cursor) => {
|
|
@@ -1080,9 +1567,10 @@ const presentRows = (rows, options = {}) => {
|
|
|
1080
1567
|
};
|
|
1081
1568
|
|
|
1082
1569
|
module.exports = {
|
|
1083
|
-
QUIT_FINISH,
|
|
1084
|
-
QUIT_CONTINUE,
|
|
1085
1570
|
QUIT_PROMPT,
|
|
1571
|
+
COMMIT_PROMPT,
|
|
1572
|
+
PUSH_PROMPT,
|
|
1573
|
+
updatePrompt,
|
|
1086
1574
|
DEL_CHAR_BG,
|
|
1087
1575
|
ADD_CHAR_BG,
|
|
1088
1576
|
paintDiffLine,
|
|
@@ -1090,6 +1578,8 @@ module.exports = {
|
|
|
1090
1578
|
layoutButtons,
|
|
1091
1579
|
todoInnerWidth,
|
|
1092
1580
|
noteInnerWidth,
|
|
1581
|
+
codeInnerWidth,
|
|
1582
|
+
formatBusyStatus,
|
|
1093
1583
|
renderFrame,
|
|
1094
1584
|
presentCursor,
|
|
1095
1585
|
presentRows,
|