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.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
3
.gitignore
vendored
@ -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
|
||||
|
@ -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); });
|
||||
|
||||
|
@ -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 = {
|
||||
|
@ -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',
|
||||
|
@ -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 = [
|
||||
|
@ -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;
|
@ -1,5 +1,5 @@
|
||||
import ResourceEditWatcher from '../ResourceEditWatcher/index';
|
||||
const ExternalEditWatcher = require('../ExternalEditWatcher');
|
||||
import ExternalEditWatcher from '../ExternalEditWatcher';
|
||||
|
||||
export default {
|
||||
|
||||
|
@ -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');
|
||||
|
Loading…
Reference in New Issue
Block a user