From 2d645958c3afa5389800c6dc9d21117298167aaf Mon Sep 17 00:00:00 2001 From: SqrtMinusOne Date: Sun, 14 Jan 2024 00:13:08 +0300 Subject: [PATCH] reverso: conjugation UI works --- reverso.el | 159 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 136 insertions(+), 23 deletions(-) diff --git a/reverso.el b/reverso.el index 0230e03..8d2e971 100644 --- a/reverso.el +++ b/reverso.el @@ -676,32 +676,39 @@ SOURCE-TEXT is the text sent for checking. DATA is the JSON reply." (:corrections . ,corrections)))) (defun reverso--empty-to-nil (value) + "If VALUE is nil or empty, return nil, otherwise return VALUE." (if (or (null value) (string-empty-p value)) nil value)) (defun reverso--get-conjugation-parse-alternate (data) + "Parse alternate verb forms in Reverso conjugation page. + +DATA is the DOM tree of the page." (when-let ((alts (thread-first data (dom-by-id "ch_divConjugatorHeader") (dom-by-class "alternate-versions")))) - `((model . ,(thread-first alts - (dom-by-id (rx bos "ch_lblModel" eos)) - ;; (dom-by-tag 'a) - (dom-texts) - (string-trim) - (reverso--empty-to-nil))) - (auxiliary . ,(thread-first alts - (dom-by-id (rx bos "ch_lblAuxiliary" eos)) - (dom-texts) - (string-trim) - (reverso--empty-to-nil))) - (other-forms . ,(thread-first alts - (dom-by-id (rx bos "ch_lblAutreForm" eos)) - (dom-texts) - (string-trim) - (reverso--empty-to-nil)))))) + `((:model . ,(thread-first alts + (dom-by-id (rx bos "ch_lblModel" eos)) + ;; (dom-by-tag 'a) + (dom-texts) + (string-trim) + (reverso--empty-to-nil))) + (:auxiliary . ,(thread-first alts + (dom-by-id (rx bos "ch_lblAuxiliary" eos)) + (dom-texts) + (string-trim) + (reverso--empty-to-nil))) + (:other-forms . ,(thread-first alts + (dom-by-id (rx bos "ch_lblAutreForm" eos)) + (dom-texts) + (string-trim) + (reverso--empty-to-nil)))))) (defun reverso--get-conjugation-parse-boxes (data) + "Parse verb conjugation boxes in Reverso conjugation page. + +DATA is the DOM tree of the page." (when-let ((boxes (thread-first data (dom-by-class "result-block-api") (car) @@ -741,11 +748,15 @@ SOURCE-TEXT is the text sent for checking. DATA is the JSON reply." (car group) (mapcar #'car (cdr group)))) (seq-group-by #'cdr listing)))) - `((title . ,title) - (listing . ,listing-by-v)))) + `((:title . ,title) + (:listing . ,listing-by-v)))) boxes))) (defun reverso--get-conjugation-parse (data) + "Parse Reverso conjugation page. + +DATA is the HTML string of the page. See `reverso--get-conjugation' +for the return value." (let* ((html (with-temp-buffer (insert data) (libxml-parse-html-region (point-min) (point-max)))) @@ -761,12 +772,30 @@ SOURCE-TEXT is the text sent for checking. DATA is the JSON reply." (reverso--empty-to-nil))) (alternate (reverso--get-conjugation-parse-alternate html)) (boxes (reverso--get-conjugation-parse-boxes html))) - `((word . ,word) - (translit-word . ,translit-word) - (alternate . ,alternate) - (boxes . ,boxes)))) + `((:word . ,word) + (:translit-word . ,translit-word) + (:alternate . ,alternate) + (:boxes . ,boxes)))) (defun reverso--get-conjugation (word language cb) + "Get conjugation of verb WORD in LANGUAGE. + +LANGUAGE is a symbol. See `reverso--languages' for supported +languages. + +CB is a callback function that takes the response as its argument. +The response is an alist with the following keys: +- `:word': the word itself +- `:translit-word': the transliteration of the word, if any +- `:alternate' - alternate forms of the verb. + - `:model' - a model verb (a verb that cojugates the same way + - `:auxiliary' - auxiliary verbs (verbs that are used to form + compound tenses) + - `:other-forms' - other forms of the verb +- `:boxes' - a list of conjugation boxes + - `:title' - the title of the box + - `:listing' - an alist with variant number as the key and a list of + conjugated forms as the value. There's usually only one variant." (unless (thread-last reverso--languages (alist-get 'conjugation) (alist-get language)) @@ -1054,6 +1083,47 @@ CORR is one element of the `:corrections' list, as defined in (insert "\n")) (insert "\n"))) +(defun reverso--conjugation-render (input data) + "Render conjugation results. + +DATA is a form as described in `reverso--get-conjugation'." + (setq-local reverso--input input) + (insert (propertize "Word: " 'face 'reverso-heading-face) + (alist-get :word data) + "\n\n") + (let ((alt (alist-get :alternate data))) + (when (or (alist-get :model alt) + (alist-get :auxiliary alt) + (alist-get :other-forms alt)) + (insert (propertize "Alternate forms: " 'face 'reverso-heading-face) + "\n") + (when-let ((model (alist-get :model alt))) + (insert (propertize "Model" 'face 'reverso-keyword-face) + ": " + model "\n")) + (when-let ((auxiliary (alist-get :auxiliary alt))) + (insert (propertize "Auxiliary" 'face 'reverso-keyword-face) + ": " auxiliary "\n")) + (when-let ((other-forms (alist-get :other-forms alt))) + (insert (propertize "Other forms" 'face 'reverso-keyword-face) + ": " other-forms "\n")) + (insert "\n"))) + (when-let ((boxes (alist-get :boxes data))) + ;; (insert (propertize "Conjugation: " 'face 'reverso-heading-face) + ;; "\n") + (dolist (box boxes) + (insert (propertize (alist-get :title box) + 'face 'reverso-heading-face) + "\n") + (dolist (variant (alist-get :listing box)) + (when (car variant) + (insert (propertize (concat (alist-get :title box) " (" (car variant) ")") + 'face 'reverso-heading-face) + "\n")) + (dolist (line (cdr variant)) + (insert "- " line "\n")) + (insert "\n"))))) + (defmacro reverso--with-buffer (&rest body) "Execute BODY in a clean `reverso' results buffer." (declare (indent 0)) @@ -1336,7 +1406,16 @@ OP, DATA and PARAMS are as described in `reverso--operation-hook'." (cl-reduce (lambda (acc x) (+ acc (length (alist-get :synonyms x)))) (nth 1 item) - :initial-value 0))))))) + :initial-value 0)))) + ('reverso--get-conjugation + (list + "Conjugation" + (format + "%s: %s" + (propertize + (symbol-name (alist-get :language (nth 2 item))) + 'face 'reverso-keyword-face) + (alist-get :word (nth 2 item)))))))) (defun reverso--history-display (widget &rest _) "Action for displaying a history item in a WIDGET." @@ -1359,6 +1438,10 @@ OP, DATA and PARAMS are as described in `reverso--operation-hook'." ('reverso--get-synonyms (reverso--synonyms-render (alist-get :text (nth 2 item)) + (nth 1 item))) + ('reverso--get-conjugation + (reverso--conjugation-render + (alist-get :word (nth 2 item)) (nth 1 item))))))) (defun reverso-history () @@ -1755,6 +1838,35 @@ inputs." (reverso--synonyms-exec-suffix) ("q" "Quit" transient-quit-one)]) +(transient-define-infix reverso--transient-conjugation-language () + :class 'reverso--transient-language + :description "Language" + :key "s" + :argument "-s" + :languages (alist-get 'conjugation reverso--languages)) + +(transient-define-suffix reverso--conjugation-exec-suffix (word language) + :key "e" + :description "Find conjugations" + (interactive (transient-args transient-current-command)) + (reverso--get-conjugation + word language + (lambda (data) + (reverso--with-buffer + (reverso--conjugation-render word data) + (setq-local reverso--data data))))) + +;;;###autoload (autoload 'reverso-conjugation "reverso" nil t) +(transient-define-prefix reverso-conjugation () + "Find conjugations." + ["Input" + ("i" "Input" reverso--transient-input-infix)] + ["Parameters" + (reverso--transient-conjugation-language)] + ["Actions" + (reverso--conjugation-exec-suffix) + ("q" "Quit" transient-quit-one)]) + (transient-define-infix reverso--transient-grammar-language () :class 'reverso--transient-language :description "Language" @@ -1885,6 +1997,7 @@ The following features are implemented as nested transient buffers: ("t" "Translation" reverso-translate) ("c" "Context" reverso-context) ("s" "Synonyms" reverso-synonyms) + ("o" "Conjugation" reverso-conjugation) ("g" "Grammar check" reverso-grammar) ("b" "Grammar check in buffer" reverso-grammar-buffer)] ["Other"