reverso: conjugation UI works

This commit is contained in:
Pavel Korytov 2024-01-14 00:13:08 +03:00
parent 64b9c3ce53
commit 2d645958c3

View file

@ -676,32 +676,39 @@ SOURCE-TEXT is the text sent for checking. DATA is the JSON reply."
(:corrections . ,corrections)))) (:corrections . ,corrections))))
(defun reverso--empty-to-nil (value) (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)) (if (or (null value) (string-empty-p value))
nil nil
value)) value))
(defun reverso--get-conjugation-parse-alternate (data) (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 (when-let ((alts (thread-first data
(dom-by-id "ch_divConjugatorHeader") (dom-by-id "ch_divConjugatorHeader")
(dom-by-class "alternate-versions")))) (dom-by-class "alternate-versions"))))
`((model . ,(thread-first alts `((:model . ,(thread-first alts
(dom-by-id (rx bos "ch_lblModel" eos)) (dom-by-id (rx bos "ch_lblModel" eos))
;; (dom-by-tag 'a) ;; (dom-by-tag 'a)
(dom-texts) (dom-texts)
(string-trim) (string-trim)
(reverso--empty-to-nil))) (reverso--empty-to-nil)))
(auxiliary . ,(thread-first alts (:auxiliary . ,(thread-first alts
(dom-by-id (rx bos "ch_lblAuxiliary" eos)) (dom-by-id (rx bos "ch_lblAuxiliary" eos))
(dom-texts) (dom-texts)
(string-trim) (string-trim)
(reverso--empty-to-nil))) (reverso--empty-to-nil)))
(other-forms . ,(thread-first alts (:other-forms . ,(thread-first alts
(dom-by-id (rx bos "ch_lblAutreForm" eos)) (dom-by-id (rx bos "ch_lblAutreForm" eos))
(dom-texts) (dom-texts)
(string-trim) (string-trim)
(reverso--empty-to-nil)))))) (reverso--empty-to-nil))))))
(defun reverso--get-conjugation-parse-boxes (data) (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 (when-let ((boxes (thread-first data
(dom-by-class "result-block-api") (dom-by-class "result-block-api")
(car) (car)
@ -741,11 +748,15 @@ SOURCE-TEXT is the text sent for checking. DATA is the JSON reply."
(car group) (car group)
(mapcar #'car (cdr group)))) (mapcar #'car (cdr group))))
(seq-group-by #'cdr listing)))) (seq-group-by #'cdr listing))))
`((title . ,title) `((:title . ,title)
(listing . ,listing-by-v)))) (:listing . ,listing-by-v))))
boxes))) boxes)))
(defun reverso--get-conjugation-parse (data) (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 (let* ((html (with-temp-buffer
(insert data) (insert data)
(libxml-parse-html-region (point-min) (point-max)))) (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))) (reverso--empty-to-nil)))
(alternate (reverso--get-conjugation-parse-alternate html)) (alternate (reverso--get-conjugation-parse-alternate html))
(boxes (reverso--get-conjugation-parse-boxes html))) (boxes (reverso--get-conjugation-parse-boxes html)))
`((word . ,word) `((:word . ,word)
(translit-word . ,translit-word) (:translit-word . ,translit-word)
(alternate . ,alternate) (:alternate . ,alternate)
(boxes . ,boxes)))) (:boxes . ,boxes))))
(defun reverso--get-conjugation (word language cb) (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 (unless (thread-last reverso--languages
(alist-get 'conjugation) (alist-get 'conjugation)
(alist-get language)) (alist-get language))
@ -1054,6 +1083,47 @@ CORR is one element of the `:corrections' list, as defined in
(insert "\n")) (insert "\n"))
(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) (defmacro reverso--with-buffer (&rest body)
"Execute BODY in a clean `reverso' results buffer." "Execute BODY in a clean `reverso' results buffer."
(declare (indent 0)) (declare (indent 0))
@ -1336,7 +1406,16 @@ OP, DATA and PARAMS are as described in `reverso--operation-hook'."
(cl-reduce (lambda (acc x) (cl-reduce (lambda (acc x)
(+ acc (length (alist-get :synonyms x)))) (+ acc (length (alist-get :synonyms x))))
(nth 1 item) (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 _) (defun reverso--history-display (widget &rest _)
"Action for displaying a history item in a WIDGET." "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--get-synonyms
(reverso--synonyms-render (reverso--synonyms-render
(alist-get :text (nth 2 item)) (alist-get :text (nth 2 item))
(nth 1 item)))
('reverso--get-conjugation
(reverso--conjugation-render
(alist-get :word (nth 2 item))
(nth 1 item))))))) (nth 1 item)))))))
(defun reverso-history () (defun reverso-history ()
@ -1755,6 +1838,35 @@ inputs."
(reverso--synonyms-exec-suffix) (reverso--synonyms-exec-suffix)
("q" "Quit" transient-quit-one)]) ("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 () (transient-define-infix reverso--transient-grammar-language ()
:class 'reverso--transient-language :class 'reverso--transient-language
:description "Language" :description "Language"
@ -1885,6 +1997,7 @@ The following features are implemented as nested transient buffers:
("t" "Translation" reverso-translate) ("t" "Translation" reverso-translate)
("c" "Context" reverso-context) ("c" "Context" reverso-context)
("s" "Synonyms" reverso-synonyms) ("s" "Synonyms" reverso-synonyms)
("o" "Conjugation" reverso-conjugation)
("g" "Grammar check" reverso-grammar) ("g" "Grammar check" reverso-grammar)
("b" "Grammar check in buffer" reverso-grammar-buffer)] ("b" "Grammar check in buffer" reverso-grammar-buffer)]
["Other" ["Other"