diff --git a/.eslintignore b/.eslintignore index dc50e7bd9..6a78e310f 100644 --- a/.eslintignore +++ b/.eslintignore @@ -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 diff --git a/.gitignore b/.gitignore index 320b303cb..2ef14d445 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/packages/app-desktop/app.ts b/packages/app-desktop/app.ts index 804cee249..83094e533 100644 --- a/packages/app-desktop/app.ts +++ b/packages/app-desktop/app.ts @@ -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); }); diff --git a/packages/app-desktop/commands/startExternalEditing.ts b/packages/app-desktop/commands/startExternalEditing.ts index c489cb9f5..19f63c945 100644 --- a/packages/app-desktop/commands/startExternalEditing.ts +++ b/packages/app-desktop/commands/startExternalEditing.ts @@ -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 = { diff --git a/packages/app-desktop/commands/stopExternalEditing.ts b/packages/app-desktop/commands/stopExternalEditing.ts index d27eb1f10..093df60c9 100644 --- a/packages/app-desktop/commands/stopExternalEditing.ts +++ b/packages/app-desktop/commands/stopExternalEditing.ts @@ -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', diff --git a/packages/app-desktop/gui/NoteEditor/NoteEditor.tsx b/packages/app-desktop/gui/NoteEditor/NoteEditor.tsx index df08ff9a1..544655c1b 100644 --- a/packages/app-desktop/gui/NoteEditor/NoteEditor.tsx +++ b/packages/app-desktop/gui/NoteEditor/NoteEditor.tsx @@ -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 = [ diff --git a/packages/lib/services/ExternalEditWatcher.js b/packages/lib/services/ExternalEditWatcher.ts similarity index 82% rename from packages/lib/services/ExternalEditWatcher.js rename to packages/lib/services/ExternalEditWatcher.ts index 2361382ff..65dd08351 100644 --- a/packages/lib/services/ExternalEditWatcher.js +++ b/packages/lib/services/ExternalEditWatcher.ts @@ -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; diff --git a/packages/lib/services/rest/actionApi.desktop.ts b/packages/lib/services/rest/actionApi.desktop.ts index d5675d976..9f1ae6d1f 100644 --- a/packages/lib/services/rest/actionApi.desktop.ts +++ b/packages/lib/services/rest/actionApi.desktop.ts @@ -1,5 +1,5 @@ import ResourceEditWatcher from '../ResourceEditWatcher/index'; -const ExternalEditWatcher = require('../ExternalEditWatcher'); +import ExternalEditWatcher from '../ExternalEditWatcher'; export default { diff --git a/packages/renderer/MarkupToHtml.ts b/packages/renderer/MarkupToHtml.ts index b7993a270..a18bb8e65 100644 --- a/packages/renderer/MarkupToHtml.ts +++ b/packages/renderer/MarkupToHtml.ts @@ -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');