branch: externals/greader
commit 8a35090cffae8fea2f29613c6f2c572ac7c77d7f
Author: Michelangelo Rodriguez <michelangelo.rodrig...@gmail.com>
Commit: Michelangelo Rodriguez <michelangelo.rodrig...@gmail.com>

    greader-dict.el: package headers added.
---
 greader-dict.el | 275 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 274 insertions(+), 1 deletion(-)

diff --git a/greader-dict.el b/greader-dict.el
index 72a7b624e9..8968a4be24 100644
--- a/greader-dict.el
+++ b/greader-dict.el
@@ -1,6 +1,36 @@
 ;; greader-dict.el - dictionary module for greader. -*- lexical-binding: t; -*-
+;; 
+;; Filename: greader-dict.el
+;; Description: 
+;; Author: Michelangelo Rodriguez
+;; Maintainer: 
+;; Created: Lun Gen  8 09:52:58 2024 (+0100)
+;; Version: 
+;; Package-Requires: ()
+;; Last-Updated: 
+;;           By: 
+;;     Update #: 0
+;; URL: 
+;; Doc URL: 
+;; Keywords: 
+;; Compatibility: 
+;; 
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; 
+;;; Commentary: 
+;; dictionary module for greader.
+;; This module gives greader the ability to define different wais Of
+;; pronounce a given sequence of characters.
 
-;; Copyright (C) 2023  Free Software Foundation, Inc.
+;; 
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; 
+;;; Change Log:
+;; 
+;; 
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; 
+;; Copyright (C) 2023, 2024  Free Software Foundation, Inc.
 
 ;; This program is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
@@ -14,3 +44,246 @@
 
 ;; You should have received a copy of the GNU General Public License
 ;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; 
+;;; Code:
+(require 'greader)
+(defvar greader-dictionary (make-hash-table :test 'equal))
+(defvar greader-dict-match-indicator "%\*"
+  "Regexp that will be used for match delimiter.")
+
+;; The following two functions deal, respectively, with
+;; replace a dictionary item with the value specified in
+;; `greader-dictionari' and its possible variants.
+;; The `greader-dict-substitute-match' function takes care of substitution
+;; an item even within a word, a sort of partial substitution.
+;; The `greader-dict-substitute-word' function takes care of that instead
+;; replace a dictionary item only if the sequence matches
+;; replace is surrounded by one or more blank class characters.
+;; This will allow the user to specify whether a given rule
+;; pronunciation in the dictionary should be applied more literally,
+;; (for example, a pronunciation rule can be defined such that if a
+;; word contains the sequence "ez" it is replaced with the
+;; sequence "es", for which, for example, "Rodriguez" would be replaced
+;; with "Rodrigues").
+(defun greader-dict-substitute-match (match)
+  "Replace MATCH with the matching value in `greader-dictionary."
+  (let ((normalized-match (string-remove-suffix
+                          greader-dict-match-indicator match)))
+    (save-excursion
+      (goto-char (point-min))
+      (while (search-forward normalized-match nil t)
+       (replace-match (gethash match greader-dictionary))))))
+
+(defun greader-dict-substitute-word (match)
+  "substitute match only if it constitutes an entire word."
+  (save-excursion
+    (goto-char (point-min))
+    (let ((word (concat match "\\W")))
+      (while (re-search-forward word nil t)
+       (setq word (match-string 0))
+       (let ((replacement (concat (gethash match
+                                           greader-dictionary)
+                                  (char-to-string (aref word (1- (length 
word)))))))
+         (replace-match replacement))))))
+
+;; This function adds to the `greader-dictionary' variable the
+;; key/value pair that you pass as arguments.
+(defcustom greader-dict-save-after-time 30
+  "Amount of idleness to wait before saving dictionary data.
+A value of 0 indicates saving immediately."
+  :type 'number)
+(defvar greader-dict--timer nil)
+(defun greader-dict-add (word replacement)
+  "Add the WORD REPLACEMent pair to `greader-dictionary'.
+If you want to add a partial replacement, you should
+add `\*'to the end of WORD string parameter."
+  (puthash (downcase word) replacement greader-dictionary)
+  (cond
+   ((> greader-dict-save-after-time 0)
+    (when (timerp greader-dict--timer)
+      (cancel-timer greader-dict--timer))
+    (run-with-idle-timer greader-dict-save-after-time nil 
#'greader-dict-write-file))
+   ((= greader-dict-save-after-time 0)
+    (greader-dict-write-file))
+   (t
+    nil)))
+
+;; This function removes the association indicated by the key argument.
+(defun greader-dict-remove (key)
+  "Remove the association specified by KEY from the variable
+`greader-dictionary'."
+  (remhash key greader-dictionary))
+
+(defun greader-dict-item-type (key)
+  "Return the type of KEY.
+Possible return values are:
+`word' for a wole word,
+`match' for partial matches.
+There may be more in the future.
+Return nil if KEY is not present in `greader-dictionary'."
+  (cond
+   ((not key)
+    nil)
+   ((not (string-suffix-p greader-dict-match-indicator key))
+    'word)
+   ((string-suffix-p greader-dict-match-indicator key)
+    'match)
+   (t nil)))
+
+(defun greader-dict--get-key-from-word (word)
+  "Return key related to WORD, nil otherwise."
+  (let ((key nil))
+    (maphash (lambda (k v)
+              (when (string-search (string-remove-suffix
+                                    greader-dict-match-indicator k) word)
+                (setq key k))) greader-dictionary)
+    key))
+
+;; This function checks that, in the string you pass to it, there are
+;; effectively words to be replaced. If so, use apis
+;; previously defined to adequately replace the words that
+;; could need it.
+;; This is the function to add to
+;; `greader-after-get-sentence-functions'.
+(defun greader-dict-check-and-replace (text)
+  "Return the TEXT passed to it, eventually modified according to
+`greader-dictionary' and variants."
+  (with-temp-buffer
+    (insert text)
+    (goto-char (point-min))
+    (re-search-forward "\\w" nil t)
+    (while (not (eobp))
+      (let*
+         ((key (greader-dict--get-key-from-word (downcase
+                                                 (thing-at-point 'word))))
+          (modified-word (concat (downcase (thing-at-point 'word)) 
greader-dict-match-indicator)))
+       (cond
+        ((equal (greader-dict-item-type key) 'word)
+         (greader-dict-substitute-word (string-remove-suffix
+                                        greader-dict-match-indicator key)))
+        ((equal (greader-dict-item-type key) 'match)
+         (greader-dict-substitute-match key))
+        ((not (greader-dict-item-type key))
+         nil)))
+      (forward-word))
+    (buffer-string)))
+
+;; This function saves the contents of the hash table.
+(defcustom greader-dict-directory (concat user-emacs-directory
+                                         ".greader-dict/"
+                                         (greader-get-language) "/")
+  "The directory containing greader-dict files."
+  :type 'directory)
+(defcustom greader-dict-filename "greader-dict.global"
+  "File name where dictionary definitions are stored."
+  :type 'string)
+
+(defun greader-dict-write-file ()
+  "Save greader-dictionary stored in `greader-dict-filename'."
+  (unless (file-exists-p greader-dict-directory)
+    (make-directory greader-dict-directory t))
+  (with-temp-buffer
+    (maphash
+     (lambda (k v)
+       (insert (concat k "=" v "\n"))) greader-dictionary)
+    (write-file (concat greader-dict-directory
+                       greader-dict-filename))))
+
+(defun greader-dict-read-from-dict-file ()
+  "populate `greader-dictionary' with the contents of
+`greader-dict-filename'."
+  (when (file-exists-p (concat greader-dict-directory
+                              greader-dict-filename))
+    (with-temp-buffer
+      (insert-file-contents (concat greader-dict-directory
+                                   greader-dict-filename))
+      (when-let ((lines (string-lines (buffer-string))))
+       (dolist (line lines)
+         (setq line (split-string line "="))
+         (greader-dict-add (car line) (car (cdr line))))))))
+
+;; Command for saving interactively dictionary data.
+(defun greader-dict-save ()
+  "Save dictionary data.
+You should use this command when you want to save your dictionary and
+`greader-dict-save-after-time' is set to a negative number.
+Otherwise, data saving is done automatically when you add a definition
+to the dictionary."
+  (interactive)
+  (greader-dict-write-file))
+
+;; This command Adds a definition to `greader-dictionary'.
+;; If the region is active and it does not constitute more than one word,
+;; the command will propose the selected word as the original word to
+;; substitute.
+;; The selected word will be added to `greader-dictionary' as
+;; "match", then the definition thus obtained can be applied to
+;; any character sequence that includes it.
+;; However, if the region is not active, this function will try to
+;; determine the word to add through the function
+;; `thing-at-point'. In case this function returns a word,
+;; it will be used to propose it as the original word to be replaced.
+;; In this last case, the word will be added to
+;; `greader-dictionary' as "word", so it must constitute itself
+;; a word to be replaced.
+(defun greader-dict-add-entry ()
+  (interactive)
+  (let (key value)
+    (cond
+     ((region-active-p)
+      (when (= (count-words(region-beginning) (region-end)) 1)
+       (setq key (concat (read-string "Original word to substitute:" nil nil
+                              (thing-at-point 'word)) 
greader-dict-match-indicator))
+       (setq value (read-string (concat "substitute match " key
+                                        "with:")))
+       (greader-dict-add key value)))
+     (t
+      (when-let ((default-word (thing-at-point 'word)))
+       (setq key (read-string "Original word to substitute:" nil nil
+                              default-word))
+       (setq value (read-string (concat "substitute word " key
+                                        "with:")))
+       (greader-dict-add key value))))))
+
+;; greader-dict-mode.
+(defvar-keymap greader-dict-mode-map
+  :doc "keymap for `greader-dict-mode'."
+  "C-r d a" #'greader-dict-add-entry
+  "C-r d s" #'greader-dict-save)
+(defun greader-dict--replace-wrapper (text)
+  "Function to add to `greader-after-get-sentence-functions'.
+It simply calls `greader-dict-check-and-replace' with TEXT as its
+argument, only if `greader-dict-mode' is enabled."
+  (when greader-dict-mode
+    (greader-dict-check-and-replace text)))
+;;;###autoload
+(define-minor-mode greader-dict-mode
+  "Dictionary module for greader.
+With this mode it is possible to instruct greader to pronounce in an 
+alternative way the words that the tts mispronounces in a given language.
+There are two types of definitions understood by greader-dict-mode:
+\"word definitions\" are those that must be surrounded by
+Non-constituent word characters;
+\"Match definitions\" are those that can be replaced regardless of
+surrounding characters.
+The definition type is determined when you add a new definition:
+If you use the region to mark a word, you can select a partial word or
+the entire word, and greader-dict-mode will understand that you want
+to add a match definition.
+If instead you add simply the word under the point, it will be added
+as a word definition."
+  :lighter " gr-dictionary"
+  (cond
+   (greader-dict-mode
+    (when (hash-table-empty-p greader-dictionary)
+      (greader-dict-read-from-dict-file))
+    (add-hook 'greader-after-get-sentence-functions
+             #'greader-dict--replace-wrapper))
+   (t
+    (remove-hook 'greader-after-get-sentence-functions
+                #'greader-dict--replace-wrapper t))))
+
+(provide 'greader-dict)
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; greader-dict.el ends here

Reply via email to