reslop 0.1.7 → 0.1.9

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.
Files changed (98) hide show
  1. package/CHANGELOG.md +32 -1
  2. package/README.md +26 -38
  3. package/bin/reslop.js +3 -1
  4. package/lib/ansi.js +2 -0
  5. package/lib/capabilities.js +109 -0
  6. package/lib/cli.js +18 -14
  7. package/lib/deps/changes.js +422 -0
  8. package/lib/deps/items.js +134 -0
  9. package/lib/deps/manifest.js +152 -0
  10. package/lib/deps/merge.js +120 -0
  11. package/lib/deps/proposals.js +164 -0
  12. package/lib/deps/reports.js +180 -0
  13. package/lib/deps/rows.js +176 -0
  14. package/lib/deps/scan.js +138 -0
  15. package/lib/deps/usage.js +68 -0
  16. package/lib/deps/versions.js +119 -0
  17. package/lib/deps.js +117 -1345
  18. package/lib/diff/align.js +226 -0
  19. package/lib/diff/display.js +101 -0
  20. package/lib/diff/edit.js +47 -0
  21. package/lib/diff/inline.js +300 -0
  22. package/lib/diff/items.js +101 -0
  23. package/lib/diff/parse.js +156 -0
  24. package/lib/diff/patch.js +103 -0
  25. package/lib/diff.js +13 -932
  26. package/lib/editor.js +37 -0
  27. package/lib/files.js +118 -4
  28. package/lib/git/branches.js +383 -0
  29. package/lib/git/changes.js +164 -0
  30. package/lib/git/dependencies.js +489 -0
  31. package/lib/git/proc.js +162 -0
  32. package/lib/git/read.js +253 -0
  33. package/lib/git/run.js +100 -0
  34. package/lib/git.js +88 -1193
  35. package/lib/github.js +92 -222
  36. package/lib/gitlab.js +72 -216
  37. package/lib/highlight/bash.js +5 -9
  38. package/lib/highlight/core.js +1 -1
  39. package/lib/highlight/css.js +1 -3
  40. package/lib/highlight/csv.js +1 -3
  41. package/lib/highlight/dart.js +6 -20
  42. package/lib/highlight/dot.js +13 -13
  43. package/lib/highlight/html.js +1 -5
  44. package/lib/highlight/js.js +151 -115
  45. package/lib/highlight/json.js +1 -3
  46. package/lib/highlight/log.js +1 -3
  47. package/lib/highlight/txt.js +5 -10
  48. package/lib/highlight.js +4 -4
  49. package/lib/keys.js +67 -168
  50. package/lib/npm/commands.js +100 -0
  51. package/lib/remote/changes.js +37 -0
  52. package/lib/remote/headers.js +36 -0
  53. package/lib/remote/http.js +122 -0
  54. package/lib/remote/notes.js +94 -0
  55. package/lib/render/branches.js +240 -0
  56. package/lib/render/chrome.js +595 -0
  57. package/lib/render/commits.js +138 -0
  58. package/lib/render/diff.js +307 -0
  59. package/lib/render/files.js +186 -0
  60. package/lib/render/frame.js +202 -0
  61. package/lib/render/notes.js +201 -0
  62. package/lib/render/present.js +25 -0
  63. package/lib/render/primitives.js +200 -0
  64. package/lib/render.js +13 -1688
  65. package/lib/review/format.js +434 -0
  66. package/lib/review/storage.js +150 -0
  67. package/lib/review/store.js +178 -0
  68. package/lib/review/templates.js +49 -0
  69. package/lib/review.js +14 -747
  70. package/lib/select.js +3 -5
  71. package/lib/session/accessors.js +25 -0
  72. package/lib/session/actions.js +316 -0
  73. package/lib/session/branches.js +302 -0
  74. package/lib/session/change-actions.js +323 -0
  75. package/lib/session/commit.js +286 -0
  76. package/lib/session/compose.js +540 -0
  77. package/lib/session/dispatch.js +115 -0
  78. package/lib/session/files-pane.js +243 -0
  79. package/lib/session/items.js +282 -0
  80. package/lib/session/lifecycle.js +516 -0
  81. package/lib/session/load.js +85 -0
  82. package/lib/session/navigation.js +94 -0
  83. package/lib/session/ops.js +110 -0
  84. package/lib/session/origins.js +7 -0
  85. package/lib/session/pointer.js +161 -0
  86. package/lib/session/progress.js +29 -0
  87. package/lib/session/review.js +88 -0
  88. package/lib/session/templates.js +64 -0
  89. package/lib/session/terminal.js +169 -0
  90. package/lib/session/todos.js +269 -0
  91. package/lib/session/update.js +125 -0
  92. package/lib/session/view.js +131 -0
  93. package/lib/session/watch.js +219 -0
  94. package/lib/session.js +598 -2350
  95. package/lib/source.js +11 -11
  96. package/lib/sys.js +25 -4
  97. package/lib/wrap.js +4 -2
  98. package/package.json +2 -2
@@ -0,0 +1,101 @@
1
+ 'use strict';
2
+
3
+ const patch = require('./patch.js');
4
+ const { splitHunk, formatHunkHeader, formatPatch } = patch;
5
+
6
+ const INDEX_SIDE = {
7
+ staged: 'new',
8
+ unstaged: 'old',
9
+ untracked: 'old',
10
+ commit: 'old',
11
+ pr: 'old',
12
+ };
13
+
14
+ const splitLines = (content) => {
15
+ if (content === '') return { lines: [''], noNl: true };
16
+ const parts = content.split('\n');
17
+ const noNl = parts.at(-1) !== '';
18
+ if (!noNl) parts.pop();
19
+ return { lines: parts, noNl };
20
+ };
21
+
22
+ const synthesizeNewFile = (relPath, content) => {
23
+ const { lines, noNl } = splitLines(content);
24
+ const body = lines.map((text, index) => ({
25
+ type: 'add',
26
+ text,
27
+ noNl: noNl && index === lines.length - 1,
28
+ blockId: 0,
29
+ }));
30
+ const oldStart = 0;
31
+ const oldCount = 0;
32
+ const newStart = 1;
33
+ const newCount = body.length;
34
+ const header = formatHunkHeader(oldStart, oldCount, newStart, newCount);
35
+ const hunk = { oldStart, oldCount, newStart, newCount, header, lines: body };
36
+ const preamble = [
37
+ `diff --git a/${relPath} b/${relPath}`,
38
+ 'new file mode 100644',
39
+ '--- /dev/null',
40
+ `+++ b/${relPath}`,
41
+ ];
42
+ return {
43
+ oldPath: relPath,
44
+ newPath: relPath,
45
+ isNew: true,
46
+ isDeleted: false,
47
+ isBinary: false,
48
+ preamble,
49
+ hunks: [hunk],
50
+ };
51
+ };
52
+
53
+ const itemsFromFiles = (files, origin) => {
54
+ const items = [];
55
+ for (const file of files) {
56
+ if (file.isBinary) {
57
+ const hunk = null;
58
+ const blockId = null;
59
+ const patchAdd = '';
60
+ const patchRevert = '';
61
+ items.push({ origin, file, hunk, blockId, patchAdd, patchRevert });
62
+ continue;
63
+ }
64
+ for (const rawHunk of file.hunks) {
65
+ const { hunk, blocks } = splitHunk(rawHunk);
66
+ for (const block of blocks) {
67
+ const blockId = block.id;
68
+ const indexSide = INDEX_SIDE[origin] ?? 'old';
69
+ const patchAdd = formatPatch(file, hunk, blockId, indexSide);
70
+ const patchRevert = formatPatch(file, hunk, blockId, 'new');
71
+ items.push({ origin, file, hunk, blockId, patchAdd, patchRevert });
72
+ }
73
+ }
74
+ }
75
+ return items;
76
+ };
77
+
78
+ const refreshIndexPatches = (items, item) => {
79
+ if (!item.hunk || !item.file) return items;
80
+ const sides = {};
81
+ for (const other of items) {
82
+ if (other.hunk !== item.hunk || other.file !== item.file) continue;
83
+ sides[other.blockId] = INDEX_SIDE[other.origin] ?? 'old';
84
+ }
85
+ const next = [];
86
+ for (const other of items) {
87
+ if (other.hunk !== item.hunk || other.file !== item.file) {
88
+ next.push(other);
89
+ continue;
90
+ }
91
+ const patchAdd = formatPatch(other.file, other.hunk, other.blockId, sides);
92
+ next.push({ ...other, patchAdd });
93
+ }
94
+ return next;
95
+ };
96
+
97
+ module.exports = {
98
+ synthesizeNewFile,
99
+ itemsFromFiles,
100
+ refreshIndexPatches,
101
+ };
@@ -0,0 +1,156 @@
1
+ 'use strict';
2
+
3
+ const LINE_TYPE = {
4
+ '+': 'add',
5
+ '-': 'del',
6
+ ' ': 'ctx',
7
+ };
8
+
9
+ const parseGitPaths = (line) => {
10
+ const rest = line.slice('diff --git '.length);
11
+ const mid = rest.lastIndexOf(' b/');
12
+ if (mid < 0 || !rest.startsWith('a/')) return { oldPath: '', newPath: '' };
13
+ const oldPath = rest.slice(2, mid);
14
+ const newPath = rest.slice(mid + 3);
15
+ return { oldPath, newPath };
16
+ };
17
+
18
+ const stripAb = (raw) => {
19
+ if (raw === '/dev/null') return raw;
20
+ if (raw.startsWith('a/') || raw.startsWith('b/')) return raw.slice(2);
21
+ return raw;
22
+ };
23
+
24
+ const parsePathLine = (line) => {
25
+ const space = line.indexOf(' ');
26
+ if (space < 0) return '';
27
+ let rest = line.slice(space + 1);
28
+ if (rest.startsWith('"') && rest.endsWith('"')) {
29
+ rest = rest.slice(1, -1);
30
+ }
31
+ const tab = rest.indexOf('\t');
32
+ if (tab >= 0) rest = rest.slice(0, tab);
33
+ return stripAb(rest);
34
+ };
35
+
36
+ const parseHunkHeader = (line) => {
37
+ const match = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@(.*)$/.exec(line);
38
+ if (!match) return null;
39
+ const oldStart = parseInt(match[1], 10);
40
+ const oldCount = match[2] === undefined ? 1 : parseInt(match[2], 10);
41
+ const newStart = parseInt(match[3], 10);
42
+ const newCount = match[4] === undefined ? 1 : parseInt(match[4], 10);
43
+ return { oldStart, oldCount, newStart, newCount, header: line };
44
+ };
45
+
46
+ const closeFile = (file, files) => {
47
+ if (file) files.push(file);
48
+ };
49
+
50
+ const createFile = (line) => {
51
+ const paths = parseGitPaths(line);
52
+ return {
53
+ oldPath: paths.oldPath,
54
+ newPath: paths.newPath,
55
+ isNew: false,
56
+ isDeleted: false,
57
+ isBinary: false,
58
+ preamble: [line],
59
+ hunks: [],
60
+ };
61
+ };
62
+
63
+ const startGitFile = (line, ctx) => {
64
+ ctx.hunk = null;
65
+ closeFile(ctx.file, ctx.files);
66
+ ctx.file = createFile(line);
67
+ };
68
+
69
+ const markBinary = (line, ctx) => {
70
+ ctx.file.isBinary = true;
71
+ ctx.file.preamble.push(line);
72
+ ctx.hunk = null;
73
+ };
74
+
75
+ const markNewFile = (line, ctx) => {
76
+ ctx.file.isNew = true;
77
+ ctx.file.preamble.push(line);
78
+ };
79
+
80
+ const markDeleted = (line, ctx) => {
81
+ ctx.file.isDeleted = true;
82
+ ctx.file.preamble.push(line);
83
+ };
84
+
85
+ const setOldPath = (line, ctx) => {
86
+ const parsed = parsePathLine(line);
87
+ if (parsed === '/dev/null') ctx.file.isNew = true;
88
+ else ctx.file.oldPath = parsed;
89
+ ctx.file.preamble.push(line);
90
+ };
91
+
92
+ const setNewPath = (line, ctx) => {
93
+ const parsed = parsePathLine(line);
94
+ if (parsed === '/dev/null') ctx.file.isDeleted = true;
95
+ else ctx.file.newPath = parsed;
96
+ ctx.file.preamble.push(line);
97
+ };
98
+
99
+ const startHunk = (line, ctx) => {
100
+ const meta = parseHunkHeader(line);
101
+ if (!meta) return;
102
+ ctx.hunk = { ...meta, lines: [] };
103
+ ctx.file.hunks.push(ctx.hunk);
104
+ };
105
+
106
+ const DIFF_PREFIX = [
107
+ { prefix: 'diff --git ', apply: startGitFile, needFile: false },
108
+ { prefix: 'Binary files ', apply: markBinary, needFile: true },
109
+ { prefix: 'GIT binary', apply: markBinary, needFile: true },
110
+ { prefix: 'new file mode', apply: markNewFile, needFile: true },
111
+ { prefix: 'deleted file mode', apply: markDeleted, needFile: true },
112
+ { prefix: '--- ', apply: setOldPath, needFile: true },
113
+ { prefix: '+++ ', apply: setNewPath, needFile: true },
114
+ { prefix: '@@ ', apply: startHunk, needFile: true },
115
+ ];
116
+
117
+ const takeHunkLine = (line, hunk) => {
118
+ if (line.startsWith('\\')) {
119
+ const prev = hunk.lines[hunk.lines.length - 1];
120
+ if (prev) prev.noNl = true;
121
+ return;
122
+ }
123
+ const type = LINE_TYPE[line[0]];
124
+ if (type) {
125
+ hunk.lines.push({ type, text: line.slice(1), noNl: false });
126
+ return;
127
+ }
128
+ hunk.lines.push({ type: 'ctx', text: line, noNl: false });
129
+ };
130
+
131
+ const parseDiff = (text) => {
132
+ const files = [];
133
+ if (!text) return files;
134
+ const rawLines = text.split('\n');
135
+ if (rawLines.at(-1) === '') rawLines.pop();
136
+ const ctx = { files, file: null, hunk: null };
137
+ for (const raw of rawLines) {
138
+ const line = raw.endsWith('\r') ? raw.slice(0, -1) : raw;
139
+ const rule = DIFF_PREFIX.find((entry) => line.startsWith(entry.prefix));
140
+ if (rule) {
141
+ if (rule.needFile && !ctx.file) continue;
142
+ rule.apply(line, ctx);
143
+ continue;
144
+ }
145
+ if (!ctx.file) continue;
146
+ if (!ctx.hunk) {
147
+ ctx.file.preamble.push(line);
148
+ continue;
149
+ }
150
+ takeHunkLine(line, ctx.hunk);
151
+ }
152
+ closeFile(ctx.file, files);
153
+ return files;
154
+ };
155
+
156
+ module.exports = { parseDiff };
@@ -0,0 +1,103 @@
1
+ 'use strict';
2
+
3
+ const { isCtxType } = require('./align.js');
4
+
5
+ const LINE_MARK = {
6
+ add: '+',
7
+ del: '-',
8
+ ctx: ' ',
9
+ warn: ' ',
10
+ note: ' ',
11
+ noteSep: ' ',
12
+ };
13
+
14
+ const splitHunk = (hunk) => {
15
+ const { oldStart, oldCount, newStart, newCount, header } = hunk;
16
+ const blocks = [];
17
+ let current = null;
18
+ let blockId = -1;
19
+ const lines = hunk.lines.map((line) => {
20
+ if (isCtxType(line.type)) {
21
+ current = null;
22
+ return { ...line, blockId: null };
23
+ }
24
+ if (!current) {
25
+ blockId += 1;
26
+ current = { id: blockId };
27
+ blocks.push(current);
28
+ }
29
+ return { ...line, blockId };
30
+ });
31
+ const nextHunk = { oldStart, oldCount, newStart, newCount, header, lines };
32
+ return { hunk: nextHunk, blocks };
33
+ };
34
+
35
+ const otherContext = (contextSide, blockId) => {
36
+ if (contextSide === 'new') return 'new';
37
+ if (contextSide === 'old') return 'old';
38
+ return contextSide[blockId] ?? 'old';
39
+ };
40
+
41
+ const flattenBlock = (hunk, blockId, contextSide) => {
42
+ const out = [];
43
+ for (const line of hunk.lines) {
44
+ if (line.blockId === blockId || isCtxType(line.type)) {
45
+ out.push(line);
46
+ continue;
47
+ }
48
+ const side = otherContext(contextSide, line.blockId);
49
+ if (side === 'old' && line.type === 'del') {
50
+ out.push({ type: 'ctx', text: line.text, noNl: line.noNl });
51
+ continue;
52
+ }
53
+ if (side === 'new' && line.type === 'add') {
54
+ out.push({ type: 'ctx', text: line.text, noNl: line.noNl });
55
+ }
56
+ }
57
+ return out;
58
+ };
59
+
60
+ const countSides = (lines) => {
61
+ let oldCount = 0;
62
+ let newCount = 0;
63
+ for (const line of lines) {
64
+ if (isCtxType(line.type) || line.type === 'del') oldCount += 1;
65
+ if (isCtxType(line.type) || line.type === 'add') newCount += 1;
66
+ }
67
+ return { oldCount, newCount };
68
+ };
69
+
70
+ const formatHunkHeader = (oldStart, oldCount, newStart, newCount) =>
71
+ `@@ -${oldStart},${oldCount} +${newStart},${newCount} @@`;
72
+
73
+ const lineMark = (type) => LINE_MARK[type] ?? ' ';
74
+
75
+ const formatBodyLine = (line) => {
76
+ const rows = [`${lineMark(line.type)}${line.text}`];
77
+ if (line.noNl) rows.push('\');
78
+ return rows;
79
+ };
80
+
81
+ const formatPatch = (file, hunk, blockId, contextSide) => {
82
+ const lines = flattenBlock(hunk, blockId, contextSide);
83
+ const counts = countSides(lines);
84
+ const header = formatHunkHeader(
85
+ hunk.oldStart,
86
+ counts.oldCount,
87
+ hunk.newStart,
88
+ counts.newCount,
89
+ );
90
+ const parts = [...file.preamble, header];
91
+ for (const line of lines) {
92
+ for (const row of formatBodyLine(line)) parts.push(row);
93
+ }
94
+ return `${parts.join('\n')}\n`;
95
+ };
96
+
97
+ module.exports = {
98
+ splitHunk,
99
+ flattenBlock,
100
+ countSides,
101
+ formatHunkHeader,
102
+ formatPatch,
103
+ };