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