reslop 0.2.4 → 0.2.5
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 +14 -1
- package/README.md +1 -2
- package/lib/capabilities.js +0 -4
- package/lib/clipboard.js +39 -1
- package/lib/deps-diff.js +0 -3
- package/lib/deps.js +144 -176
- package/lib/diff/inline.js +66 -1
- package/lib/diff/unit.js +11 -28
- package/lib/editor.js +69 -9
- package/lib/files.js +17 -16
- package/lib/git-deps.js +58 -95
- package/lib/github.js +7 -7
- package/lib/gitlab.js +3 -3
- package/lib/highlight/core.js +11 -0
- package/lib/highlight/dot.js +2 -12
- package/lib/highlight/log.js +4 -11
- package/lib/highlight/txt.js +2 -12
- package/lib/keys.js +22 -12
- package/lib/manifest.js +0 -4
- package/lib/npm-commands.js +1 -22
- package/lib/remote.js +1 -7
- package/lib/render/branches.js +4 -13
- package/lib/render/chrome.js +85 -31
- package/lib/render/commits.js +95 -207
- package/lib/render/diff.js +31 -1
- package/lib/render/files.js +7 -16
- package/lib/render/frame.js +95 -110
- package/lib/render/notes.js +71 -16
- package/lib/render/npm.js +104 -18
- package/lib/render/primitives.js +108 -45
- package/lib/render/unit.js +4 -1
- package/lib/report-parse.js +0 -1
- package/lib/report-run.js +0 -1
- package/lib/report-test.js +40 -47
- package/lib/report-tools.js +0 -7
- package/lib/review-merge.js +101 -131
- package/lib/review.js +64 -77
- package/lib/select.js +52 -0
- package/lib/session/actions.js +23 -22
- package/lib/session/branches.js +5 -3
- package/lib/session/change-actions.js +173 -146
- package/lib/session/commit.js +32 -13
- package/lib/session/compose.js +120 -77
- package/lib/session/dispatch.js +36 -21
- package/lib/session/edit-click.js +57 -0
- package/lib/session/files-pane.js +24 -24
- package/lib/session/items.js +69 -89
- package/lib/session/lifecycle.js +31 -96
- package/lib/session/load.js +51 -58
- package/lib/session/navigation.js +105 -96
- package/lib/session/npm.js +158 -77
- package/lib/session/ops.js +192 -51
- package/lib/session/pointer.js +11 -5
- package/lib/session/progress.js +9 -6
- package/lib/session/review.js +35 -54
- package/lib/session/tasks.js +292 -0
- package/lib/session/terminal.js +13 -17
- package/lib/session/unit.js +3 -4
- package/lib/session/update.js +76 -97
- package/lib/session/view.js +17 -14
- package/lib/session/watch.js +47 -27
- package/lib/session.js +34 -79
- package/lib/update.js +1 -5
- package/lib/utilities.js +21 -1
- package/lib/wrap.js +15 -0
- package/package.json +1 -1
- package/lib/session/todos.js +0 -293
package/lib/session/pointer.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const select = require('../select.js');
|
|
4
|
+
const editClick = require('./edit-click.js');
|
|
4
5
|
const { extractText, isRange } = select;
|
|
5
6
|
const keys = require('../keys.js');
|
|
6
7
|
const { hitAction } = keys;
|
|
@@ -100,16 +101,18 @@ const RELEASE_TARGETS = [
|
|
|
100
101
|
match: (ui) => ui.pane === 'files',
|
|
101
102
|
hits: 'fileHits',
|
|
102
103
|
onHit: (ui, hit) => {
|
|
104
|
+
const again = armDoubleClick(ui.nav, 'files', hit.cursor);
|
|
103
105
|
ui.nav.fileCursor = hit.cursor;
|
|
104
|
-
ui.filesPane.onOpenFile();
|
|
106
|
+
if (again) ui.filesPane.onOpenFile();
|
|
105
107
|
},
|
|
106
108
|
},
|
|
107
109
|
{
|
|
108
110
|
match: (ui) => ui.pane === 'branches',
|
|
109
111
|
hits: 'fileHits',
|
|
110
112
|
onHit: (ui, hit) => {
|
|
113
|
+
const again = armDoubleClick(ui.nav, 'branches', hit.cursor);
|
|
111
114
|
ui.nav.branchCursor = hit.cursor;
|
|
112
|
-
ui.gitBranches.onCheckoutBranch();
|
|
115
|
+
if (again) ui.gitBranches.onCheckoutBranch();
|
|
113
116
|
},
|
|
114
117
|
},
|
|
115
118
|
{
|
|
@@ -117,7 +120,9 @@ const RELEASE_TARGETS = [
|
|
|
117
120
|
hits: 'fileHits',
|
|
118
121
|
onHit: (ui, hit) => {
|
|
119
122
|
if (ui.mode === 'compose') return;
|
|
123
|
+
const again = armDoubleClick(ui.nav, 'commits', hit.cursor);
|
|
120
124
|
ui.nav.commitCursor = hit.cursor;
|
|
125
|
+
if (again) ui.filesPane.onOpenFile();
|
|
121
126
|
},
|
|
122
127
|
},
|
|
123
128
|
{
|
|
@@ -131,10 +136,10 @@ const RELEASE_TARGETS = [
|
|
|
131
136
|
},
|
|
132
137
|
{
|
|
133
138
|
match: (ui) => ui.pane === 'diff',
|
|
134
|
-
hits: '
|
|
139
|
+
hits: 'taskHits',
|
|
135
140
|
onHit: (ui, hit) => {
|
|
136
|
-
if (hit.check) return void ui.composer.
|
|
137
|
-
ui.composer.
|
|
141
|
+
if (hit.check) return void ui.composer.onTaskCheck(hit.cursor);
|
|
142
|
+
ui.composer.onTaskHit(hit.cursor);
|
|
138
143
|
},
|
|
139
144
|
},
|
|
140
145
|
{
|
|
@@ -154,6 +159,7 @@ const onRelease = (ui, event) => {
|
|
|
154
159
|
nav.pendingClick = null;
|
|
155
160
|
nav.mouseAnchor = null;
|
|
156
161
|
if (clickButton(ui, cell, clickId)) return;
|
|
162
|
+
if (editClick.placeClick(ui, cell)) return;
|
|
157
163
|
const frame = ui.lastFrame;
|
|
158
164
|
for (const target of RELEASE_TARGETS) {
|
|
159
165
|
if (!target.match(ui)) continue;
|
package/lib/session/progress.js
CHANGED
|
@@ -3,22 +3,25 @@
|
|
|
3
3
|
const createProgress = (term, onTick, intervalMs) => {
|
|
4
4
|
const active = new Set();
|
|
5
5
|
|
|
6
|
+
const tick = () => {
|
|
7
|
+
if (!active.size) return;
|
|
8
|
+
onTick();
|
|
9
|
+
term.later('progress', tick, intervalMs);
|
|
10
|
+
};
|
|
11
|
+
|
|
6
12
|
const start = (id) => {
|
|
7
|
-
const wasEmpty = active.size === 0;
|
|
8
13
|
active.add(id);
|
|
9
|
-
|
|
10
|
-
term.startTimer('progress', onTick, intervalMs);
|
|
11
|
-
}
|
|
14
|
+
term.later('progress', tick, intervalMs);
|
|
12
15
|
};
|
|
13
16
|
|
|
14
17
|
const stop = (id) => {
|
|
15
18
|
active.delete(id);
|
|
16
|
-
if (active.size === 0) term.
|
|
19
|
+
if (active.size === 0) term.cancel('progress');
|
|
17
20
|
};
|
|
18
21
|
|
|
19
22
|
const clear = () => {
|
|
20
23
|
active.clear();
|
|
21
|
-
term.
|
|
24
|
+
term.cancel('progress');
|
|
22
25
|
};
|
|
23
26
|
|
|
24
27
|
const size = () => active.size;
|
package/lib/session/review.js
CHANGED
|
@@ -1,75 +1,56 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const accessors = require('./accessors.js');
|
|
4
|
-
const { stateView } = accessors;
|
|
5
|
-
|
|
6
3
|
const review = require('../review.js');
|
|
7
4
|
const { createStore, applyImportedNotes, setStatus } = review;
|
|
8
5
|
const { listReviewNames, loadTemplates, resolveReviewPath } = review;
|
|
9
6
|
const { loadReview, flushReview } = review;
|
|
10
7
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
store
|
|
14
|
-
didResume
|
|
15
|
-
importedRemote
|
|
16
|
-
}
|
|
8
|
+
class ReviewController {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.store = null;
|
|
11
|
+
this.didResume = false;
|
|
12
|
+
this.importedRemote = false;
|
|
13
|
+
}
|
|
17
14
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
15
|
+
reset() {
|
|
16
|
+
this.store = null;
|
|
17
|
+
this.didResume = false;
|
|
18
|
+
this.importedRemote = false;
|
|
19
|
+
}
|
|
23
20
|
|
|
24
|
-
|
|
21
|
+
init(dir, date, extra = {}) {
|
|
25
22
|
const names = listReviewNames(dir);
|
|
26
23
|
const templates = loadTemplates(dir);
|
|
27
24
|
const resolved = resolveReviewPath(dir, date, names, extra);
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (state.didResume || state.importedRemote) return false;
|
|
41
|
-
applyImportedNotes(state.store, imported);
|
|
42
|
-
state.importedRemote = true;
|
|
25
|
+
this.didResume = resolved.resume === true;
|
|
26
|
+
this.store = this.didResume
|
|
27
|
+
? loadReview(resolved.reviewPath, templates)
|
|
28
|
+
: createStore(resolved.reviewPath, templates);
|
|
29
|
+
return this.store;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
applyImported(imported) {
|
|
33
|
+
if (!imported || !this.store) return false;
|
|
34
|
+
if (this.didResume || this.importedRemote) return false;
|
|
35
|
+
applyImportedNotes(this.store, imported);
|
|
36
|
+
this.importedRemote = true;
|
|
43
37
|
return true;
|
|
44
|
-
}
|
|
38
|
+
}
|
|
45
39
|
|
|
46
|
-
|
|
47
|
-
if (!
|
|
40
|
+
flush(force = false) {
|
|
41
|
+
if (!this.store) return { ok: true, wrote: false };
|
|
48
42
|
try {
|
|
49
|
-
const wrote = flushReview(
|
|
43
|
+
const wrote = flushReview(this.store, force);
|
|
50
44
|
return { ok: true, wrote };
|
|
51
45
|
} catch (error) {
|
|
52
46
|
return { ok: false, wrote: false, error };
|
|
53
47
|
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const finishQuit = (status) => {
|
|
57
|
-
setStatus(state.store, status);
|
|
58
|
-
return flush();
|
|
59
|
-
};
|
|
48
|
+
}
|
|
60
49
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
applyImported,
|
|
67
|
-
flush,
|
|
68
|
-
finishQuit,
|
|
69
|
-
},
|
|
70
|
-
[],
|
|
71
|
-
['store', 'didResume', 'importedRemote'],
|
|
72
|
-
);
|
|
73
|
-
};
|
|
50
|
+
finishQuit(status) {
|
|
51
|
+
setStatus(this.store, status);
|
|
52
|
+
return this.flush();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
74
55
|
|
|
75
|
-
module.exports = {
|
|
56
|
+
module.exports = { ReviewController };
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const files = require('../files.js');
|
|
4
|
+
const { isTasksEntry, TASKS_FILE } = files;
|
|
5
|
+
const review = require('../review.js');
|
|
6
|
+
const { addTask, setTaskText, setTaskDone, removeTask, checkLabel } = review;
|
|
7
|
+
|
|
8
|
+
const TASK_TYPE_SKIP = [
|
|
9
|
+
'enter',
|
|
10
|
+
'escape',
|
|
11
|
+
'tab',
|
|
12
|
+
'backspace',
|
|
13
|
+
'delete',
|
|
14
|
+
'left',
|
|
15
|
+
'right',
|
|
16
|
+
'up',
|
|
17
|
+
'down',
|
|
18
|
+
'home',
|
|
19
|
+
'end',
|
|
20
|
+
'pageUp',
|
|
21
|
+
'pageDown',
|
|
22
|
+
'j',
|
|
23
|
+
'k',
|
|
24
|
+
'n',
|
|
25
|
+
'p',
|
|
26
|
+
'q',
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
const isTaskTypeKey = (key) => {
|
|
30
|
+
if (!key) return false;
|
|
31
|
+
if (TASK_TYPE_SKIP.includes(key)) return false;
|
|
32
|
+
if (key.startsWith('ctrl-')) return false;
|
|
33
|
+
if (key.startsWith('alt-')) return false;
|
|
34
|
+
return key.charCodeAt(0) >= 32;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const listTasks = (api) => {
|
|
38
|
+
const notes = api.review.store;
|
|
39
|
+
if (!notes) return [];
|
|
40
|
+
return notes.tasks;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const isDraftCompose = (api) => {
|
|
44
|
+
const { state } = api;
|
|
45
|
+
return state.composeKind === 'tasks' && state.composeTaskId === null;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const taskTexts = (api) => {
|
|
49
|
+
const nav = api.nav;
|
|
50
|
+
if (!nav.tasksOpen) return [];
|
|
51
|
+
const todos = listTasks(api);
|
|
52
|
+
const texts = new Array(todos.length + 1);
|
|
53
|
+
const { state } = api;
|
|
54
|
+
const editingTodo = state.composeKind === 'tasks';
|
|
55
|
+
const editingId = editingTodo ? state.composeTaskId : null;
|
|
56
|
+
for (let i = 0; i < todos.length; i++) {
|
|
57
|
+
const todo = todos[i];
|
|
58
|
+
const live = editingId === todo.id && state.editor;
|
|
59
|
+
const text = live ? state.editor.text : todo.text;
|
|
60
|
+
texts[i] = checkLabel(text, todo.done);
|
|
61
|
+
}
|
|
62
|
+
const draft = isDraftCompose(api) && state.editor ? state.editor.text : '';
|
|
63
|
+
texts[todos.length] = checkLabel(draft, false);
|
|
64
|
+
return texts;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const taskRowCount = (api) => {
|
|
68
|
+
if (!api.nav.tasksOpen) return 0;
|
|
69
|
+
return listTasks(api).length + 1;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const clampedTaskFocus = (api) => {
|
|
73
|
+
const last = Math.max(0, taskRowCount(api) - 1);
|
|
74
|
+
const focus = api.nav.tasksFocus;
|
|
75
|
+
if (focus < 0) return 0;
|
|
76
|
+
if (focus > last) return last;
|
|
77
|
+
return focus;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const moveTaskFocus = (api, delta) => {
|
|
81
|
+
const nav = api.nav;
|
|
82
|
+
if (!nav.tasksOpen) return;
|
|
83
|
+
const n = taskRowCount(api);
|
|
84
|
+
if (!n) return;
|
|
85
|
+
const next = clampedTaskFocus(api) + delta;
|
|
86
|
+
if (next < 0) nav.tasksFocus = 0;
|
|
87
|
+
else if (next >= n) nav.tasksFocus = n - 1;
|
|
88
|
+
else nav.tasksFocus = next;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const openTasksPage = (api) => {
|
|
92
|
+
const nav = api.nav;
|
|
93
|
+
nav.tasksOpen = true;
|
|
94
|
+
nav.pane = 'diff';
|
|
95
|
+
nav.scroll = 0;
|
|
96
|
+
nav.clearSelection();
|
|
97
|
+
api.ui.syncReviewPath();
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const closeTasksPage = (api) => {
|
|
101
|
+
api.nav.tasksOpen = false;
|
|
102
|
+
api.ui.syncReviewPath();
|
|
103
|
+
api.ui.syncFileCursor();
|
|
104
|
+
api.ui.lifecycle.catchUpWatch();
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const focusTodoPage = (api, todoId) => {
|
|
108
|
+
openTasksPage(api);
|
|
109
|
+
const todos = listTasks(api);
|
|
110
|
+
let focus = 0;
|
|
111
|
+
if (todoId === null) {
|
|
112
|
+
focus = todos.length;
|
|
113
|
+
} else if (todoId !== undefined) {
|
|
114
|
+
const at = todos.findIndex((todo) => todo.id === todoId);
|
|
115
|
+
if (at >= 0) focus = at;
|
|
116
|
+
else if (todos.length) focus = todos.length - 1;
|
|
117
|
+
}
|
|
118
|
+
api.nav.tasksFocus = focus;
|
|
119
|
+
api.nav.scroll = 0;
|
|
120
|
+
return true;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const startDraftCompose = (api) => {
|
|
124
|
+
focusTodoPage(api, null);
|
|
125
|
+
api.open('tasks', '', null);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const editFocusedTask = (api) => {
|
|
129
|
+
if (!api.nav.tasksOpen) return;
|
|
130
|
+
const todos = listTasks(api);
|
|
131
|
+
const todo = todos[clampedTaskFocus(api)];
|
|
132
|
+
if (!todo) return void startDraftCompose(api);
|
|
133
|
+
api.open('tasks', todo.text, todo.id);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const editNextTask = (api) => {
|
|
137
|
+
if (!api.nav.tasksOpen) return;
|
|
138
|
+
const last = taskRowCount(api) - 1;
|
|
139
|
+
const next = clampedTaskFocus(api) + 1;
|
|
140
|
+
if (next <= last) api.nav.tasksFocus = next;
|
|
141
|
+
editFocusedTask(api);
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const moveTaskCompose = (api, delta, width, extend) => {
|
|
145
|
+
if (!api.nav.tasksOpen) return;
|
|
146
|
+
const editor = api.state.editor;
|
|
147
|
+
if (editor && width) {
|
|
148
|
+
const before = editor.cursor;
|
|
149
|
+
editor.moveLine(delta, width, extend);
|
|
150
|
+
if (editor.cursor !== before) return;
|
|
151
|
+
if (extend) return;
|
|
152
|
+
}
|
|
153
|
+
const last = taskRowCount(api) - 1;
|
|
154
|
+
const at = clampedTaskFocus(api);
|
|
155
|
+
let next = at + delta;
|
|
156
|
+
if (next < 0) next = 0;
|
|
157
|
+
if (next > last) next = last;
|
|
158
|
+
if (next === at) return;
|
|
159
|
+
api.commit();
|
|
160
|
+
api.ui.flushReview();
|
|
161
|
+
api.close();
|
|
162
|
+
const max = Math.max(0, taskRowCount(api) - 1);
|
|
163
|
+
api.nav.tasksFocus = Math.min(next, max);
|
|
164
|
+
editFocusedTask(api);
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const typeIntoTask = (api, key) => {
|
|
168
|
+
const nav = api.nav;
|
|
169
|
+
if (!nav.tasksOpen) return false;
|
|
170
|
+
if (nav.pane !== 'diff') return false;
|
|
171
|
+
if (api.ui.mode !== 'review') return false;
|
|
172
|
+
if (!isTaskTypeKey(key)) return false;
|
|
173
|
+
editFocusedTask(api);
|
|
174
|
+
api.state.editor.insert(key);
|
|
175
|
+
return true;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const removeFocusedTask = (api) => {
|
|
179
|
+
const nav = api.nav;
|
|
180
|
+
if (nav.pane !== 'diff' || !nav.tasksOpen) return;
|
|
181
|
+
const todos = listTasks(api);
|
|
182
|
+
const todo = todos[clampedTaskFocus(api)];
|
|
183
|
+
if (!todo) return;
|
|
184
|
+
const notes = api.review.store;
|
|
185
|
+
removeTask(notes, todo.id);
|
|
186
|
+
const left = listTasks(api);
|
|
187
|
+
if (nav.tasksFocus >= left.length) {
|
|
188
|
+
nav.tasksFocus = Math.max(0, left.length - 1);
|
|
189
|
+
}
|
|
190
|
+
api.ui.flushReview(true);
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const toggleFocusedTask = (api) => {
|
|
194
|
+
const nav = api.nav;
|
|
195
|
+
if (!nav.tasksOpen || nav.pane !== 'diff') return false;
|
|
196
|
+
if (api.ui.mode !== 'review') return false;
|
|
197
|
+
const todo = listTasks(api)[clampedTaskFocus(api)];
|
|
198
|
+
if (!todo) return false;
|
|
199
|
+
setTaskDone(api.review.store, todo.id, !todo.done);
|
|
200
|
+
api.ui.flushReview(true);
|
|
201
|
+
return true;
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
const onTaskHit = (api, cursor) => {
|
|
205
|
+
if (!api.nav.tasksOpen) return;
|
|
206
|
+
const todos = listTasks(api);
|
|
207
|
+
const draft = cursor === todos.length;
|
|
208
|
+
const target = todos[cursor];
|
|
209
|
+
if (!draft && !target) return;
|
|
210
|
+
const { state } = api;
|
|
211
|
+
if (state.composeKind === 'tasks') {
|
|
212
|
+
if (draft && state.composeTaskId === null) return;
|
|
213
|
+
if (target && target.id === state.composeTaskId) return;
|
|
214
|
+
api.commit();
|
|
215
|
+
api.close();
|
|
216
|
+
}
|
|
217
|
+
api.nav.tasksFocus = cursor;
|
|
218
|
+
editFocusedTask(api);
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const onTaskCheck = (api, cursor) => {
|
|
222
|
+
if (!api.nav.tasksOpen) return;
|
|
223
|
+
const todo = listTasks(api)[cursor];
|
|
224
|
+
if (!todo) return void onTaskHit(api, cursor);
|
|
225
|
+
const { state } = api;
|
|
226
|
+
if (state.composeKind === 'tasks' && state.composeTaskId !== todo.id) {
|
|
227
|
+
api.commit();
|
|
228
|
+
api.close();
|
|
229
|
+
}
|
|
230
|
+
setTaskDone(api.review.store, todo.id, !todo.done);
|
|
231
|
+
api.nav.tasksFocus = cursor;
|
|
232
|
+
api.ui.flushReview(true);
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const createTask = (api) => {
|
|
236
|
+
startDraftCompose(api);
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
const onTasks = (api) => {
|
|
240
|
+
const { nav, ui } = api;
|
|
241
|
+
if (nav.pane === 'files') {
|
|
242
|
+
const entry = ui.fileCursorEntry();
|
|
243
|
+
if (entry && !isTasksEntry(entry)) {
|
|
244
|
+
nav.index = entry.openIndex ?? entry.firstIndex;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
createTask(api);
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const taskEditView = (api) => {
|
|
251
|
+
const { state } = api;
|
|
252
|
+
if (state.composeKind !== 'tasks') return null;
|
|
253
|
+
if (!state.editor) return null;
|
|
254
|
+
return { cursor: state.editor.cursor };
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const commitTask = (api, text) => {
|
|
258
|
+
const notes = api.review.store;
|
|
259
|
+
const id = api.state.composeTaskId;
|
|
260
|
+
const todos = notes.tasks;
|
|
261
|
+
const prev = id === null ? null : todos.find((entry) => entry.id === id);
|
|
262
|
+
if (prev) return void setTaskText(notes, id, text);
|
|
263
|
+
if (!text.trim()) return;
|
|
264
|
+
const todo = addTask(notes, TASKS_FILE, text);
|
|
265
|
+
api.state.composeTaskId = todo.id;
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
const createTasksCompose = (api) => ({
|
|
269
|
+
taskTexts: () => taskTexts(api),
|
|
270
|
+
clampedTaskFocus: () => clampedTaskFocus(api),
|
|
271
|
+
taskRowCount: () => taskRowCount(api),
|
|
272
|
+
moveTaskFocus: (delta) => moveTaskFocus(api, delta),
|
|
273
|
+
openTasksPage: () => openTasksPage(api),
|
|
274
|
+
closeTasksPage: () => closeTasksPage(api),
|
|
275
|
+
startDraftCompose: () => startDraftCompose(api),
|
|
276
|
+
editFocusedTask: () => editFocusedTask(api),
|
|
277
|
+
editNextTask: () => editNextTask(api),
|
|
278
|
+
moveTaskCompose: (delta, width, extend) =>
|
|
279
|
+
moveTaskCompose(api, delta, width, extend),
|
|
280
|
+
typeIntoTask: (key) => typeIntoTask(api, key),
|
|
281
|
+
removeFocusedTask: () => removeFocusedTask(api),
|
|
282
|
+
onTaskHit: (cursor) => onTaskHit(api, cursor),
|
|
283
|
+
onTaskCheck: (cursor) => onTaskCheck(api, cursor),
|
|
284
|
+
toggleFocusedTask: () => toggleFocusedTask(api),
|
|
285
|
+
onTasks: () => onTasks(api),
|
|
286
|
+
taskEditView: () => taskEditView(api),
|
|
287
|
+
commitTask: (text) => commitTask(api, text),
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
module.exports = {
|
|
291
|
+
createTasksCompose,
|
|
292
|
+
};
|
package/lib/session/terminal.js
CHANGED
|
@@ -17,28 +17,25 @@ const sameCursor = (a, b) => {
|
|
|
17
17
|
|
|
18
18
|
const hasTimer = (api, name) => api.timers.has(name);
|
|
19
19
|
|
|
20
|
-
const
|
|
20
|
+
const cancel = (api, name) => {
|
|
21
21
|
const id = api.timers.get(name);
|
|
22
22
|
if (id === undefined) return;
|
|
23
|
-
|
|
23
|
+
clearTimeout(id);
|
|
24
24
|
api.timers.delete(name);
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
-
const
|
|
28
|
-
if (api.state.disposed) return;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
const later = (api, name, fn, ms) => {
|
|
28
|
+
if (api.state.disposed || api.timers.has(name)) return;
|
|
29
|
+
const id = setTimeout(() => {
|
|
30
|
+
api.timers.delete(name);
|
|
31
|
+
fn();
|
|
32
|
+
}, ms);
|
|
33
|
+
if (typeof id.unref === 'function') id.unref();
|
|
32
34
|
api.timers.set(name, id);
|
|
33
35
|
};
|
|
34
36
|
|
|
35
|
-
const restartTimer = (api, name, fn, ms) => {
|
|
36
|
-
stopTimer(api, name);
|
|
37
|
-
startTimer(api, name, fn, ms);
|
|
38
|
-
};
|
|
39
|
-
|
|
40
37
|
const clearTimers = (api) => {
|
|
41
|
-
for (const name of [...api.timers.keys()])
|
|
38
|
+
for (const name of [...api.timers.keys()]) cancel(api, name);
|
|
42
39
|
};
|
|
43
40
|
|
|
44
41
|
const stopListening = (api) => {
|
|
@@ -98,6 +95,7 @@ const paint = (api, frame, size, cursor) => {
|
|
|
98
95
|
api.state.lastFrame &&
|
|
99
96
|
api.state.lastFrame.text === frame.text
|
|
100
97
|
) {
|
|
98
|
+
api.state.lastFrame = frame;
|
|
101
99
|
if (sameCursor(api.state.lastPresentedCursor, cursor)) return;
|
|
102
100
|
api.state.lastPresentedCursor = cursor;
|
|
103
101
|
api.stdout.write(presentCursor(cursor));
|
|
@@ -148,9 +146,8 @@ const createTerminal = (options) => {
|
|
|
148
146
|
paint: (frame, size, cursor) => paint(api, frame, size, cursor),
|
|
149
147
|
startListening: (handlers) => startListening(api, handlers),
|
|
150
148
|
stopListening: () => stopListening(api),
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
restartTimer: (name, fn, ms) => restartTimer(api, name, fn, ms),
|
|
149
|
+
later: (name, fn, ms) => later(api, name, fn, ms),
|
|
150
|
+
cancel: (name) => cancel(api, name),
|
|
154
151
|
clearTimers: () => clearTimers(api),
|
|
155
152
|
hasTimer: (name) => hasTimer(api, name),
|
|
156
153
|
resetCache: () => resetCache(api),
|
|
@@ -160,6 +157,5 @@ const createTerminal = (options) => {
|
|
|
160
157
|
module.exports = {
|
|
161
158
|
ENTER_TERM,
|
|
162
159
|
LEAVE_TERM,
|
|
163
|
-
sameCursor,
|
|
164
160
|
createTerminal,
|
|
165
161
|
};
|
package/lib/session/unit.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const files = require('../files.js');
|
|
4
|
-
const { listPath,
|
|
4
|
+
const { listPath, isTasksEntry } = files;
|
|
5
5
|
const diff = require('../diff/diff.js');
|
|
6
6
|
const { unitLines, itemsForPath, blockLineIndex } = diff;
|
|
7
7
|
const { hunkLineRange, sameHunk, hunkKey } = diff;
|
|
@@ -150,10 +150,10 @@ const moveUnitBlock = (ui, step) => {
|
|
|
150
150
|
};
|
|
151
151
|
|
|
152
152
|
const openUnitFile = (ui, entry) => {
|
|
153
|
-
if (!entry ||
|
|
153
|
+
if (!entry || isTasksEntry(entry)) return;
|
|
154
154
|
const nav = ui.nav;
|
|
155
155
|
nav.reviewPath = entry.path;
|
|
156
|
-
nav.
|
|
156
|
+
nav.tasksOpen = false;
|
|
157
157
|
nav.pane = 'unit';
|
|
158
158
|
nav.scroll = 0;
|
|
159
159
|
nav.unitLine = 0;
|
|
@@ -203,6 +203,5 @@ module.exports = {
|
|
|
203
203
|
openUnitFile,
|
|
204
204
|
setFileScope,
|
|
205
205
|
editCursor,
|
|
206
|
-
ensureVisible,
|
|
207
206
|
followFileCursor,
|
|
208
207
|
};
|