tvl-depot/tools/emacs-pkgs/passively/passively.el

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

122 lines
4 KiB
EmacsLisp
Raw Normal View History

;;; passively.el --- Passively learn new information -*- lexical-binding: t; -*-
;;
;; SPDX-License-Identifier: MIT
;; Copyright (C) 2020 The TVL Contributors
;;
;; Author: Vincent Ambo <tazjin@tvl.su>
;; Version: 1.0
;; Package-Requires: (ht seq)
;; URL: https://code.tvl.fyi/about/tools/emacs-pkgs/passively/
;;
;; This file is not part of GNU Emacs.
(require 'ht)
(require 'seq)
;; Customisation options
(defgroup passively nil
"Customisation options for passively"
:group 'applications)
(defcustom passively-learn-terms nil
"Terms that passively should randomly display to the user. The
format of this variable is a hash table with a string key that
uniquely identifies the term, and a string value that is
displayed to the user.
For example, a possible value could be:
(ht (\"забыть\" \"забыть - to forget\")
(\"действительно\" \"действительно - indeed, really\")))
"
;; TODO(tazjin): No hash-table type in customization.el?
:type '(sexp)
:group 'passively)
(defcustom passively-store-state (format "%spassively.el" user-emacs-directory)
"File in which passively should store its state (e.g. known terms)"
:type '(file)
:group 'passively)
(defcustom passively-show-after-idle-for 4
"Number of seconds after Emacs goes idle that passively should
wait before displaying a term."
:type '(integer)
:group 'passively)
;; Implementation of state persistence
(defvar passively-last-displayed nil
"Key of the last displayed passively term.")
(defvar passively--known-terms (make-hash-table)
"Set of terms that are already known.")
(defun passively--persist-known-terms ()
"Persist the set of known passively terms to disk."
(with-temp-file passively-store-state
(insert (prin1-to-string (ht-keys passively--known-terms)))))
(defun passively--load-known-terms ()
"Load the set of known passively terms from disk."
(with-temp-buffer
(insert-file-contents passively-store-state)
(let ((keys (read (current-buffer))))
(setq passively--known-terms (make-hash-table))
(seq-do
(lambda (key) (ht-set passively--known-terms key t))
keys)))
(message "passively: loaded %d known words"
(seq-length (ht-keys passively--known-terms))))
(defun passively-mark-last-as-known ()
"Mark the last term that passively displayed as known. It will
not be displayed again."
(interactive)
(ht-set passively--known-terms passively-last-displayed t)
(passively--persist-known-terms)
(message "passively: Marked '%s' as known" passively-last-displayed))
;; Implementation of main display logic
(defvar passively--display-timer nil
"idle-timer used for displaying terms by passively")
(defun passively--random-term (timeout)
;; This is stupid, calculate set intersections instead.
(if (< 1000 timeout)
(error "It seems you already know all the terms?")
(seq-random-elt (ht-keys passively-learn-terms))))
(defun passively--display-random-term ()
(let* ((timeout 1)
(term (passively--random-term timeout)))
(while (ht-contains? passively--known-terms term)
(setq timeout (+ 1 timeout))
(setq term (passively--random-term timeout)))
(setq passively-last-displayed term)
(message (ht-get passively-learn-terms term))))
(defun passively-enable ()
"Enable automatic display of terms via passively."
(interactive)
(if passively--display-timer
(error "passively: Already running!")
(passively--load-known-terms)
(setq passively--display-timer
(run-with-idle-timer passively-show-after-idle-for t
#'passively--display-random-term))
(message "passively: Now running after %s seconds of idle time"
passively-show-after-idle-for)))
(defun passively-disable ()
"Turn off automatic display of terms via passively."
(interactive)
(unless passively--display-timer
(error "passively: Not running!"))
(cancel-timer passively--display-timer)
(setq passively--display-timer nil)
(message "passively: Now disabled"))
(provide 'passively)