You've already forked joplin
							
							
				mirror of
				https://github.com/laurent22/joplin.git
				synced 2025-10-31 00:07:48 +02:00 
			
		
		
		
	Fixed editing notes
This commit is contained in:
		| @@ -52,8 +52,20 @@ class AppGui { | ||||
|  | ||||
| 		this.inputMode_ = AppGui.INPUT_MODE_NORMAL; | ||||
|  | ||||
| 		this.commandCancelCalled_ = false; | ||||
|  | ||||
| 		this.currentShortcutKeys_ = []; | ||||
| 		this.lastShortcutKeyTime_ = 0;	 | ||||
|  | ||||
| 		cliUtils.setStdout((...object) => { | ||||
| 			for (let i = 0; i < object.length; i++) { | ||||
| 				this.widget('console').bufferPush(object[i]); | ||||
| 			} | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	renderer() { | ||||
| 		return this.renderer_; | ||||
| 	} | ||||
|  | ||||
| 	buildUi() { | ||||
| @@ -112,8 +124,10 @@ class AppGui { | ||||
| 		consoleWidget.hStretch = true; | ||||
| 		consoleWidget.name = 'console'; | ||||
| 		consoleWidget.prompt = chalk.green('Joplin') + ' ' + chalk.magenta('>') + ' '; | ||||
| 		consoleWidget.on('accept', (event) => { | ||||
| 			this.processCommand(event.input, 'console'); | ||||
| 		consoleWidget.on('accept', async (event) => { | ||||
| 			consoleWidget.promptVisible = false; | ||||
| 			await this.processCommand(event.input, 'console'); | ||||
| 			consoleWidget.promptVisible = true; | ||||
| 		}); | ||||
|  | ||||
| 		const hLayout = new HLayoutWidget(); | ||||
| @@ -330,11 +344,21 @@ class AppGui { | ||||
| 		return ['ENTER', 'DOWN', 'UP', 'LEFT', 'RIGHT', 'DELETE', 'BACKSPACE', 'ESCAPE', 'TAB', 'SHIFT_TAB'].indexOf(name) >= 0; | ||||
| 	} | ||||
|  | ||||
| 	fullScreen(enable = true) { | ||||
| 		if (enable) { | ||||
| 			this.term().fullscreen(); | ||||
| 			this.term().hideCursor(); | ||||
| 			this.widget('root').invalidate(); | ||||
| 		} else { | ||||
| 			this.term().fullscreen(false); | ||||
| 			this.term().showCursor(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	async start() { | ||||
| 		const term = this.term(); | ||||
|  | ||||
| 		term.fullscreen(); | ||||
| 		term.hideCursor(); | ||||
| 		this.fullScreen(); | ||||
|  | ||||
| 		try { | ||||
| 			this.renderer_.start(); | ||||
| @@ -344,13 +368,33 @@ class AppGui { | ||||
| 			term.grabInput(); | ||||
|  | ||||
| 			term.on('key', async (name, matches, data) => { | ||||
| 				if (name === 'CTRL_C' ) { | ||||
| 					term.showCursor(); | ||||
| 					term.fullscreen(false); | ||||
|  | ||||
| 				if (name === 'CTRL_D') { | ||||
| 					const cmd = this.app().currentCommand(); | ||||
|  | ||||
| 					if (cmd && cmd.cancellable() && !this.commandCancelCalled_) { | ||||
| 						this.commandCancelCalled_ = true; | ||||
| 						await cmd.cancel(); | ||||
| 						this.commandCancelCalled_ = false; | ||||
| 					} | ||||
|  | ||||
| 					this.fullScreen(false); | ||||
| 					await this.app().exit(); | ||||
| 					return; | ||||
| 				} | ||||
|  | ||||
| 				if (name === 'CTRL_C' ) { | ||||
| 					const cmd = this.app().currentCommand(); | ||||
| 					if (!cmd || !cmd.cancellable() || this.commandCancelCalled_) { | ||||
| 						consoleWidget.bufferPush(_('Press Ctrl+D or type "exit" to exit the application')); | ||||
| 					} else { | ||||
| 						this.commandCancelCalled_ = true; | ||||
| 						await cmd.cancel(); | ||||
| 						this.commandCancelCalled_ = false; | ||||
| 					} | ||||
| 					return; | ||||
| 				} | ||||
| 				 | ||||
| 				const now = (new Date()).getTime(); | ||||
|  | ||||
| 				if (now - this.lastShortcutKeyTime_ > 800 || this.isSpecialKey(name)) { | ||||
| @@ -381,24 +425,20 @@ class AppGui { | ||||
| 								cmd(); | ||||
| 							} else { | ||||
| 								consoleWidget.bufferPush(cmd); | ||||
| 								consoleWidget.pause(); | ||||
| 								await this.processCommand(cmd); | ||||
| 								consoleWidget.resume(); | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			}); | ||||
| 		} catch (error) { | ||||
| 			this.fullScreen(false); | ||||
| 			this.logger().error(error); | ||||
| 			term.fullscreen(false); | ||||
| 			this.term.showCursor(); | ||||
| 			console.error(error); | ||||
| 		} | ||||
|  | ||||
| 		process.on('unhandledRejection', (reason, p) => { | ||||
| 			term.fullscreen(false); | ||||
| 			this.term.showCursor(); | ||||
| 			this.fullScreen(false); | ||||
| 			console.error('Unhandled promise rejection', p, 'reason:', reason); | ||||
| 			process.exit(1); | ||||
| 		}); | ||||
|   | ||||
| @@ -278,6 +278,11 @@ class Application { | ||||
| 			} | ||||
| 		}); | ||||
|  | ||||
| 		cmd.setForceRender(async () => { | ||||
| 			this.gui_.widget('root').invalidate(); | ||||
| 			await this.gui_.renderer().forceRender(); | ||||
| 		}); | ||||
|  | ||||
| 		cmd.setPrompt(async (message, options) => { | ||||
| 			consoleWidget.focus(); | ||||
|  | ||||
| @@ -384,6 +389,7 @@ class Application { | ||||
| 		this.activeCommand_ = this.findCommandByName(commandName); | ||||
| 		const cmdArgs = cliUtils.makeCommandArgs(this.activeCommand_, argv); | ||||
| 		await this.activeCommand_.action(cmdArgs); | ||||
| 		this.activeCommand_ = null; | ||||
| 	} | ||||
|  | ||||
| 	currentCommand() { | ||||
|   | ||||
| @@ -55,6 +55,14 @@ class BaseCommand { | ||||
| 		if (this.stdout_) this.stdout_(...object); | ||||
| 	} | ||||
|  | ||||
| 	setForceRender(fn) { | ||||
| 		this.forceRender_ = fn; | ||||
| 	} | ||||
|  | ||||
| 	async forceRender() { | ||||
| 		if (this.forceRender_) await this.forceRender_(); | ||||
| 	} | ||||
|  | ||||
| 	setPrompt(fn) { | ||||
| 		this.prompt_ = fn; | ||||
| 	} | ||||
|   | ||||
| @@ -12,7 +12,7 @@ cliUtils.splitCommandString = function(s) { | ||||
| 	let r = yargParser(s); | ||||
| 	let output = []; | ||||
| 	for (let i = 0; i < r._.length; i++) { | ||||
| 		let a = r._[i]; | ||||
| 		let a = r._[i].toString(); | ||||
| 		a = a.replace(/__JOP_DASH_JOP_DASH__/g, '--'); | ||||
| 		a = a.replace(/__JOP_DASH__/g, '-'); | ||||
| 		output.push(a); | ||||
| @@ -213,11 +213,15 @@ let redrawStarted_ = false; | ||||
| let redrawLastLog_ = null; | ||||
| let redrawLastUpdateTime_ = 0; | ||||
|  | ||||
| cliUtils.setStdout = function(v) { | ||||
| 	this.stdout_ = v; | ||||
| } | ||||
|  | ||||
| cliUtils.redraw = function(s) { | ||||
| 	const now = time.unixMs(); | ||||
|  | ||||
| 	if (now - redrawLastUpdateTime_ > 4000) { | ||||
| 		console.info(s); | ||||
| 		this.stdout_ (s); | ||||
| 		redrawLastUpdateTime_ = now; | ||||
| 		redrawLastLog_ = null; | ||||
| 	} else { | ||||
| @@ -231,7 +235,7 @@ cliUtils.redrawDone = function() { | ||||
| 	if (!redrawStarted_) return; | ||||
|  | ||||
| 	if (redrawLastLog_) { | ||||
| 		console.info(redrawLastLog_); | ||||
| 		this.stdout_(redrawLastLog_); | ||||
| 	} | ||||
|  | ||||
| 	redrawLastLog_ = null; | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| import fs from 'fs-extra'; | ||||
| import { BaseCommand } from './base-command.js'; | ||||
| import { uuid } from 'lib/uuid.js'; | ||||
| import { app } from './app.js'; | ||||
| import { _ } from 'lib/locale.js'; | ||||
| import { Folder } from 'lib/models/folder.js'; | ||||
| @@ -7,6 +8,7 @@ import { Note } from 'lib/models/note.js'; | ||||
| import { Setting } from 'lib/models/setting.js'; | ||||
| import { BaseModel } from 'lib/base-model.js'; | ||||
| import { cliUtils } from './cli-utils.js'; | ||||
| import { time } from 'lib/time-utils.js'; | ||||
|  | ||||
| class Command extends BaseCommand { | ||||
|  | ||||
| @@ -20,13 +22,10 @@ class Command extends BaseCommand { | ||||
|  | ||||
| 	async action(args) { | ||||
| 		let watcher = null; | ||||
| 		let newNote = null; | ||||
| 		let tempFilePath = null; | ||||
|  | ||||
| 		const onFinishedEditing = async () => { | ||||
| 			if (watcher) watcher.close(); | ||||
| 			//app().vorpal().show(); | ||||
| 			newNote = null; | ||||
| 			this.stdout(_('Done editing.')); | ||||
| 			if (tempFilePath) fs.removeSync(tempFilePath); | ||||
| 		} | ||||
|  | ||||
| 		const textEditorPath = () => { | ||||
| @@ -36,6 +35,10 @@ class Command extends BaseCommand { | ||||
| 		} | ||||
|  | ||||
| 		try {		 | ||||
| 			// ------------------------------------------------------------------------- | ||||
| 			// Load note or create it if it doesn't exist | ||||
| 			// ------------------------------------------------------------------------- | ||||
|  | ||||
| 			let title = args['note']; | ||||
|  | ||||
| 			if (!app().currentFolder()) throw new Error(_('No active notebook.')); | ||||
| @@ -44,47 +47,55 @@ class Command extends BaseCommand { | ||||
| 			if (!note) { | ||||
| 				const ok = await this.prompt(_('Note does not exist: "%s". Create it?', title)); | ||||
| 				if (!ok) return; | ||||
| 				newNote = await Note.save({ title: title, parent_id: app().currentFolder().id }); | ||||
| 				note = await Note.load(newNote.id); | ||||
| 				note = await Note.save({ title: title, parent_id: app().currentFolder().id }); | ||||
| 				note = await Note.load(note.id); | ||||
| 			} | ||||
|  | ||||
| 			// ------------------------------------------------------------------------- | ||||
| 			// Create the file to be edited and prepare the editor program arguments | ||||
| 			// ------------------------------------------------------------------------- | ||||
|  | ||||
| 			let editorPath = textEditorPath(); | ||||
| 			let editorArgs = editorPath.split(' '); | ||||
|  | ||||
| 			editorPath = editorArgs[0]; | ||||
| 			editorArgs = editorArgs.splice(1); | ||||
|  | ||||
| 			let content = await Note.serializeForEdit(note); | ||||
| 			const originalContent = await Note.serializeForEdit(note); | ||||
|  | ||||
| 			let tempFilePath = Setting.value('tempDir') + '/' + Note.systemPath(note); | ||||
| 			tempFilePath = Setting.value('tempDir') + '/' + uuid.create() + '.md'; | ||||
| 			editorArgs.push(tempFilePath); | ||||
|  | ||||
| 			const spawn	= require('child_process').spawn; | ||||
| 			await fs.writeFile(tempFilePath, originalContent); | ||||
|  | ||||
| 			// ------------------------------------------------------------------------- | ||||
| 			// Start editing the file | ||||
| 			// ------------------------------------------------------------------------- | ||||
|  | ||||
| 			this.logger().info('Disabling fullscreen...'); | ||||
|  | ||||
| 			this.stdout(_('Starting to edit note. Close the editor to get back to the prompt.')); | ||||
| 			await this.forceRender(); | ||||
|  | ||||
| 			await fs.writeFile(tempFilePath, content); | ||||
| 			const spawnSync	= require('child_process').spawnSync; | ||||
| 			spawnSync(editorPath, editorArgs, { stdio: 'inherit' }); | ||||
|  | ||||
| 			let watchTimeout = null; | ||||
| 			watcher = fs.watch(tempFilePath, (eventType, filename) => { | ||||
| 				// We need a timeout because for each change to the file, multiple events are generated. | ||||
| 			await this.forceRender(); | ||||
|  | ||||
| 				if (watchTimeout) return; | ||||
| 			// ------------------------------------------------------------------------- | ||||
| 			// Save the note and clean up | ||||
| 			// ------------------------------------------------------------------------- | ||||
|  | ||||
| 				watchTimeout = setTimeout(async () => { | ||||
| 					let updatedNote = await fs.readFile(tempFilePath, 'utf8'); | ||||
| 					updatedNote = await Note.unserializeForEdit(updatedNote); | ||||
| 			const updatedContent = await fs.readFile(tempFilePath, 'utf8'); | ||||
| 			if (updatedContent !== originalContent) { | ||||
| 				let updatedNote = await Note.unserializeForEdit(updatedContent); | ||||
| 				updatedNote.id = note.id; | ||||
| 				await Note.save(updatedNote); | ||||
| 					//process.stdout.write('.'); | ||||
| 					watchTimeout = null; | ||||
| 				}, 200); | ||||
| 			}); | ||||
| 				this.logger().info('Note has been saved'); | ||||
| 			} | ||||
|  | ||||
| 			const childProcess = spawn(editorPath, editorArgs, { stdio: 'inherit' }); | ||||
| 			childProcess.on('exit', async (error, code) => { | ||||
| 			await onFinishedEditing(); | ||||
| 			}); | ||||
|  | ||||
| 		} catch(error) { | ||||
| 			await onFinishedEditing(); | ||||
| 			throw error; | ||||
|   | ||||
| @@ -153,7 +153,7 @@ class Command extends BaseCommand { | ||||
|  | ||||
| 		if (reg.syncHasAuth(target)) { | ||||
| 			let sync = await reg.synchronizer(target); | ||||
| 			if (sync) sync.cancel(); | ||||
| 			if (sync) await sync.cancel(); | ||||
| 		} else { | ||||
| 			if (this.releaseLockFn_) this.releaseLockFn_(); | ||||
| 			this.releaseLockFn_ = null; | ||||
|   | ||||
| @@ -49,18 +49,18 @@ if (process.platform === "win32") { | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| let commandCancelCalled_ = false; | ||||
| // let commandCancelCalled_ = false; | ||||
|  | ||||
| process.on("SIGINT", async function() { | ||||
| 	const cmd = application.currentCommand(); | ||||
| // process.on("SIGINT", async function() { | ||||
| // 	const cmd = application.currentCommand(); | ||||
|  | ||||
| 	if (!cmd || !cmd.cancellable() || commandCancelCalled_) { | ||||
| 		process.exit(0); | ||||
| 	} else { | ||||
| 		commandCancelCalled_ = true; | ||||
| 		await cmd.cancel(); | ||||
| 	} | ||||
| }); | ||||
| // 	if (!cmd || !cmd.cancellable() || commandCancelCalled_) { | ||||
| // 		process.exit(0); | ||||
| // 	} else { | ||||
| // 		commandCancelCalled_ = true; | ||||
| // 		await cmd.cancel(); | ||||
| // 	} | ||||
| // }); | ||||
|  | ||||
| process.stdout.on('error', function( err ) { | ||||
| 	// https://stackoverflow.com/questions/12329816/error-write-epipe-when-piping-node-output-to-head#15884508 | ||||
|   | ||||
| @@ -39,6 +39,9 @@ msgstr "" | ||||
| msgid "Maximise/minimise the console" | ||||
| msgstr "" | ||||
|  | ||||
| msgid "Press Ctrl+D or type \"exit\" to exit the application" | ||||
| msgstr "" | ||||
|  | ||||
| msgid "[Cancel]" | ||||
| msgstr "" | ||||
|  | ||||
| @@ -172,9 +175,6 @@ msgstr "" | ||||
| msgid "Edit note." | ||||
| msgstr "" | ||||
|  | ||||
| msgid "Done editing." | ||||
| msgstr "" | ||||
|  | ||||
| msgid "" | ||||
| "No text editor is defined. Please set it using `config editor <editor-path>`" | ||||
| msgstr "" | ||||
|   | ||||
| @@ -46,6 +46,9 @@ msgstr "Créer un carnet." | ||||
| msgid "Maximise/minimise the console" | ||||
| msgstr "Quitter le logiciel." | ||||
|  | ||||
| msgid "Press Ctrl+D or type \"exit\" to exit the application" | ||||
| msgstr "" | ||||
|  | ||||
| #, fuzzy | ||||
| msgid "[Cancel]" | ||||
| msgstr "Annulation..." | ||||
| @@ -189,9 +192,6 @@ msgstr "" | ||||
| msgid "Edit note." | ||||
| msgstr "Editer la note." | ||||
|  | ||||
| msgid "Done editing." | ||||
| msgstr "Edition terminée." | ||||
|  | ||||
| msgid "" | ||||
| "No text editor is defined. Please set it using `config editor <editor-path>`" | ||||
| msgstr "" | ||||
| @@ -744,6 +744,9 @@ msgstr "" | ||||
| msgid "Welcome" | ||||
| msgstr "Bienvenue" | ||||
|  | ||||
| #~ msgid "Done editing." | ||||
| #~ msgstr "Edition terminée." | ||||
|  | ||||
| #, fuzzy | ||||
| #~ msgid "Confirm" | ||||
| #~ msgstr "Conflits" | ||||
|   | ||||
| @@ -39,6 +39,9 @@ msgstr "" | ||||
| msgid "Maximise/minimise the console" | ||||
| msgstr "" | ||||
|  | ||||
| msgid "Press Ctrl+D or type \"exit\" to exit the application" | ||||
| msgstr "" | ||||
|  | ||||
| msgid "[Cancel]" | ||||
| msgstr "" | ||||
|  | ||||
| @@ -172,9 +175,6 @@ msgstr "" | ||||
| msgid "Edit note." | ||||
| msgstr "" | ||||
|  | ||||
| msgid "Done editing." | ||||
| msgstr "" | ||||
|  | ||||
| msgid "" | ||||
| "No text editor is defined. Please set it using `config editor <editor-path>`" | ||||
| msgstr "" | ||||
|   | ||||
| @@ -136,11 +136,20 @@ class Synchronizer { | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	cancel() { | ||||
| 	async cancel() { | ||||
| 		if (this.cancelling_ || this.state() == 'idle') return; | ||||
| 		 | ||||
| 		this.logSyncOperation('cancelling', null, null, ''); | ||||
| 		this.cancelling_ = true; | ||||
|  | ||||
| 		return new Promise((resolve, reject) => { | ||||
| 			const iid = setInterval(() => { | ||||
| 				if (this.state() == 'idle') { | ||||
| 					clearInterval(iid); | ||||
| 					resolve(); | ||||
| 				} | ||||
| 			}, 100); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	cancelling() { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user