122 lines
4 KiB
EmacsLisp
122 lines
4 KiB
EmacsLisp
|
;;; 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)
|