mirror of
https://github.com/pybricks/pybricks-code.git
synced 2026-09-12 17:45:22 +00:00
editor: fix replacing file when open in editor
When we import/replace a file that is open in an editor, instead of modifying the file in storage, we need to modify the file in the editor. This allows the modification to be pushed on the undo stack so that the user can undo the change in case it wrote over any of their recent changes. Issue: https://github.com/pybricks/support/issues/975
This commit is contained in:
committed by
David Lechner
parent
3fc275eaed
commit
7351dfb95c
+12
-1
@@ -1,5 +1,5 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2022 The Pybricks Authors
|
||||
// Copyright (c) 2022-2023 The Pybricks Authors
|
||||
|
||||
import { createAction } from '../actions';
|
||||
import { UUID } from '../fileStorage';
|
||||
@@ -121,3 +121,14 @@ export const editorGoto = createAction((uuid: UUID, line: number) => ({
|
||||
uuid,
|
||||
line,
|
||||
}));
|
||||
|
||||
/**
|
||||
* Requests to replace the value a file in the editor.
|
||||
* @param uuid The file UUID.
|
||||
* @param value The new value for the contents of the file.
|
||||
*/
|
||||
export const editorReplaceFile = createAction((uuid: UUID, value: string) => ({
|
||||
type: 'editor.action.replaceFile',
|
||||
uuid,
|
||||
value,
|
||||
}));
|
||||
|
||||
+32
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
import type { DatabaseChangeType, IDatabaseChange } from 'dexie-observable/api';
|
||||
import * as monaco from 'monaco-editor';
|
||||
import { EventChannel, buffers, eventChannel } from 'redux-saga';
|
||||
import { EventChannel, Task, buffers, eventChannel } from 'redux-saga';
|
||||
import {
|
||||
call,
|
||||
cancelled,
|
||||
@@ -62,6 +62,7 @@ import {
|
||||
editorGetValueResponse,
|
||||
editorGoto,
|
||||
editorOpenFile,
|
||||
editorReplaceFile,
|
||||
} from './actions';
|
||||
import { EditorError } from './error';
|
||||
import { ActiveFileHistoryManager, OpenFileManager } from './lib';
|
||||
@@ -92,6 +93,28 @@ function* handleModelDidChange(
|
||||
}
|
||||
}
|
||||
|
||||
function handleReplaceFile(
|
||||
editor: monaco.editor.ICodeEditor,
|
||||
model: monaco.editor.ITextModel,
|
||||
action: ReturnType<typeof editorReplaceFile>,
|
||||
) {
|
||||
// model might not be open in editor, but it doesn't hurt to do this in the
|
||||
// open editor
|
||||
editor.pushUndoStop();
|
||||
// pushEditOperations says it expects a cursorComputer, but doesn't seem to need one.
|
||||
model.pushEditOperations(
|
||||
[],
|
||||
[
|
||||
{
|
||||
range: model.getFullModelRange(),
|
||||
text: action.value,
|
||||
},
|
||||
],
|
||||
undefined as unknown as monaco.editor.ICursorStateComputer,
|
||||
);
|
||||
editor.pushUndoStop();
|
||||
}
|
||||
|
||||
function* handleEditorOpenFile(
|
||||
editor: monaco.editor.ICodeEditor,
|
||||
openFiles: OpenFileManager,
|
||||
@@ -161,6 +184,14 @@ function* handleEditorOpenFile(
|
||||
// https://github.com/redux-saga/redux-saga/issues/620#issuecomment-259161095
|
||||
yield* fork(handleModelDidChange, 1000, didChangeModelChan, model);
|
||||
|
||||
const replaceFileTask: Task = yield* takeEvery(
|
||||
editorReplaceFile.when((a) => a.uuid === action.uuid),
|
||||
handleReplaceFile,
|
||||
editor,
|
||||
model,
|
||||
);
|
||||
defer.push(() => replaceFileTask.cancel());
|
||||
|
||||
openFiles.add(action.uuid, model, didLoad.viewState);
|
||||
defer.push(() => openFiles.remove(action.uuid));
|
||||
|
||||
|
||||
+19
-9
@@ -20,6 +20,7 @@ import {
|
||||
editorDidActivateFile,
|
||||
editorDidCloseFile,
|
||||
editorDidFailToActivateFile,
|
||||
editorReplaceFile,
|
||||
} from '../editor/actions';
|
||||
import { EditorError } from '../editor/error';
|
||||
import { getPybricksMicroPythonFileTemplate } from '../editor/pybricksMicroPython';
|
||||
@@ -243,17 +244,26 @@ function* importPythonFile(
|
||||
fileName = accepted.newName;
|
||||
}
|
||||
|
||||
yield* put(fileStorageWriteFile(fileName, sourceFileContents));
|
||||
const existingFileInfo = existingFiles.find((x) => x.path === fileName);
|
||||
const openFileUuids = yield* select((s: RootState) => s.editor.openFileUuids);
|
||||
|
||||
const { didFailToWrite } = yield* race({
|
||||
didWrite: take(fileStorageDidWriteFile.when((a) => a.path === fileName)),
|
||||
didFailToWrite: take(
|
||||
fileStorageDidFailToWriteFile.when((a) => a.path === fileName),
|
||||
),
|
||||
});
|
||||
// If the file is open, modify contents in the editor so preserve undo
|
||||
// history, otherwise write directly to storage.
|
||||
if (existingFileInfo && openFileUuids.includes(existingFileInfo.uuid)) {
|
||||
yield* put(editorReplaceFile(existingFileInfo.uuid, sourceFileContents));
|
||||
} else {
|
||||
yield* put(fileStorageWriteFile(fileName, sourceFileContents));
|
||||
|
||||
if (didFailToWrite) {
|
||||
throw didFailToWrite.error;
|
||||
const { didFailToWrite } = yield* race({
|
||||
didWrite: take(fileStorageDidWriteFile.when((a) => a.path === fileName)),
|
||||
didFailToWrite: take(
|
||||
fileStorageDidFailToWriteFile.when((a) => a.path === fileName),
|
||||
),
|
||||
});
|
||||
|
||||
if (didFailToWrite) {
|
||||
throw didFailToWrite.error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user