Watch buffer file for changes, and automatically reload it if changed (#76)

* Implement Buffer class in main process that watches for changes to the file, and notifies the editor in the renderer process so that it can update the buffer.

* Add Editor.setReadOnly() method

* Add dummy onChangeCallback function

* Remove debug logging
This commit is contained in:
Jonatan Heyman 2024-01-01 19:04:40 +01:00 committed by GitHub
parent 0b6a1a49e8
commit 4274e6237b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 103 additions and 10 deletions

View File

@ -1,6 +1,8 @@
import fs from "fs"
import { join } from "path"
import { join, dirname, basename } from "path"
import { app } from "electron"
import * as jetpack from "fs-jetpack";
import CONFIG from "../config"
import { isDev } from "../detect-platform"
@ -21,3 +23,60 @@ export function getBufferFilePath() {
return bufferFilePath
}
}
export class Buffer {
constructor({filePath, onChange}) {
this.filePath = filePath
this.onChange = onChange
this.watcher = null
this.setupWatcher()
this._lastSavedContent = null
}
async load() {
const content = await jetpack.read(this.filePath, 'utf8')
this.setupWatcher()
return content
}
async save(content) {
this._lastSavedContent = content
const saveResult = await jetpack.write(this.filePath, content, {
atomic: true,
mode: '600',
})
return saveResult
}
exists() {
return jetpack.exists(this.filePath) === "file"
}
setupWatcher() {
if (!this.watcher && this.exists()) {
this.watcher = fs.watch(
dirname(this.filePath),
{
persistent: true,
recursive: false,
encoding: "utf8",
},
async (eventType, filename) => {
if (filename !== basename(this.filePath)) {
return
}
// read the file content and compare it to the last saved content
// (if the content is the same, then we can ignore the event)
const content = await jetpack.read(this.filePath, 'utf8')
if (this._lastSavedContent !== content) {
// file has changed on disk, trigger onChange
this.onChange({filename, eventType, content})
}
}
)
}
}
}

View File

@ -11,7 +11,7 @@ import { onBeforeInputEvent } from "../keymap"
import { isDev } from '../detect-platform';
import { initializeAutoUpdate, checkForUpdates } from './auto-update';
import { fixElectronCors } from './cors';
import { getBufferFilePath } from './buffer';
import { getBufferFilePath, Buffer } from './buffer';
// The built directory structure
@ -210,20 +210,24 @@ ipcMain.handle('dark-mode:set', (event, mode) => {
ipcMain.handle('dark-mode:get', () => nativeTheme.themeSource)
const buffer = new Buffer({
filePath: getBufferFilePath(),
onChange: (eventData) => {
win?.webContents.send("buffer-content:change", eventData)
},
})
ipcMain.handle('buffer-content:load', async () => {
let bufferPath = getBufferFilePath()
if (jetpack.exists(bufferPath) === "file") {
return await jetpack.read(bufferPath, 'utf8')
if (buffer.exists()) {
return await buffer.load()
} else {
return isDev? initialDevContent : initialContent
}
});
async function save(content) {
return await jetpack.write(getBufferFilePath(), content, {
atomic: true,
mode: '600',
})
return await buffer.save(content)
}
ipcMain.handle('buffer-content:save', async (event, content) =>  {

View File

@ -53,6 +53,10 @@ contextBridge.exposeInMainWorld("heynote", {
async saveAndQuit(content) {
return await ipcRenderer.invoke("buffer-content:saveAndQuit", content)
},
onChangeCallback(callback) {
ipcRenderer.on("buffer-content:change", callback)
},
},
settings: CONFIG.get("settings"),

View File

@ -21,6 +21,8 @@
},
},
components: {},
data() {
return {
syntaxTreeDebugContent: null,
@ -44,11 +46,16 @@
// load buffer content and create editor
window.heynote.buffer.load().then((content) => {
let diskContent = content
this.editor = new HeynoteEditor({
element: this.$refs.editor,
content: content,
theme: this.theme,
saveFunction: (content) => {
if (content === diskContent) {
return
}
diskContent = content
window.heynote.buffer.save(content)
},
keymap: this.keymap,
@ -57,6 +64,12 @@
})
window._heynote_editor = this.editor
window.document.addEventListener("currenciesLoaded", this.onCurrenciesLoaded)
// set up buffer change listener
window.heynote.buffer.onChangeCallback((event, {filename, content, eventType}) => {
diskContent = content
this.editor.setContent(content)
})
})
// set up window close handler that will save the buffer and quit
window.heynote.onWindowClose(() => {
@ -142,7 +155,7 @@
</div>
</template>
<style lang="sass">
<style lang="sass" scoped>
.debug-syntax-tree
position: absolute
top: 0

View File

@ -48,6 +48,7 @@ export class HeynoteEditor {
this.lineNumberCompartmentPre = new Compartment
this.lineNumberCompartment = new Compartment
this.foldGutterCompartment = new Compartment
this.readOnlyCompartment = new Compartment
this.deselectOnCopy = keymap === "emacs"
const state = EditorState.create({
@ -61,6 +62,8 @@ export class HeynoteEditor {
customSetup,
this.foldGutterCompartment.of(showFoldGutter ? [foldGutter()] : []),
this.readOnlyCompartment.of([]),
this.themeCompartment.of(theme === "dark" ? heynoteDark : heynoteLight),
heynoteBase,
indentUnit.of(" "),
@ -135,6 +138,12 @@ export class HeynoteEditor {
this.view.focus()
}
setReadOnly(readOnly) {
this.view.dispatch({
effects: this.readOnlyCompartment.reconfigure(readOnly ? [EditorState.readOnly.of(true)] : []),
})
}
setTheme(theme) {
this.view.dispatch({
effects: this.themeCompartment.reconfigure(theme === "dark" ? heynoteDark : heynoteLight),

View File

@ -47,6 +47,10 @@ const Heynote = {
async saveAndQuit(content) {
},
onChangeCallback(callback) {
},
},
onWindowClose(callback) {