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:
parent
6103aad2a7
commit
a808281dd2
@ -964,6 +964,9 @@ packages/lib/services/BaseService.js.map
|
|||||||
packages/lib/services/CommandService.d.ts
|
packages/lib/services/CommandService.d.ts
|
||||||
packages/lib/services/CommandService.js
|
packages/lib/services/CommandService.js
|
||||||
packages/lib/services/CommandService.js.map
|
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.d.ts
|
||||||
packages/lib/services/KeymapService.js
|
packages/lib/services/KeymapService.js
|
||||||
packages/lib/services/KeymapService.js.map
|
packages/lib/services/KeymapService.js.map
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -956,6 +956,9 @@ packages/lib/services/BaseService.js.map
|
|||||||
packages/lib/services/CommandService.d.ts
|
packages/lib/services/CommandService.d.ts
|
||||||
packages/lib/services/CommandService.js
|
packages/lib/services/CommandService.js
|
||||||
packages/lib/services/CommandService.js.map
|
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.d.ts
|
||||||
packages/lib/services/KeymapService.js
|
packages/lib/services/KeymapService.js
|
||||||
packages/lib/services/KeymapService.js.map
|
packages/lib/services/KeymapService.js.map
|
||||||
|
@ -21,6 +21,7 @@ import menuCommandNames from './gui/menuCommandNames';
|
|||||||
import { LayoutItem } from './gui/ResizableLayout/utils/types';
|
import { LayoutItem } from './gui/ResizableLayout/utils/types';
|
||||||
import stateToWhenClauseContext from './services/commands/stateToWhenClauseContext';
|
import stateToWhenClauseContext from './services/commands/stateToWhenClauseContext';
|
||||||
import ResourceService from '@joplin/lib/services/ResourceService';
|
import ResourceService from '@joplin/lib/services/ResourceService';
|
||||||
|
import ExternalEditWatcher from '@joplin/lib/services/ExternalEditWatcher';
|
||||||
|
|
||||||
const { FoldersScreenUtils } = require('@joplin/lib/folders-screen-utils.js');
|
const { FoldersScreenUtils } = require('@joplin/lib/folders-screen-utils.js');
|
||||||
const MasterKey = require('@joplin/lib/models/MasterKey');
|
const MasterKey = require('@joplin/lib/models/MasterKey');
|
||||||
@ -31,7 +32,6 @@ const { reg } = require('@joplin/lib/registry.js');
|
|||||||
const packageInfo = require('./packageInfo.js');
|
const packageInfo = require('./packageInfo.js');
|
||||||
const DecryptionWorker = require('@joplin/lib/services/DecryptionWorker');
|
const DecryptionWorker = require('@joplin/lib/services/DecryptionWorker');
|
||||||
const ClipperServer = require('@joplin/lib/ClipperServer');
|
const ClipperServer = require('@joplin/lib/ClipperServer');
|
||||||
const ExternalEditWatcher = require('@joplin/lib/services/ExternalEditWatcher');
|
|
||||||
const { webFrame } = require('electron');
|
const { webFrame } = require('electron');
|
||||||
const Menu = bridge().Menu;
|
const Menu = bridge().Menu;
|
||||||
const PluginManager = require('@joplin/lib/services/PluginManager');
|
const PluginManager = require('@joplin/lib/services/PluginManager');
|
||||||
@ -691,7 +691,7 @@ class Application extends BaseApplication {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ExternalEditWatcher.instance().setLogger(reg.logger());
|
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); });
|
ResourceEditWatcher.instance().initialize(reg.logger(), (action: any) => { this.store().dispatch(action); });
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService';
|
import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService';
|
||||||
import { _ } from '@joplin/lib/locale';
|
import { _ } from '@joplin/lib/locale';
|
||||||
import { stateUtils } from '@joplin/lib/reducer';
|
import { stateUtils } from '@joplin/lib/reducer';
|
||||||
|
import ExternalEditWatcher from '@joplin/lib/services/ExternalEditWatcher';
|
||||||
const Note = require('@joplin/lib/models/Note');
|
const Note = require('@joplin/lib/models/Note');
|
||||||
const ExternalEditWatcher = require('@joplin/lib/services/ExternalEditWatcher');
|
|
||||||
const bridge = require('electron').remote.require('./bridge').default;
|
const bridge = require('electron').remote.require('./bridge').default;
|
||||||
|
|
||||||
export const declaration: CommandDeclaration = {
|
export const declaration: CommandDeclaration = {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService';
|
import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService';
|
||||||
import { _ } from '@joplin/lib/locale';
|
import { _ } from '@joplin/lib/locale';
|
||||||
import { stateUtils } from '@joplin/lib/reducer';
|
import { stateUtils } from '@joplin/lib/reducer';
|
||||||
const ExternalEditWatcher = require('@joplin/lib/services/ExternalEditWatcher');
|
import ExternalEditWatcher from '@joplin/lib/services/ExternalEditWatcher';
|
||||||
|
|
||||||
export const declaration: CommandDeclaration = {
|
export const declaration: CommandDeclaration = {
|
||||||
name: 'stopExternalEditing',
|
name: 'stopExternalEditing',
|
||||||
|
@ -29,6 +29,7 @@ import markupLanguageUtils from '@joplin/lib/markupLanguageUtils';
|
|||||||
import usePrevious from '../hooks/usePrevious';
|
import usePrevious from '../hooks/usePrevious';
|
||||||
import Setting from '@joplin/lib/models/Setting';
|
import Setting from '@joplin/lib/models/Setting';
|
||||||
import stateToWhenClauseContext from '../../services/commands/stateToWhenClauseContext';
|
import stateToWhenClauseContext from '../../services/commands/stateToWhenClauseContext';
|
||||||
|
import ExternalEditWatcher from '@joplin/lib/services/ExternalEditWatcher';
|
||||||
|
|
||||||
const { themeStyle } = require('@joplin/lib/theme');
|
const { themeStyle } = require('@joplin/lib/theme');
|
||||||
const { substrWithEllipsis } = require('@joplin/lib/string-utils');
|
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 { reg } = require('@joplin/lib/registry.js');
|
||||||
const Note = require('@joplin/lib/models/Note.js');
|
const Note = require('@joplin/lib/models/Note.js');
|
||||||
const bridge = require('electron').remote.require('./bridge').default;
|
const bridge = require('electron').remote.require('./bridge').default;
|
||||||
const ExternalEditWatcher = require('@joplin/lib/services/ExternalEditWatcher');
|
|
||||||
const NoteRevisionViewer = require('../NoteRevisionViewer.min');
|
const NoteRevisionViewer = require('../NoteRevisionViewer.min');
|
||||||
|
|
||||||
const commands = [
|
const commands = [
|
||||||
|
@ -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 Note = require('../models/Note');
|
||||||
const Setting = require('../models/Setting').default;
|
|
||||||
const shim = require('../shim').default;
|
|
||||||
const EventEmitter = require('events');
|
const EventEmitter = require('events');
|
||||||
const { splitCommandString } = require('../string-utils');
|
const { splitCommandString } = require('../string-utils');
|
||||||
const { fileExtension, basename } = require('../path-utils');
|
|
||||||
const spawn = require('child_process').spawn;
|
const spawn = require('child_process').spawn;
|
||||||
const chokidar = require('chokidar');
|
const chokidar = require('chokidar');
|
||||||
const bridge = require('electron').remote.require('./bridge').default;
|
|
||||||
const time = require('../time').default;
|
|
||||||
const { ErrorNotFound } = require('./rest/utils/errors');
|
const { ErrorNotFound } = require('./rest/utils/errors');
|
||||||
|
|
||||||
class ExternalEditWatcher {
|
export default class ExternalEditWatcher {
|
||||||
constructor() {
|
|
||||||
this.logger_ = new Logger();
|
|
||||||
this.dispatch = () => {};
|
|
||||||
this.watcher_ = null;
|
|
||||||
this.eventEmitter_ = new EventEmitter();
|
|
||||||
this.skipNextChangeEvent_ = {};
|
|
||||||
this.chokidar_ = chokidar;
|
|
||||||
}
|
|
||||||
|
|
||||||
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_;
|
if (this.instance_) return this.instance_;
|
||||||
this.instance_ = new ExternalEditWatcher();
|
this.instance_ = new ExternalEditWatcher();
|
||||||
return this.instance_;
|
return this.instance_;
|
||||||
}
|
}
|
||||||
|
|
||||||
externalApi() {
|
public initialize(bridge: Function, dispatch: Function) {
|
||||||
const loadNote = async (noteId) => {
|
this.bridge_ = bridge;
|
||||||
|
this.dispatch = dispatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public externalApi() {
|
||||||
|
const loadNote = async (noteId: string) => {
|
||||||
const note = await Note.load(noteId);
|
const note = await Note.load(noteId);
|
||||||
if (!note) throw new ErrorNotFound(`No such note: ${noteId}`);
|
if (!note) throw new ErrorNotFound(`No such note: ${noteId}`);
|
||||||
return note;
|
return note;
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
openAndWatch: async ({ noteId }) => {
|
openAndWatch: async (args: any) => {
|
||||||
const note = await loadNote(noteId);
|
const note = await loadNote(args.noteId);
|
||||||
return this.openAndWatch(note);
|
return this.openAndWatch(note);
|
||||||
},
|
},
|
||||||
stopWatching: async ({ noteId }) => {
|
stopWatching: async (args: any) => {
|
||||||
return this.stopWatching(noteId);
|
return this.stopWatching(args.noteId);
|
||||||
},
|
},
|
||||||
noteIsWatched: async ({ noteId }) => {
|
noteIsWatched: async (args: any) => {
|
||||||
const note = await loadNote(noteId);
|
const note = await loadNote(args.noteId);
|
||||||
return this.noteIsWatched(note);
|
return this.noteIsWatched(note);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -53,15 +61,15 @@ class ExternalEditWatcher {
|
|||||||
return Setting.value('profileDir');
|
return Setting.value('profileDir');
|
||||||
}
|
}
|
||||||
|
|
||||||
on(eventName, callback) {
|
on(eventName: string, callback: Function) {
|
||||||
return this.eventEmitter_.on(eventName, callback);
|
return this.eventEmitter_.on(eventName, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
off(eventName, callback) {
|
off(eventName: string, callback: Function) {
|
||||||
return this.eventEmitter_.removeListener(eventName, callback);
|
return this.eventEmitter_.removeListener(eventName, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
setLogger(l) {
|
setLogger(l: Logger) {
|
||||||
this.logger_ = l;
|
this.logger_ = l;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,12 +77,12 @@ class ExternalEditWatcher {
|
|||||||
return this.logger_;
|
return this.logger_;
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(fileToWatch) {
|
watch(fileToWatch: string) {
|
||||||
if (!this.chokidar_) return;
|
if (!this.chokidar_) return;
|
||||||
|
|
||||||
if (!this.watcher_) {
|
if (!this.watcher_) {
|
||||||
this.watcher_ = this.chokidar_.watch(fileToWatch);
|
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,
|
// 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.
|
// 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"
|
// 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)
|
// Hack to support external watcher on some linux applications (gedit, gvim, etc)
|
||||||
// taken from https://github.com/paulmillr/chokidar/issues/591
|
// 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}`);
|
/* was_debug */ this.logger().info(`ExternalEditWatcher: Raw event: ${event}: ${watchedPath}`);
|
||||||
if (event === 'rename') {
|
if (event === 'rename') {
|
||||||
this.watcher_.unwatch(watchedPath);
|
this.watcher_.unwatch(watchedPath);
|
||||||
@ -154,12 +163,12 @@ class ExternalEditWatcher {
|
|||||||
return this.watcher_;
|
return this.watcher_;
|
||||||
}
|
}
|
||||||
|
|
||||||
noteIdToFilePath_(noteId) {
|
noteIdToFilePath_(noteId: string) {
|
||||||
return `${this.tempDir()}/edit-${noteId}.md`;
|
return `${this.tempDir()}/edit-${noteId}.md`;
|
||||||
}
|
}
|
||||||
|
|
||||||
noteFilePathToId_(path) {
|
noteFilePathToId_(path: string) {
|
||||||
let id = path.split('/');
|
let id: any = toSystemSlashes(path, 'linux').split('/');
|
||||||
if (!id.length) throw new Error(`Invalid path: ${path}`);
|
if (!id.length) throw new Error(`Invalid path: ${path}`);
|
||||||
id = id[id.length - 1];
|
id = id[id.length - 1];
|
||||||
id = id.split('.');
|
id = id.split('.');
|
||||||
@ -186,7 +195,7 @@ class ExternalEditWatcher {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
noteIsWatched(note) {
|
noteIsWatched(note: NoteEntity) {
|
||||||
if (!this.watcher_) return false;
|
if (!this.watcher_) return false;
|
||||||
|
|
||||||
const noteFilename = basename(this.noteIdToFilePath_(note.id));
|
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) => {
|
return new Promise((resolve, reject) => {
|
||||||
// App bundles need to be opened using the `open` command.
|
// App bundles need to be opened using the `open` command.
|
||||||
// Additional args can be specified after --args, and the
|
// Additional args can be specified after --args, and the
|
||||||
@ -238,7 +247,7 @@ class ExternalEditWatcher {
|
|||||||
path = 'open';
|
path = 'open';
|
||||||
}
|
}
|
||||||
|
|
||||||
const wrapError = error => {
|
const wrapError = (error: any) => {
|
||||||
if (!error) return error;
|
if (!error) return error;
|
||||||
const msg = error.message ? [error.message] : [];
|
const msg = error.message ? [error.message] : [];
|
||||||
msg.push(`Command was: "${path}" ${args.join(' ')}`);
|
msg.push(`Command was: "${path}" ${args.join(' ')}`);
|
||||||
@ -257,7 +266,7 @@ class ExternalEditWatcher {
|
|||||||
}
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
subProcess.on('error', error => {
|
subProcess.on('error', (error: any) => {
|
||||||
shim.clearInterval(iid);
|
shim.clearInterval(iid);
|
||||||
reject(wrapError(error));
|
reject(wrapError(error));
|
||||||
});
|
});
|
||||||
@ -267,18 +276,19 @@ class ExternalEditWatcher {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async openAndWatch(note) {
|
async openAndWatch(note: NoteEntity) {
|
||||||
if (!note || !note.id) {
|
if (!note || !note.id) {
|
||||||
this.logger().warn('ExternalEditWatcher: Cannot open note: ', note);
|
this.logger().warn('ExternalEditWatcher: Cannot open note: ', note);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const filePath = await this.writeNoteToFile_(note);
|
const filePath = await this.writeNoteToFile_(note);
|
||||||
|
if (!filePath) return;
|
||||||
this.watch(filePath);
|
this.watch(filePath);
|
||||||
|
|
||||||
const cmd = this.textEditorCommand();
|
const cmd = this.textEditorCommand();
|
||||||
if (!cmd) {
|
if (!cmd) {
|
||||||
bridge().openExternal(`file://${filePath}`);
|
this.bridge_().openExternal(`file://${filePath}`);
|
||||||
} else {
|
} else {
|
||||||
cmd.args.push(filePath);
|
cmd.args.push(filePath);
|
||||||
await this.spawnCommand(cmd.path, cmd.args, { detached: true });
|
await this.spawnCommand(cmd.path, cmd.args, { detached: true });
|
||||||
@ -292,7 +302,7 @@ class ExternalEditWatcher {
|
|||||||
this.logger().info(`ExternalEditWatcher: Started watching ${filePath}`);
|
this.logger().info(`ExternalEditWatcher: Started watching ${filePath}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async stopWatching(noteId) {
|
async stopWatching(noteId: string) {
|
||||||
if (!noteId) return;
|
if (!noteId) return;
|
||||||
|
|
||||||
const filePath = this.noteIdToFilePath_(noteId);
|
const filePath = this.noteIdToFilePath_(noteId);
|
||||||
@ -319,7 +329,7 @@ class ExternalEditWatcher {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateNoteFile(note) {
|
async updateNoteFile(note: NoteEntity) {
|
||||||
if (!this.noteIsWatched(note)) return;
|
if (!this.noteIsWatched(note)) return;
|
||||||
|
|
||||||
if (!note || !note.id) {
|
if (!note || !note.id) {
|
||||||
@ -336,10 +346,10 @@ class ExternalEditWatcher {
|
|||||||
this.writeNoteToFile_(note);
|
this.writeNoteToFile_(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
async writeNoteToFile_(note) {
|
async writeNoteToFile_(note: NoteEntity) {
|
||||||
if (!note || !note.id) {
|
if (!note || !note.id) {
|
||||||
this.logger().warn('ExternalEditWatcher: Cannot update note file: ', note);
|
this.logger().warn('ExternalEditWatcher: Cannot update note file: ', note);
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const filePath = this.noteIdToFilePath_(note.id);
|
const filePath = this.noteIdToFilePath_(note.id);
|
||||||
@ -348,5 +358,3 @@ class ExternalEditWatcher {
|
|||||||
return filePath;
|
return filePath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = ExternalEditWatcher;
|
|
@ -1,5 +1,5 @@
|
|||||||
import ResourceEditWatcher from '../ResourceEditWatcher/index';
|
import ResourceEditWatcher from '../ResourceEditWatcher/index';
|
||||||
const ExternalEditWatcher = require('../ExternalEditWatcher');
|
import ExternalEditWatcher from '../ExternalEditWatcher';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
const MdToHtml = require('./MdToHtml').default;
|
import MdToHtml from './MdToHtml';
|
||||||
const HtmlToHtml = require('./HtmlToHtml');
|
const HtmlToHtml = require('./HtmlToHtml');
|
||||||
const htmlUtils = require('./htmlUtils');
|
const htmlUtils = require('./htmlUtils');
|
||||||
const MarkdownIt = require('markdown-it');
|
const MarkdownIt = require('markdown-it');
|
||||||
|
Loading…
Reference in New Issue
Block a user