1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-21 09:38:01 +02:00

Desktop: Regression: Fixed external edit file watching

This commit is contained in:
Laurent Cozic 2020-11-16 11:03:44 +00:00
parent 6103aad2a7
commit a808281dd2
9 changed files with 66 additions and 52 deletions

View File

@ -964,6 +964,9 @@ packages/lib/services/BaseService.js.map
packages/lib/services/CommandService.d.ts
packages/lib/services/CommandService.js
packages/lib/services/CommandService.js.map
packages/lib/services/ExternalEditWatcher.d.ts
packages/lib/services/ExternalEditWatcher.js
packages/lib/services/ExternalEditWatcher.js.map
packages/lib/services/KeymapService.d.ts
packages/lib/services/KeymapService.js
packages/lib/services/KeymapService.js.map

3
.gitignore vendored
View File

@ -956,6 +956,9 @@ packages/lib/services/BaseService.js.map
packages/lib/services/CommandService.d.ts
packages/lib/services/CommandService.js
packages/lib/services/CommandService.js.map
packages/lib/services/ExternalEditWatcher.d.ts
packages/lib/services/ExternalEditWatcher.js
packages/lib/services/ExternalEditWatcher.js.map
packages/lib/services/KeymapService.d.ts
packages/lib/services/KeymapService.js
packages/lib/services/KeymapService.js.map

View File

@ -21,6 +21,7 @@ import menuCommandNames from './gui/menuCommandNames';
import { LayoutItem } from './gui/ResizableLayout/utils/types';
import stateToWhenClauseContext from './services/commands/stateToWhenClauseContext';
import ResourceService from '@joplin/lib/services/ResourceService';
import ExternalEditWatcher from '@joplin/lib/services/ExternalEditWatcher';
const { FoldersScreenUtils } = require('@joplin/lib/folders-screen-utils.js');
const MasterKey = require('@joplin/lib/models/MasterKey');
@ -31,7 +32,6 @@ const { reg } = require('@joplin/lib/registry.js');
const packageInfo = require('./packageInfo.js');
const DecryptionWorker = require('@joplin/lib/services/DecryptionWorker');
const ClipperServer = require('@joplin/lib/ClipperServer');
const ExternalEditWatcher = require('@joplin/lib/services/ExternalEditWatcher');
const { webFrame } = require('electron');
const Menu = bridge().Menu;
const PluginManager = require('@joplin/lib/services/PluginManager');
@ -691,7 +691,7 @@ class Application extends BaseApplication {
}
ExternalEditWatcher.instance().setLogger(reg.logger());
ExternalEditWatcher.instance().dispatch = this.store().dispatch;
ExternalEditWatcher.instance().initialize(bridge, this.store().dispatch);
ResourceEditWatcher.instance().initialize(reg.logger(), (action: any) => { this.store().dispatch(action); });

View File

@ -1,8 +1,8 @@
import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService';
import { _ } from '@joplin/lib/locale';
import { stateUtils } from '@joplin/lib/reducer';
import ExternalEditWatcher from '@joplin/lib/services/ExternalEditWatcher';
const Note = require('@joplin/lib/models/Note');
const ExternalEditWatcher = require('@joplin/lib/services/ExternalEditWatcher');
const bridge = require('electron').remote.require('./bridge').default;
export const declaration: CommandDeclaration = {

View File

@ -1,7 +1,7 @@
import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService';
import { _ } from '@joplin/lib/locale';
import { stateUtils } from '@joplin/lib/reducer';
const ExternalEditWatcher = require('@joplin/lib/services/ExternalEditWatcher');
import ExternalEditWatcher from '@joplin/lib/services/ExternalEditWatcher';
export const declaration: CommandDeclaration = {
name: 'stopExternalEditing',

View File

@ -29,6 +29,7 @@ import markupLanguageUtils from '@joplin/lib/markupLanguageUtils';
import usePrevious from '../hooks/usePrevious';
import Setting from '@joplin/lib/models/Setting';
import stateToWhenClauseContext from '../../services/commands/stateToWhenClauseContext';
import ExternalEditWatcher from '@joplin/lib/services/ExternalEditWatcher';
const { themeStyle } = require('@joplin/lib/theme');
const { substrWithEllipsis } = require('@joplin/lib/string-utils');
@ -36,7 +37,6 @@ const NoteSearchBar = require('../NoteSearchBar.min.js');
const { reg } = require('@joplin/lib/registry.js');
const Note = require('@joplin/lib/models/Note.js');
const bridge = require('electron').remote.require('./bridge').default;
const ExternalEditWatcher = require('@joplin/lib/services/ExternalEditWatcher');
const NoteRevisionViewer = require('../NoteRevisionViewer.min');
const commands = [

View File

@ -1,49 +1,57 @@
const Logger = require('../Logger').default;
import Logger from '../Logger';
import Setting from '../models/Setting';
import shim from '../shim';
import { fileExtension, basename, toSystemSlashes } from '../path-utils';
import time from '../time';
import { NoteEntity } from './database/types';
const Note = require('../models/Note');
const Setting = require('../models/Setting').default;
const shim = require('../shim').default;
const EventEmitter = require('events');
const { splitCommandString } = require('../string-utils');
const { fileExtension, basename } = require('../path-utils');
const spawn = require('child_process').spawn;
const chokidar = require('chokidar');
const bridge = require('electron').remote.require('./bridge').default;
const time = require('../time').default;
const { ErrorNotFound } = require('./rest/utils/errors');
class ExternalEditWatcher {
constructor() {
this.logger_ = new Logger();
this.dispatch = () => {};
this.watcher_ = null;
this.eventEmitter_ = new EventEmitter();
this.skipNextChangeEvent_ = {};
this.chokidar_ = chokidar;
}
export default class ExternalEditWatcher {
static instance() {
private dispatch: Function;
private bridge_: Function;
private logger_: Logger = new Logger();
private watcher_: any = null;
private eventEmitter_: any = new EventEmitter();
private skipNextChangeEvent_: any = {};
private chokidar_: any = chokidar;
private static instance_: ExternalEditWatcher;
public static instance() {
if (this.instance_) return this.instance_;
this.instance_ = new ExternalEditWatcher();
return this.instance_;
}
externalApi() {
const loadNote = async (noteId) => {
public initialize(bridge: Function, dispatch: Function) {
this.bridge_ = bridge;
this.dispatch = dispatch;
}
public externalApi() {
const loadNote = async (noteId: string) => {
const note = await Note.load(noteId);
if (!note) throw new ErrorNotFound(`No such note: ${noteId}`);
return note;
};
return {
openAndWatch: async ({ noteId }) => {
const note = await loadNote(noteId);
openAndWatch: async (args: any) => {
const note = await loadNote(args.noteId);
return this.openAndWatch(note);
},
stopWatching: async ({ noteId }) => {
return this.stopWatching(noteId);
stopWatching: async (args: any) => {
return this.stopWatching(args.noteId);
},
noteIsWatched: async ({ noteId }) => {
const note = await loadNote(noteId);
noteIsWatched: async (args: any) => {
const note = await loadNote(args.noteId);
return this.noteIsWatched(note);
},
};
@ -53,15 +61,15 @@ class ExternalEditWatcher {
return Setting.value('profileDir');
}
on(eventName, callback) {
on(eventName: string, callback: Function) {
return this.eventEmitter_.on(eventName, callback);
}
off(eventName, callback) {
off(eventName: string, callback: Function) {
return this.eventEmitter_.removeListener(eventName, callback);
}
setLogger(l) {
setLogger(l: Logger) {
this.logger_ = l;
}
@ -69,12 +77,12 @@ class ExternalEditWatcher {
return this.logger_;
}
watch(fileToWatch) {
watch(fileToWatch: string) {
if (!this.chokidar_) return;
if (!this.watcher_) {
this.watcher_ = this.chokidar_.watch(fileToWatch);
this.watcher_.on('all', async (event, path) => {
this.watcher_.on('all', async (event: string, path: string) => {
// For now, to investigate the lost content issue when using an external editor,
// make all the debug statement to info() so that it goes to the log file.
// Those that were previous debug() statements are marked as "was_debug"
@ -140,7 +148,8 @@ class ExternalEditWatcher {
});
// Hack to support external watcher on some linux applications (gedit, gvim, etc)
// taken from https://github.com/paulmillr/chokidar/issues/591
this.watcher_.on('raw', async (event, path, { watchedPath }) => {
this.watcher_.on('raw', async (event: string, _path: string, options: any) => {
const watchedPath: string = options.watchedPath;
/* was_debug */ this.logger().info(`ExternalEditWatcher: Raw event: ${event}: ${watchedPath}`);
if (event === 'rename') {
this.watcher_.unwatch(watchedPath);
@ -154,12 +163,12 @@ class ExternalEditWatcher {
return this.watcher_;
}
noteIdToFilePath_(noteId) {
noteIdToFilePath_(noteId: string) {
return `${this.tempDir()}/edit-${noteId}.md`;
}
noteFilePathToId_(path) {
let id = path.split('/');
noteFilePathToId_(path: string) {
let id: any = toSystemSlashes(path, 'linux').split('/');
if (!id.length) throw new Error(`Invalid path: ${path}`);
id = id[id.length - 1];
id = id.split('.');
@ -186,7 +195,7 @@ class ExternalEditWatcher {
return output;
}
noteIsWatched(note) {
noteIsWatched(note: NoteEntity) {
if (!this.watcher_) return false;
const noteFilename = basename(this.noteIdToFilePath_(note.id));
@ -219,7 +228,7 @@ class ExternalEditWatcher {
};
}
async spawnCommand(path, args, options) {
async spawnCommand(path: string, args: string[], options: any) {
return new Promise((resolve, reject) => {
// App bundles need to be opened using the `open` command.
// Additional args can be specified after --args, and the
@ -238,7 +247,7 @@ class ExternalEditWatcher {
path = 'open';
}
const wrapError = error => {
const wrapError = (error: any) => {
if (!error) return error;
const msg = error.message ? [error.message] : [];
msg.push(`Command was: "${path}" ${args.join(' ')}`);
@ -257,7 +266,7 @@ class ExternalEditWatcher {
}
}, 100);
subProcess.on('error', error => {
subProcess.on('error', (error: any) => {
shim.clearInterval(iid);
reject(wrapError(error));
});
@ -267,18 +276,19 @@ class ExternalEditWatcher {
});
}
async openAndWatch(note) {
async openAndWatch(note: NoteEntity) {
if (!note || !note.id) {
this.logger().warn('ExternalEditWatcher: Cannot open note: ', note);
return;
}
const filePath = await this.writeNoteToFile_(note);
if (!filePath) return;
this.watch(filePath);
const cmd = this.textEditorCommand();
if (!cmd) {
bridge().openExternal(`file://${filePath}`);
this.bridge_().openExternal(`file://${filePath}`);
} else {
cmd.args.push(filePath);
await this.spawnCommand(cmd.path, cmd.args, { detached: true });
@ -292,7 +302,7 @@ class ExternalEditWatcher {
this.logger().info(`ExternalEditWatcher: Started watching ${filePath}`);
}
async stopWatching(noteId) {
async stopWatching(noteId: string) {
if (!noteId) return;
const filePath = this.noteIdToFilePath_(noteId);
@ -319,7 +329,7 @@ class ExternalEditWatcher {
});
}
async updateNoteFile(note) {
async updateNoteFile(note: NoteEntity) {
if (!this.noteIsWatched(note)) return;
if (!note || !note.id) {
@ -336,10 +346,10 @@ class ExternalEditWatcher {
this.writeNoteToFile_(note);
}
async writeNoteToFile_(note) {
async writeNoteToFile_(note: NoteEntity) {
if (!note || !note.id) {
this.logger().warn('ExternalEditWatcher: Cannot update note file: ', note);
return;
return null;
}
const filePath = this.noteIdToFilePath_(note.id);
@ -348,5 +358,3 @@ class ExternalEditWatcher {
return filePath;
}
}
module.exports = ExternalEditWatcher;

View File

@ -1,5 +1,5 @@
import ResourceEditWatcher from '../ResourceEditWatcher/index';
const ExternalEditWatcher = require('../ExternalEditWatcher');
import ExternalEditWatcher from '../ExternalEditWatcher';
export default {

View File

@ -1,4 +1,4 @@
const MdToHtml = require('./MdToHtml').default;
import MdToHtml from './MdToHtml';
const HtmlToHtml = require('./HtmlToHtml');
const htmlUtils = require('./htmlUtils');
const MarkdownIt = require('markdown-it');