diff --git a/.gitmodules b/.gitmodules index efa0918..041c91f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "native/inotify_simple"] - path = native/inotify_simple - url = https://github.com/chrisjbillington/inotify_simple +[submodule "native/watchgod"] + path = native/watchgod + url = https://github.com/samuelcolvin/watchgod diff --git a/Makefile b/Makefile index b44f9e4..0edebb3 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,20 @@ +UNAME_S := $(shell uname -s) + ifneq ($(USER),1) PREFIX ?= /usr/local - MOZILLA_PREFIX ?= /usr - MOZILLA_NATIVE ?= $(MOZILLA_PREFIX)/lib64/mozilla/native-messaging-hosts + ifeq ($(UNAME_S),Darwin) + MOZILLA_NATIVE ?= /Library/Application\ Support/Mozilla/NativeMessagingHosts + else + MOZILLA_PREFIX ?= /usr + MOZILLA_NATIVE ?= $(MOZILLA_PREFIX)/lib64/mozilla/native-messaging-hosts + endif else PREFIX ?= $(HOME)/.local - MOZILLA_NATIVE ?= $(HOME)/.mozilla/native-messaging-hosts + ifeq ($(UNAME_S),Darwin) + MOZILLA_NATIVE ?= $(HOME)/Library/Application\ Support/Mozilla/NativeMessagingHosts + else + MOZILLA_NATIVE ?= $(HOME)/.mozilla/native-messaging-hosts + endif endif LIBEXEC ?= $(PREFIX)/libexec @@ -28,7 +38,7 @@ native-install: native/textern.json mkdir -p $(DESTDIR)$(MOZILLA_NATIVE) cp -f native/textern.json $(DESTDIR)$(MOZILLA_NATIVE) mkdir -p $(DESTDIR)$(LIBEXEC)/textern - cp -rf native/textern.py native/inotify_simple $(DESTDIR)$(LIBEXEC)/textern + cp -rf native/textern.py native/watchgod $(DESTDIR)$(LIBEXEC)/textern .PHONY: native-uninstall native-uninstall: diff --git a/README.md b/README.md index f3bf913..00e7fc4 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ The add-on is divided into two parts: - the native application, which handles text editor launching and monitoring. -The native application currently only supports Linux with +The native application currently supports Linux or macOS with Python 3.5. Patches to add support for other platforms are welcome! @@ -53,6 +53,18 @@ the native app for the current user, run: $ make native-install USER=1 ``` + +### Note for macOS + +If you installed Python via [homebrew](https://brew.sh/), note +that `/usr/local/bin` is *not* on the PATH by default, so +`textern.py` cannot find it. You either need to adjust your +PATH globally, the exact procedure for which seems to [change with +each macOS version](https://stackoverflow.com/a/32902449/1885340), +or edit the first line of `native/textern.py` to read +`#!/usr/local/bin/python3` and repeating the `make native-install` call. + + ## Usage Once both the WebExtension and the native application are @@ -118,6 +130,14 @@ set your editor in the preferences to something like and make sure, that `gnome-terminal-wrapper` is in your `PATH`. +### Notes on the macOS Terminal.app + +As it operates similarly to gnome-terminal (described above), +it has the same issue with exiting prematurely. An appropriate +wrapper script can be found +[here](https://gist.github.com/wosc/680f87845d0fd9d8b4a9e2f8c42f98a2) + + ### GUI editors Non-terminal-based editors can also suffer from the same diff --git a/native/inotify_simple b/native/inotify_simple deleted file mode 160000 index 979648c..0000000 --- a/native/inotify_simple +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 979648cefa83dda61611ed9d8fb1fe4b644950be diff --git a/native/textern.py b/native/textern.py index 85d54f7..c5eb4dc 100755 --- a/native/textern.py +++ b/native/textern.py @@ -12,14 +12,19 @@ import asyncio import tempfile import urllib.parse -from inotify_simple.inotify_simple import INotify, flags +from watchgod.watchgod import awatch + +if sys.platform == 'darwin': + TMPDIR = os.environ['TMPDIR'] +else: + TMPDIR = os.environ['XDG_RUNTIME_DIR'] class TmpManager(): def __init__(self): try: - tmpdir_parent = os.path.join(os.environ['XDG_RUNTIME_DIR'], 'textern') + tmpdir_parent = os.path.join(TMPDIR, 'textern') os.makedirs(tmpdir_parent) except: tmpdir_parent = None @@ -63,11 +68,10 @@ def has(self, relfn): def main(): - with INotify() as ino, TmpManager() as tmp_mgr: - ino.add_watch(tmp_mgr.tmpdir, flags.CLOSE_WRITE) + with TmpManager() as tmp_mgr: loop = asyncio.get_event_loop() + loop.create_task(watch_directory(tmp_mgr)) loop.add_reader(sys.stdin.buffer, handle_stdin, tmp_mgr) - loop.add_reader(ino.fd, handle_inotify_event, ino, tmp_mgr) loop.run_forever() loop.close() @@ -161,14 +165,16 @@ async def handle_message_new_text(tmp_mgr, msg): } -def handle_inotify_event(ino, tmp_mgr): - for event in ino.read(): - # this check is relevant in the case where we're handling the inotify - # event caused by tmp_mgr.new(), but then an exception occurred in - # handle_message() which caused the tmpfile to already be deleted - if tmp_mgr.has(event.name): - text, id = tmp_mgr.get(event.name) - send_text_update(id, text) +async def watch_directory(tmp_mgr): + async for changes in awatch(tmp_mgr.tmpdir): + for typ, path in changes: + path = os.path.basename(path) + # this check is relevant in the case where we're handling the + # event caused by tmp_mgr.new(), but then an exception occurred in + # handle_message() which caused the tmpfile to already be deleted + if tmp_mgr.has(path): + text, id = tmp_mgr.get(path) + send_text_update(id, text) def send_text_update(id, text): diff --git a/native/watchgod b/native/watchgod new file mode 160000 index 0000000..15c72be --- /dev/null +++ b/native/watchgod @@ -0,0 +1 @@ +Subproject commit 15c72be2fca0afe2a081e4076f9353e5cdd8e42f