enable `eldoc-mode` on `org-tag-eldoc-mode` enabled
[org-tag-eldoc.git] / org-tag-eldoc-urban-dictionary.el
blob2c149294f94076ce3f5de20169b58e648fd19223
1 ;;; org-tag-eldoc-urban-dictionary.el --- Urban Dictionary -*- lexical-binding: t; -*-
2 ;; -*- coding: utf-8 -*-
4 ;; Copyright (C) 2024-2025 Christopher M. Miles, all rights reserved.
6 ;;; Commentary:
8 ;; search API: https://www.urbandictionary.com/define.php?term=[query]. The `query' is "prone%20bone".
10 ;;; Code:
12 (require 'url)
13 (require 'url-http) ; for `url-http-end-of-headers'
14 (require 'request)
15 (require 'dom)
16 (require 'elquery nil t) ; optionally use elquery.el to replace dom.el.
17 (require 'org-tag-eldoc-common)
20 (defun org-tag-eldoc-urban-dictionary--request (tag)
21 "Send HTTP GET request to get TAG explanation dtaa on www.urbandictionary.com."
22 (let ((request-backend 'url-retrieve) ; use `url-retrieve' backend for proxy.
23 (request-message-level -1)
24 (url-proxy-services org-tag-eldoc-request-proxy)
25 (url (format "https://www.urbandictionary.com/define.php?term=%s"
26 (url-hexify-string (string-replace "_" " " tag)))))
27 (request url
28 :type "GET"
29 :parser (lambda ()
30 (cond
31 ((featurep 'elquery) ; convert HTML -> elquery object structure
32 (elquery-read-buffer (current-buffer)))
33 ((fboundp 'libxml-parse-html-region) ; convert HTML -> Elisp alist structure
34 (libxml-parse-html-region (point-min) (point-max)))))
35 :success (cl-function
36 (lambda (&key data &allow-other-keys)
37 ;; DEBUG: (setq request-result data)
38 (cond
39 ((featurep 'elquery)
40 ;; for `elquery' parser
41 (setq org-tag-eldoc--explanation
42 (car
43 (reverse
44 (mapcar 'elquery-text
45 (elquery-$ "section > div:nth-child(1) > div > div.break-words.meaning.mb-4" data))))))
46 ((featurep 'dom)
47 ;; for `libxml' parser
48 (setq org-tag-eldoc--explanation
49 (org-tag-eldoc-common--format-explanation
50 (dom-texts
51 ;; FIXME: The CSS selector is not right.
52 (dom-search (dom-by-class data "div.break-words.meaning.mb-4")
53 (lambda (node) (and (eq (cl-first node) 'p) (null (cl-second node)))))))))
54 )))
55 :error (cl-function
56 (lambda (&rest args &key error-thrown &allow-other-keys)
57 ;; (message "[org-tag-eldoc] (Urban Dictionary) request error %s!" error-thrown)
58 nil))
59 :status-code '((404 . (lambda (&rest _) nil))
60 (443 . (lambda (&rest _) nil))
61 (500 . (lambda (&rest _) nil))))
63 ;; (with-current-buffer (url-retrieve-synchronously url)
64 ;; (let ((dom (progn
65 ;; (goto-char url-http-end-of-headers)
66 ;; (libxml-parse-html-region (point) (point-max)))))
67 ;; ;; DEBUG: (setq request-result dom)
68 ;; (org-tag-eldoc-common--format-explanation
69 ;; (cond
70 ;; ((featurep 'dom)
71 ;; (dom-texts
72 ;; (dom-search (dom-by-class dom "mw-content-ltr\\ mw-parser-output")
73 ;; (lambda (node) (and (eq (cl-first node) 'p) (null (cl-second node)))))))
74 ;; ((featurep 'elquery)
75 ;; (elquery-text (car (elquery-$ "#mw-content-text > div.mw-content-ltr.mw-parser-output > p" data))))))))
79 ;;; TEST: https://www.urbandictionary.com/define.php?term=bed%20time
80 ;; (org-tag-eldoc-urban-dictionary--request "bed_time")
82 ;;; CSS Selector:
83 ;;; #ud-root > div > main > div > div.flex.flex-col.lg\:flex-row.mx-0.gap-4 > section > div:nth-child(1) > div > div.break-words.meaning.mb-4
84 ;;;
85 ;;; XPath:
86 ;;; //*[@id="ud-root"]/div/main/div/div[4]/section/div[1]/div/div[2]
87 ;;; full XPath:
88 ;;; /html/body/div[1]/div/main/div/div[4]/section/div[1]/div/div[2]
89 ;;;
90 ;;; JavaScript Path
91 ;;; document.querySelector("#ud-root > div > main > div > div.flex.flex-col.lg\\:flex-row.mx-0.gap-4 > section > div:nth-child(1) > div > div.break-words.meaning.mb-4")
93 ;;; TEST: `request-result'
95 ;;; TODO:
96 ;; (when (featurep 'dom)
97 ;; ;; for `libxml' parser
98 ;; (setq org-tag-eldoc--explanation
99 ;; (org-tag-eldoc-common--format-explanation
100 ;; (dom-texts
101 ;; (dom-search (dom-by-class request-result "section > div:nth-child(1) > div > div.break-words.meaning.mb-4")
102 ;; (lambda (node) (and (eq (cl-first node) 'p) (null (cl-second node)))))))))
104 ;;; DONE:
105 ;; (when (featurep 'elquery)
106 ;; ;; for `elquery' parser
107 ;; (setq org-tag-eldoc--explanation
108 ;; (car
109 ;; (reverse
110 ;; (mapcar 'elquery-text
111 ;; (elquery-$ "section > div:nth-child(1) > div > div.break-words.meaning.mb-4" request-result))))))
113 (defun org-tag-eldoc-urban-dictionary-query (tag)
114 "Query TAG on Urban Dictionary then return a cons cell of tag and explanation."
115 (if (stringp org-tag-eldoc--explanation)
116 org-tag-eldoc--explanation
117 (org-tag-eldoc-urban-dictionary--request tag)
118 (sit-for 1.0)
119 (org-tag-eldoc-database-save tag org-tag-eldoc--explanation)))
123 (provide 'org-tag-eldoc-urban-dictionary)
125 ;;; org-tag-eldoc-urban-dictionary.el ends here