1 |
|
2 |
|
3 | import { nullTranslator } from '@jupyterlab/translation';
|
4 | import { Signal } from '@lumino/signaling';
|
5 |
|
6 |
|
7 |
|
8 | export class NotebookHistory {
|
9 | |
10 |
|
11 |
|
12 | constructor(options) {
|
13 | |
14 |
|
15 |
|
16 | this._requestBatchSize = 10;
|
17 | this._cursor = 0;
|
18 | this._hasSession = false;
|
19 | this._history = [];
|
20 | this._placeholder = '';
|
21 | this._kernelSession = '';
|
22 | this._setByHistory = false;
|
23 | this._isDisposed = false;
|
24 | this._editor = null;
|
25 | this._filtered = [];
|
26 | this._kernel = null;
|
27 | this._sessionContext = options.sessionContext;
|
28 | this._trans = (options.translator || nullTranslator).load('jupyterlab');
|
29 | void this._handleKernel().then(() => {
|
30 | this._sessionContext.kernelChanged.connect(this._handleKernel, this);
|
31 | });
|
32 | this._toRequest = this._requestBatchSize;
|
33 | }
|
34 | |
35 |
|
36 |
|
37 | get editor() {
|
38 | return this._editor;
|
39 | }
|
40 | set editor(value) {
|
41 | if (this._editor === value) {
|
42 | return;
|
43 | }
|
44 | const prev = this._editor;
|
45 | if (prev) {
|
46 | prev.model.sharedModel.changed.disconnect(this.onTextChange, this);
|
47 | }
|
48 | this._editor = value;
|
49 | if (value) {
|
50 | value.model.sharedModel.changed.connect(this.onTextChange, this);
|
51 | }
|
52 | }
|
53 | |
54 |
|
55 |
|
56 | get placeholder() {
|
57 | return this._placeholder;
|
58 | }
|
59 | |
60 |
|
61 |
|
62 | get kernelSession() {
|
63 | return this._kernelSession;
|
64 | }
|
65 | |
66 |
|
67 |
|
68 | get isDisposed() {
|
69 | return this._isDisposed;
|
70 | }
|
71 | |
72 |
|
73 |
|
74 | dispose() {
|
75 | this._isDisposed = true;
|
76 | this._history.length = 0;
|
77 | Signal.clearData(this);
|
78 | }
|
79 | |
80 |
|
81 |
|
82 |
|
83 |
|
84 | async checkSession(activeCell) {
|
85 | var _a;
|
86 | if (!this._hasSession) {
|
87 | await this._retrieveHistory();
|
88 | this._hasSession = true;
|
89 | this.editor = activeCell.editor;
|
90 | this._placeholder = ((_a = this._editor) === null || _a === void 0 ? void 0 : _a.model.sharedModel.getSource()) || '';
|
91 |
|
92 | this.setFilter(this._placeholder);
|
93 | this._cursor = this._filtered.length - 1;
|
94 | }
|
95 | }
|
96 | |
97 |
|
98 |
|
99 |
|
100 |
|
101 |
|
102 |
|
103 | async back(activeCell) {
|
104 | await this.checkSession(activeCell);
|
105 | --this._cursor;
|
106 | if (this._cursor < 0) {
|
107 | await this.fetchBatch();
|
108 | }
|
109 | this._cursor = Math.max(0, this._cursor);
|
110 | const content = this._filtered[this._cursor];
|
111 |
|
112 | return content;
|
113 | }
|
114 | |
115 |
|
116 |
|
117 |
|
118 |
|
119 |
|
120 |
|
121 | async forward(activeCell) {
|
122 | await this.checkSession(activeCell);
|
123 | ++this._cursor;
|
124 | this._cursor = Math.min(this._filtered.length - 1, this._cursor);
|
125 | const content = this._filtered[this._cursor];
|
126 |
|
127 | return content;
|
128 | }
|
129 | |
130 |
|
131 |
|
132 |
|
133 |
|
134 |
|
135 | updateEditor(activeCell, content) {
|
136 | var _a, _b;
|
137 | if (activeCell) {
|
138 | const model = (_a = activeCell.editor) === null || _a === void 0 ? void 0 : _a.model;
|
139 | const source = model === null || model === void 0 ? void 0 : model.sharedModel.getSource();
|
140 | if (this.isDisposed || !content) {
|
141 | return;
|
142 | }
|
143 | if (source === content) {
|
144 | return;
|
145 | }
|
146 | this._setByHistory = true;
|
147 | model === null || model === void 0 ? void 0 : model.sharedModel.setSource(content);
|
148 | let columnPos = 0;
|
149 | columnPos = content.indexOf('\n');
|
150 | if (columnPos < 0) {
|
151 | columnPos = content.length;
|
152 | }
|
153 | (_b = activeCell.editor) === null || _b === void 0 ? void 0 : _b.setCursorPosition({ line: 0, column: columnPos });
|
154 | }
|
155 | }
|
156 | |
157 |
|
158 |
|
159 | reset() {
|
160 | this._hasSession = false;
|
161 | this._placeholder = '';
|
162 | this._toRequest = this._requestBatchSize;
|
163 | }
|
164 | |
165 |
|
166 |
|
167 |
|
168 | async fetchBatch() {
|
169 | this._toRequest += this._requestBatchSize;
|
170 | let oldFilteredReversed = this._filtered.slice().reverse();
|
171 | let oldHistory = this._history.slice();
|
172 | await this._retrieveHistory().then(() => {
|
173 | this.setFilter(this._placeholder);
|
174 | let cursorOffset = 0;
|
175 | let filteredReversed = this._filtered.slice().reverse();
|
176 | for (let i = 0; i < oldFilteredReversed.length; i++) {
|
177 | let item = oldFilteredReversed[i];
|
178 | for (let ij = i + cursorOffset; ij < filteredReversed.length; ij++) {
|
179 | if (item === filteredReversed[ij]) {
|
180 | break;
|
181 | }
|
182 | else {
|
183 | cursorOffset += 1;
|
184 | }
|
185 | }
|
186 | }
|
187 | this._cursor =
|
188 | this._filtered.length - (oldFilteredReversed.length + 1) - cursorOffset;
|
189 | });
|
190 | if (this._cursor < 0) {
|
191 | if (this._history.length > oldHistory.length) {
|
192 | await this.fetchBatch();
|
193 | }
|
194 | }
|
195 | }
|
196 | |
197 |
|
198 |
|
199 |
|
200 |
|
201 |
|
202 |
|
203 |
|
204 |
|
205 |
|
206 | onHistory(value, cell) {
|
207 | this._history.length = 0;
|
208 | let last = ['', '', ''];
|
209 | let current = ['', '', ''];
|
210 | let kernelSession = '';
|
211 | if (value.content.status === 'ok') {
|
212 | for (let i = 0; i < value.content.history.length; i++) {
|
213 | current = value.content.history[i];
|
214 | if (current !== last) {
|
215 | kernelSession = value.content.history[i][0];
|
216 | this._history.push((last = current));
|
217 | }
|
218 | }
|
219 |
|
220 | if (!this.kernelSession) {
|
221 | if (current[2] == (cell === null || cell === void 0 ? void 0 : cell.model.sharedModel.getSource())) {
|
222 | this._kernelSession = kernelSession;
|
223 | }
|
224 | }
|
225 | }
|
226 | }
|
227 | |
228 |
|
229 |
|
230 | onTextChange() {
|
231 | if (this._setByHistory) {
|
232 | this._setByHistory = false;
|
233 | return;
|
234 | }
|
235 | this.reset();
|
236 | }
|
237 | |
238 |
|
239 |
|
240 | async _handleKernel() {
|
241 | var _a;
|
242 | this._kernel = (_a = this._sessionContext.session) === null || _a === void 0 ? void 0 : _a.kernel;
|
243 | if (!this._kernel) {
|
244 | this._history.length = 0;
|
245 | return;
|
246 | }
|
247 | await this._retrieveHistory().catch();
|
248 | return;
|
249 | }
|
250 | |
251 |
|
252 |
|
253 |
|
254 |
|
255 | async _retrieveHistory(cell) {
|
256 | var _a;
|
257 | return await ((_a = this._kernel) === null || _a === void 0 ? void 0 : _a.requestHistory(request(this._toRequest)).then(v => {
|
258 | this.onHistory(v, cell);
|
259 | }).catch(() => {
|
260 | console.warn(this._trans.__('History was unable to be retrieved'));
|
261 | }));
|
262 | }
|
263 | |
264 |
|
265 |
|
266 |
|
267 |
|
268 | setFilter(filterStr = '') {
|
269 |
|
270 | this._filtered.length = 0;
|
271 | let last = '';
|
272 | let current = '';
|
273 | for (let i = 0; i < this._history.length; i++) {
|
274 | current = this._history[i][2];
|
275 | if (current !== last && filterStr !== current) {
|
276 | this._filtered.push((last = current));
|
277 | }
|
278 | }
|
279 | this._filtered.push(filterStr);
|
280 | }
|
281 | }
|
282 | function request(n) {
|
283 | return {
|
284 | output: false,
|
285 | raw: true,
|
286 | hist_access_type: 'tail',
|
287 | n: n
|
288 | };
|
289 | }
|
290 |
|
\ | No newline at end of file |