mirror of
https://github.com/heyman/heynote.git
synced 2024-11-25 01:13:17 +01:00
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:
parent
0b6a1a49e8
commit
4274e6237b
@ -1,6 +1,8 @@
|
|||||||
import fs from "fs"
|
import fs from "fs"
|
||||||
import { join } from "path"
|
import { join, dirname, basename } from "path"
|
||||||
import { app } from "electron"
|
import { app } from "electron"
|
||||||
|
import * as jetpack from "fs-jetpack";
|
||||||
|
|
||||||
import CONFIG from "../config"
|
import CONFIG from "../config"
|
||||||
import { isDev } from "../detect-platform"
|
import { isDev } from "../detect-platform"
|
||||||
|
|
||||||
@ -21,3 +23,60 @@ export function getBufferFilePath() {
|
|||||||
return bufferFilePath
|
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})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -11,7 +11,7 @@ import { onBeforeInputEvent } from "../keymap"
|
|||||||
import { isDev } from '../detect-platform';
|
import { isDev } from '../detect-platform';
|
||||||
import { initializeAutoUpdate, checkForUpdates } from './auto-update';
|
import { initializeAutoUpdate, checkForUpdates } from './auto-update';
|
||||||
import { fixElectronCors } from './cors';
|
import { fixElectronCors } from './cors';
|
||||||
import { getBufferFilePath } from './buffer';
|
import { getBufferFilePath, Buffer } from './buffer';
|
||||||
|
|
||||||
|
|
||||||
// The built directory structure
|
// The built directory structure
|
||||||
@ -210,20 +210,24 @@ ipcMain.handle('dark-mode:set', (event, mode) => {
|
|||||||
|
|
||||||
ipcMain.handle('dark-mode:get', () => nativeTheme.themeSource)
|
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 () => {
|
ipcMain.handle('buffer-content:load', async () => {
|
||||||
let bufferPath = getBufferFilePath()
|
if (buffer.exists()) {
|
||||||
if (jetpack.exists(bufferPath) === "file") {
|
return await buffer.load()
|
||||||
return await jetpack.read(bufferPath, 'utf8')
|
|
||||||
} else {
|
} else {
|
||||||
return isDev? initialDevContent : initialContent
|
return isDev? initialDevContent : initialContent
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
async function save(content) {
|
async function save(content) {
|
||||||
return await jetpack.write(getBufferFilePath(), content, {
|
return await buffer.save(content)
|
||||||
atomic: true,
|
|
||||||
mode: '600',
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ipcMain.handle('buffer-content:save', async (event, content) => {
|
ipcMain.handle('buffer-content:save', async (event, content) => {
|
||||||
|
@ -53,6 +53,10 @@ contextBridge.exposeInMainWorld("heynote", {
|
|||||||
async saveAndQuit(content) {
|
async saveAndQuit(content) {
|
||||||
return await ipcRenderer.invoke("buffer-content:saveAndQuit", content)
|
return await ipcRenderer.invoke("buffer-content:saveAndQuit", content)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onChangeCallback(callback) {
|
||||||
|
ipcRenderer.on("buffer-content:change", callback)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
settings: CONFIG.get("settings"),
|
settings: CONFIG.get("settings"),
|
||||||
|
@ -21,6 +21,8 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
components: {},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
syntaxTreeDebugContent: null,
|
syntaxTreeDebugContent: null,
|
||||||
@ -44,11 +46,16 @@
|
|||||||
|
|
||||||
// load buffer content and create editor
|
// load buffer content and create editor
|
||||||
window.heynote.buffer.load().then((content) => {
|
window.heynote.buffer.load().then((content) => {
|
||||||
|
let diskContent = content
|
||||||
this.editor = new HeynoteEditor({
|
this.editor = new HeynoteEditor({
|
||||||
element: this.$refs.editor,
|
element: this.$refs.editor,
|
||||||
content: content,
|
content: content,
|
||||||
theme: this.theme,
|
theme: this.theme,
|
||||||
saveFunction: (content) => {
|
saveFunction: (content) => {
|
||||||
|
if (content === diskContent) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
diskContent = content
|
||||||
window.heynote.buffer.save(content)
|
window.heynote.buffer.save(content)
|
||||||
},
|
},
|
||||||
keymap: this.keymap,
|
keymap: this.keymap,
|
||||||
@ -57,6 +64,12 @@
|
|||||||
})
|
})
|
||||||
window._heynote_editor = this.editor
|
window._heynote_editor = this.editor
|
||||||
window.document.addEventListener("currenciesLoaded", this.onCurrenciesLoaded)
|
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
|
// set up window close handler that will save the buffer and quit
|
||||||
window.heynote.onWindowClose(() => {
|
window.heynote.onWindowClose(() => {
|
||||||
@ -142,7 +155,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass" scoped>
|
||||||
.debug-syntax-tree
|
.debug-syntax-tree
|
||||||
position: absolute
|
position: absolute
|
||||||
top: 0
|
top: 0
|
||||||
|
@ -48,6 +48,7 @@ export class HeynoteEditor {
|
|||||||
this.lineNumberCompartmentPre = new Compartment
|
this.lineNumberCompartmentPre = new Compartment
|
||||||
this.lineNumberCompartment = new Compartment
|
this.lineNumberCompartment = new Compartment
|
||||||
this.foldGutterCompartment = new Compartment
|
this.foldGutterCompartment = new Compartment
|
||||||
|
this.readOnlyCompartment = new Compartment
|
||||||
this.deselectOnCopy = keymap === "emacs"
|
this.deselectOnCopy = keymap === "emacs"
|
||||||
|
|
||||||
const state = EditorState.create({
|
const state = EditorState.create({
|
||||||
@ -60,6 +61,8 @@ export class HeynoteEditor {
|
|||||||
this.lineNumberCompartment.of(showLineNumberGutter ? [lineNumbers(), blockLineNumbers] : []),
|
this.lineNumberCompartment.of(showLineNumberGutter ? [lineNumbers(), blockLineNumbers] : []),
|
||||||
customSetup,
|
customSetup,
|
||||||
this.foldGutterCompartment.of(showFoldGutter ? [foldGutter()] : []),
|
this.foldGutterCompartment.of(showFoldGutter ? [foldGutter()] : []),
|
||||||
|
|
||||||
|
this.readOnlyCompartment.of([]),
|
||||||
|
|
||||||
this.themeCompartment.of(theme === "dark" ? heynoteDark : heynoteLight),
|
this.themeCompartment.of(theme === "dark" ? heynoteDark : heynoteLight),
|
||||||
heynoteBase,
|
heynoteBase,
|
||||||
@ -135,6 +138,12 @@ export class HeynoteEditor {
|
|||||||
this.view.focus()
|
this.view.focus()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setReadOnly(readOnly) {
|
||||||
|
this.view.dispatch({
|
||||||
|
effects: this.readOnlyCompartment.reconfigure(readOnly ? [EditorState.readOnly.of(true)] : []),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
setTheme(theme) {
|
setTheme(theme) {
|
||||||
this.view.dispatch({
|
this.view.dispatch({
|
||||||
effects: this.themeCompartment.reconfigure(theme === "dark" ? heynoteDark : heynoteLight),
|
effects: this.themeCompartment.reconfigure(theme === "dark" ? heynoteDark : heynoteLight),
|
||||||
|
@ -47,6 +47,10 @@ const Heynote = {
|
|||||||
async saveAndQuit(content) {
|
async saveAndQuit(content) {
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onChangeCallback(callback) {
|
||||||
|
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
onWindowClose(callback) {
|
onWindowClose(callback) {
|
||||||
|
Loading…
Reference in New Issue
Block a user