2024-04-09 01:20:39 +00:00
|
|
|
|
|
|
|
;;; Code:
|
|
|
|
|
|
|
|
(require 'json)
|
|
|
|
(require 'plz)
|
|
|
|
(require 'message)
|
2024-04-13 01:40:06 +00:00
|
|
|
(require 'rx)
|
2024-04-15 02:56:00 +00:00
|
|
|
(require 'markdown-mode)
|
2024-04-09 01:20:39 +00:00
|
|
|
|
|
|
|
(defgroup discoursel nil
|
|
|
|
"Discourse TODO")
|
|
|
|
|
2024-04-15 02:56:00 +00:00
|
|
|
(defcustom discoursel-instances
|
|
|
|
'((ubuntu
|
|
|
|
:url "https://discourse.ubuntu.com"
|
|
|
|
:name "Ubuntu")
|
|
|
|
(fedora
|
|
|
|
:url "https://discussion.fedoraproject.org"
|
|
|
|
:name "Fedora"))
|
2024-04-09 01:20:39 +00:00
|
|
|
"The Discourse instance."
|
|
|
|
:group 'discoursel
|
|
|
|
:type 'string)
|
|
|
|
|
2024-04-13 01:40:06 +00:00
|
|
|
(defcustom discoursel-fetch-all-posts-from-topic t
|
|
|
|
"Whether to fetch all posts from a certain topic.
|
|
|
|
|
|
|
|
If t, the default, all posts are fetched (which may take a while
|
|
|
|
depending on the topic's length).
|
|
|
|
|
|
|
|
If nil, pagination is enabled and the user needs to ask for more
|
|
|
|
posts manually and incrementally."
|
|
|
|
:group 'discoursel
|
|
|
|
:type 'boolean)
|
|
|
|
|
2024-04-09 01:20:39 +00:00
|
|
|
(defvar discoursel-buffer-list nil
|
|
|
|
"List of discoursel buffers.")
|
|
|
|
|
|
|
|
(defface discoursel-topic-face
|
|
|
|
'((((class color) (background light)) (:foreground "#000"))
|
|
|
|
(((class color) (background dark)) (:foreground "#fff")))
|
|
|
|
"Face used for topics."
|
|
|
|
:group 'discoursel)
|
|
|
|
|
|
|
|
(defface discoursel-username-face
|
|
|
|
'((((class color) (background light)) (:foreground "#aa0"))
|
|
|
|
(((class color) (background dark)) (:foreground "#ff0")))
|
|
|
|
"Face used for usernames."
|
|
|
|
:group 'discoursel)
|
|
|
|
|
|
|
|
(defface discoursel-code-face
|
|
|
|
'((((class color) (background light)) (:background "#cdcecc" :extend t))
|
|
|
|
(((class color) (background dark)) (:background "#17211c" :extend t)))
|
|
|
|
"Face used for code blocks."
|
|
|
|
:group 'discoursel)
|
|
|
|
|
|
|
|
(defun discoursel--parse-pre (tag)
|
2024-04-13 01:40:06 +00:00
|
|
|
(let ((p (point))
|
|
|
|
(shr-folding-mode 'none))
|
|
|
|
(shr-ensure-newline)
|
|
|
|
(shr-generic tag)
|
|
|
|
(shr-ensure-newline)
|
|
|
|
(add-text-properties p (point)
|
|
|
|
'(pre-tag t face discoursel-code-face))))
|
2024-04-09 01:20:39 +00:00
|
|
|
|
2024-04-15 02:56:00 +00:00
|
|
|
(defun discoursel--get (instance endpoint &optional args)
|
2024-04-09 01:20:39 +00:00
|
|
|
(condition-case plzerror
|
2024-04-15 02:56:00 +00:00
|
|
|
(let* ((instance-url (plist-get (cdr instance) ':url))
|
|
|
|
(full-url (concat instance-url "/" endpoint ".json")))
|
|
|
|
(plz 'get (concat full-url
|
|
|
|
(when args
|
|
|
|
(concat "?"
|
|
|
|
(mapconcat (lambda (elt)
|
|
|
|
(concat (car elt) "=" (cdr elt)))
|
|
|
|
args "&"))))
|
|
|
|
:as #'json-read))
|
2024-04-09 01:20:39 +00:00
|
|
|
(plz-error
|
|
|
|
(error "Could not query endpoint '%s': '%s'"
|
|
|
|
endpoint (plz-error-message plzerror)))))
|
|
|
|
|
2024-04-15 02:56:00 +00:00
|
|
|
(defun discoursel--get-invisibility-spec (instance &optional section)
|
|
|
|
(let* ((instance-name (symbol-name (car instance)))
|
|
|
|
(spec-str (concat "discoursel-visibility-" instance-name
|
|
|
|
(when section
|
|
|
|
(concat "-" section)))))
|
|
|
|
(intern spec-str)))
|
|
|
|
|
|
|
|
(defun discoursel--latest-topics (instance)
|
|
|
|
(let* ((r (discoursel--get instance "latest"))
|
|
|
|
(instance-symbol (car instance))
|
2024-04-09 01:20:39 +00:00
|
|
|
(topics (alist-get 'topics (alist-get 'topic_list r)))
|
2024-04-15 02:56:00 +00:00
|
|
|
(users (alist-get 'users r))
|
|
|
|
(initial-point (point)))
|
|
|
|
(insert (propertize ">> Latest topics" 'invisible (discoursel--get-invisibility-spec instance)))
|
|
|
|
(insert
|
|
|
|
(with-temp-buffer
|
|
|
|
(insert "\n")
|
|
|
|
(cl-map 'vector
|
|
|
|
(lambda (topic)
|
|
|
|
(let* ((userid (alist-get 'user_id (aref (alist-get 'posters topic) 0)))
|
|
|
|
(username (seq-find
|
|
|
|
(lambda (v)
|
|
|
|
(= (alist-get 'id v) userid))
|
|
|
|
users))
|
|
|
|
(inhibit-read-only t))
|
|
|
|
(insert
|
|
|
|
(propertize (format " %s (by %s)\n"
|
|
|
|
(propertize (alist-get 'title topic)
|
|
|
|
'face 'discoursel-topic-face)
|
|
|
|
(propertize (alist-get 'name username)
|
|
|
|
'face 'discoursel-username-face))
|
|
|
|
'discoursel-topic-slug (alist-get 'slug topic)
|
|
|
|
'discoursel-topic-id (alist-get 'id topic)))))
|
|
|
|
topics)
|
|
|
|
(put-text-property (point-min) (point-max)
|
|
|
|
'invisible `(,(discoursel--get-invisibility-spec instance "latest")
|
|
|
|
,(discoursel--get-invisibility-spec instance)))
|
|
|
|
(buffer-string)))
|
|
|
|
(add-text-properties (1- initial-point) (point-max)
|
|
|
|
`(discoursel-section "latest"
|
|
|
|
discoursel-instance ,instance-symbol))))
|
|
|
|
|
2024-04-09 01:20:39 +00:00
|
|
|
(defun discoursel-update ()
|
|
|
|
(interactive)
|
|
|
|
(with-current-buffer (discoursel-get-create-buffer)
|
2024-04-13 01:40:06 +00:00
|
|
|
(save-excursion
|
|
|
|
(let ((inhibit-read-only t))
|
2024-04-15 02:56:00 +00:00
|
|
|
(erase-buffer)
|
|
|
|
(dolist (instance discoursel-instances)
|
|
|
|
(let* ((instance-symbol (car instance))
|
|
|
|
(instance-plist (cdr instance))
|
|
|
|
(instance-name (plist-get instance-plist ':name)))
|
|
|
|
(insert (propertize (format "💻 %s" instance-name)
|
|
|
|
'discoursel-instance instance-symbol))
|
|
|
|
(insert (propertize "\n" 'invisible (discoursel--get-invisibility-spec instance)))
|
|
|
|
;; (let ((latest-topic-initial-point (point))
|
|
|
|
;; (latest-topic-final-point))
|
|
|
|
;; (insert ">> Latest topics")
|
|
|
|
;; (setq latest-topic-final-point (point))
|
|
|
|
;; (insert "\n")
|
|
|
|
(discoursel--latest-topics instance)
|
|
|
|
(insert "\n")))
|
|
|
|
(kill-line -2)))))
|
|
|
|
; (put-text-property latest-topic-final-point (point) 'invisible 'latest-topics))))))
|
|
|
|
;; (put-text-property latest-topic-initial-point
|
|
|
|
;; latest-topic-final-point
|
|
|
|
;; 'overlay (make-overlay (1+ latest-topic-final-point) (point-max))))))))
|
|
|
|
|
|
|
|
(defun discoursel-toggle-heading ()
|
|
|
|
(interactive)
|
|
|
|
(when-let* ((instance-to-toggle (get-text-property (point) 'discoursel-instance))
|
|
|
|
(instance (assoc instance-to-toggle discoursel-instances)))
|
|
|
|
(let* ((section-to-toggle (get-text-property (point) 'discoursel-section))
|
|
|
|
(spec (cons (discoursel--get-invisibility-spec instance
|
|
|
|
section-to-toggle)
|
|
|
|
t))
|
|
|
|
(invisible-p (and (listp buffer-invisibility-spec)
|
|
|
|
(member spec buffer-invisibility-spec))))
|
|
|
|
(if invisible-p
|
|
|
|
(remove-from-invisibility-spec spec)
|
|
|
|
(add-to-invisibility-spec spec)))))
|
2024-04-13 01:40:06 +00:00
|
|
|
|
|
|
|
(defun discoursel--get-name-or-username (post)
|
|
|
|
(let ((name (alist-get 'display_username post)))
|
|
|
|
(if (and name (not (string-empty-p name)))
|
|
|
|
name
|
|
|
|
(alist-get 'username post))))
|
|
|
|
|
|
|
|
(defun discoursel--parse-heading (dom)
|
|
|
|
"Prepend <hN> tag (DOM) with the equivalent number of sharp (#)
|
|
|
|
characters."
|
|
|
|
(let* ((idx (1- (length dom)))
|
|
|
|
;; This is a bit hacky and assumes that the text will always
|
|
|
|
;; be available as the last item in the list.
|
|
|
|
(tagname (symbol-name (car dom)))
|
|
|
|
(level (string-to-number
|
|
|
|
(string-remove-prefix "h" tagname)))
|
|
|
|
(sharps (make-string level ?#))
|
|
|
|
(face (intern (concat "shr-" tagname)))
|
|
|
|
(possible-text (nth idx dom)))
|
|
|
|
(when (stringp possible-text)
|
|
|
|
(setf (nth idx dom) (concat sharps " " possible-text)))
|
|
|
|
(shr-heading dom face)))
|
|
|
|
|
|
|
|
(defun discoursel-topic-next-entry ()
|
|
|
|
(interactive)
|
|
|
|
(let ((pos (point)))
|
|
|
|
(save-excursion
|
2024-04-15 02:56:00 +00:00
|
|
|
(setq pos (next-single-char-property-change pos 'discoursel-post-id))
|
2024-04-13 01:40:06 +00:00
|
|
|
(goto-char pos)
|
|
|
|
(while (and (not (eobp))
|
|
|
|
(not (looking-at-p (rx line-start "Author:"))))
|
2024-04-15 02:56:00 +00:00
|
|
|
(setq pos (next-single-char-property-change pos 'discoursel-post-id))
|
2024-04-13 01:40:06 +00:00
|
|
|
(goto-char pos))
|
|
|
|
(when (eobp)
|
|
|
|
(setq pos nil)))
|
|
|
|
(when pos
|
|
|
|
(goto-char pos))))
|
|
|
|
|
|
|
|
(defun discoursel-topic-previous-entry ()
|
|
|
|
(interactive)
|
|
|
|
(let ((pos (point)))
|
2024-04-15 02:56:00 +00:00
|
|
|
(setq pos (or (previous-single-property-change pos 'discoursel-post-id)
|
2024-04-13 01:40:06 +00:00
|
|
|
(point-min)))
|
|
|
|
(goto-char pos)
|
|
|
|
(while (and (not (bobp))
|
|
|
|
(not (looking-at-p (rx line-start (or "Author:" "Title:")))))
|
2024-04-15 02:56:00 +00:00
|
|
|
(setq pos (or (previous-single-property-change pos 'discoursel-post-id)
|
2024-04-13 01:40:06 +00:00
|
|
|
(point-min)))
|
|
|
|
(goto-char pos))))
|
|
|
|
|
2024-04-15 02:56:00 +00:00
|
|
|
(defun discoursel--insert-post (instance post &optional post-title)
|
2024-04-13 01:40:06 +00:00
|
|
|
(let* ((post-slug (alist-get 'topic_slug post))
|
|
|
|
(post-id (alist-get 'id post))
|
2024-04-15 02:56:00 +00:00
|
|
|
(post-number (alist-get 'post_number post))
|
2024-04-13 01:40:06 +00:00
|
|
|
(post-date (alist-get 'created_at post))
|
|
|
|
(post-date-string
|
|
|
|
(format-time-string "%c"
|
|
|
|
(encode-time
|
|
|
|
(parse-time-string post-date))))
|
2024-04-15 02:56:00 +00:00
|
|
|
(post-author-name (discoursel--get-name-or-username post))
|
|
|
|
(post-author-username (alist-get 'username post))
|
|
|
|
(post-html-content (alist-get 'cooked post))
|
|
|
|
(instance-symbol (car instance)))
|
|
|
|
(save-excursion
|
|
|
|
(insert
|
|
|
|
(with-temp-buffer
|
|
|
|
(when post-title
|
|
|
|
(insert (format (propertize "Title: %s\n" 'face 'message-header-name)
|
|
|
|
(propertize post-title 'face 'message-header-subject))))
|
|
|
|
(insert (format (propertize "Author: %s\n" 'face 'message-header-name)
|
|
|
|
(propertize post-author-name 'face 'message-header-to)))
|
|
|
|
(insert (format (propertize "Date: %s\n" 'face 'message-header-name)
|
|
|
|
(propertize post-date-string 'face 'message-header-other)))
|
|
|
|
(insert "\n")
|
|
|
|
(save-excursion
|
|
|
|
(insert post-html-content))
|
|
|
|
(let ((shr-use-fonts nil)
|
|
|
|
(shr-external-rendering-functions
|
|
|
|
'((pre . discoursel--parse-pre)
|
|
|
|
(h1 . discoursel--parse-heading)
|
|
|
|
(h2 . discoursel--parse-heading)
|
|
|
|
(h3 . discoursel--parse-heading)
|
|
|
|
(h4 . discoursel--parse-heading)
|
|
|
|
(h5 . discoursel--parse-heading)
|
|
|
|
(h6 . discoursel--parse-heading))))
|
|
|
|
(shr-render-region (point) (point-max)))
|
|
|
|
(add-text-properties (point-min) (point-max)
|
|
|
|
`(discoursel-post-id ,post-id
|
|
|
|
discoursel-post-number ,post-number
|
|
|
|
discoursel-post-author-username ,post-author-username
|
|
|
|
discoursel-post-author-name ,post-author-name
|
|
|
|
discoursel-instance ,instance-symbol))
|
|
|
|
(buffer-string))))))
|
|
|
|
|
|
|
|
(defun discoursel-render-posts-from-topic (instance topic &optional start-idx)
|
2024-04-13 02:17:16 +00:00
|
|
|
(let* ((posts (alist-get 'posts (alist-get 'post_stream topic)))
|
|
|
|
(topic-slug (alist-get 'slug topic))
|
|
|
|
(buf (discoursel-get-create-buffer topic-slug))
|
|
|
|
(startidx (or start-idx 0)))
|
|
|
|
(with-current-buffer buf
|
|
|
|
(save-excursion
|
|
|
|
(let ((inhibit-read-only t))
|
|
|
|
(cl-loop for idx from startidx to (1- (length posts)) do
|
|
|
|
(goto-char (point-max))
|
|
|
|
(insert
|
|
|
|
"\n"
|
|
|
|
(with-temp-buffer
|
|
|
|
(insert "<hr>")
|
|
|
|
(shr-render-region (point-min) (point-max))
|
|
|
|
(buffer-string)))
|
2024-04-15 02:56:00 +00:00
|
|
|
(discoursel--insert-post instance (aref posts idx))))))))
|
2024-04-13 02:17:16 +00:00
|
|
|
|
2024-04-15 02:56:00 +00:00
|
|
|
(defun discoursel--fetch-all-posts-from-topic (instance topic-id number-of-pages)
|
2024-04-13 02:17:16 +00:00
|
|
|
(cl-loop for page from 2 to number-of-pages do
|
|
|
|
(with-delayed-message (2 (format "Rendering page %d out of %d..." page number-of-pages))
|
2024-04-15 02:56:00 +00:00
|
|
|
(let ((topic (discoursel--get instance
|
|
|
|
(concat "t/" topic-id)
|
|
|
|
`(("page" . ,(number-to-string page))))))
|
|
|
|
(discoursel-render-posts-from-topic instance topic)))))
|
2024-04-13 02:17:16 +00:00
|
|
|
|
2024-04-15 02:56:00 +00:00
|
|
|
(defun discoursel--open-topic (&optional instance id)
|
2024-04-09 01:20:39 +00:00
|
|
|
(interactive)
|
|
|
|
(when (and (not (eq major-mode 'discoursel-mode))
|
|
|
|
(not id))
|
2024-04-13 01:40:06 +00:00
|
|
|
(user-error "You must be in a discoursel buffer"))
|
2024-04-15 02:56:00 +00:00
|
|
|
(let* ((instance-name (or instance
|
|
|
|
(get-text-property (point) 'discoursel-instance)
|
|
|
|
(user-error "You must select a topic to open")))
|
|
|
|
(instance-data (assoc instance-name discoursel-instances))
|
|
|
|
(topic-id (number-to-string
|
2024-04-09 01:20:39 +00:00
|
|
|
(or id (get-text-property (point) 'discoursel-topic-id))))
|
2024-04-15 02:56:00 +00:00
|
|
|
(topic (discoursel--get instance-data (concat "t/" topic-id)))
|
2024-04-13 02:17:16 +00:00
|
|
|
(posts (alist-get 'posts (alist-get 'post_stream topic)))
|
2024-04-09 01:20:39 +00:00
|
|
|
(first-post (aref posts 0))
|
2024-04-13 02:17:16 +00:00
|
|
|
(topic-slug (alist-get 'slug topic))
|
|
|
|
(topic-title (alist-get 'title topic))
|
|
|
|
(buf (discoursel-get-create-buffer topic-slug))
|
|
|
|
(number-of-pages (ceiling (/ (float (alist-get 'posts_count topic))
|
|
|
|
(float (alist-get 'chunk_size topic))))))
|
2024-04-09 01:20:39 +00:00
|
|
|
(switch-to-buffer buf)
|
|
|
|
(with-current-buffer buf
|
|
|
|
(discoursel-topic-mode)
|
2024-04-13 01:40:06 +00:00
|
|
|
(save-excursion
|
|
|
|
(let ((inhibit-read-only t))
|
|
|
|
(erase-buffer)
|
2024-04-15 02:56:00 +00:00
|
|
|
(discoursel--insert-post instance-data first-post topic-title)
|
|
|
|
(discoursel-render-posts-from-topic instance-data topic 1)
|
2024-04-13 02:17:16 +00:00
|
|
|
(when (> number-of-pages 1)
|
|
|
|
(if discoursel-fetch-all-posts-from-topic
|
2024-04-15 02:56:00 +00:00
|
|
|
(discoursel--fetch-all-posts-from-topic instance-data
|
|
|
|
topic-id
|
|
|
|
number-of-pages)
|
|
|
|
(message "TODO")))
|
|
|
|
(put-text-property (point-min) (point-max)
|
|
|
|
'discoursel-topic-id (string-to-number topic-id)))))))
|
|
|
|
|
|
|
|
(defun discoursel-reply-to-post (&optional arg)
|
|
|
|
(interactive "P")
|
|
|
|
(let* ((post-id (or (get-text-property (point) 'discoursel-post-id)
|
|
|
|
(user-error "You must choose a post to reply to")))
|
|
|
|
(instance (assoc (get-text-property (point) 'discoursel-instance)
|
|
|
|
discoursel-instances))
|
|
|
|
(buf (discoursel-get-create-buffer (number-to-string post-id)))
|
|
|
|
(original-topic-point (point))
|
|
|
|
(original-topic-buffer (current-buffer)))
|
|
|
|
(switch-to-buffer-other-window buf)
|
|
|
|
(with-current-buffer buf
|
|
|
|
(when arg
|
|
|
|
(let ((post-raw-contents
|
|
|
|
(alist-get 'raw
|
|
|
|
(discoursel--get instance (concat "posts/"
|
|
|
|
(number-to-string post-id)))))
|
|
|
|
(post-author-username (get-text-property original-topic-point
|
|
|
|
'discoursel-post-author-username
|
|
|
|
original-topic-buffer))
|
|
|
|
(post-number (get-text-property original-topic-point
|
|
|
|
'discoursel-post-number
|
|
|
|
original-topic-buffer))
|
|
|
|
(topic-id (get-text-property original-topic-point
|
|
|
|
'discoursel-topic-id
|
|
|
|
original-topic-buffer)))
|
|
|
|
(insert (format "[quote=\"%s, post:%d, topic:%d\"]\n"
|
|
|
|
post-author-username
|
|
|
|
post-number
|
|
|
|
topic-id))
|
|
|
|
(insert post-raw-contents)
|
|
|
|
(goto-char (point-min))
|
|
|
|
;; We can't quote the [quote...] tag.
|
|
|
|
(next-line)
|
|
|
|
(while (re-search-forward (rx line-start) nil t)
|
|
|
|
(replace-match "> " t t))
|
|
|
|
(goto-char (point-max))
|
|
|
|
(insert "\n\n")))
|
|
|
|
(discoursel-post-reply-mode))
|
|
|
|
(message "Type C-c C-c to post reply, C-c C-k to discard it")))
|
|
|
|
|
|
|
|
(defun discoursel-open-topic (instance id)
|
|
|
|
(interactive "nInstance: \nnTopic ID: ")
|
|
|
|
(discoursel--open-topic instance id))
|
2024-04-09 01:20:39 +00:00
|
|
|
|
|
|
|
(defun discoursel-get-create-buffer (&optional name)
|
|
|
|
(let* ((bufname (concat "*discoursel"
|
|
|
|
(when name (concat "-" name))
|
|
|
|
"*"))
|
|
|
|
(buf (get-buffer-create bufname)))
|
|
|
|
(prog1 buf
|
|
|
|
(unless (memq buf discoursel-buffer-list)
|
|
|
|
(add-to-list 'discoursel-buffer-list buf)))))
|
|
|
|
|
|
|
|
(defun discoursel-kill-buffer (&optional buf)
|
|
|
|
(interactive)
|
|
|
|
(let* ((tmpb (or buf (current-buffer)))
|
|
|
|
(b (get-buffer tmpb)))
|
|
|
|
(unless b
|
|
|
|
(error "Invalid buffer %s" b))
|
|
|
|
(with-current-buffer b
|
|
|
|
(unless (derived-mode-p 'discoursel-mode)
|
|
|
|
(error "Cannot kill buffer %s: not a discoursel buffer" (buffer-name))))
|
|
|
|
(kill-buffer b)
|
|
|
|
(setq discoursel-buffer-list (remove b discoursel-buffer-list))))
|
|
|
|
|
|
|
|
(defun discoursel-exit ()
|
|
|
|
(interactive)
|
|
|
|
(dolist (buf discoursel-buffer-list)
|
|
|
|
(kill-buffer buf))
|
|
|
|
(setq discoursel-buffer-list nil))
|
|
|
|
|
|
|
|
(defvar discoursel-mode-map
|
|
|
|
(let ((map (make-sparse-keymap)))
|
|
|
|
(prog1 map
|
|
|
|
(suppress-keymap map)
|
|
|
|
(define-key map "n" #'next-line)
|
|
|
|
(define-key map "p" #'previous-line)
|
|
|
|
(define-key map (kbd "RET") #'discoursel--open-topic)
|
|
|
|
(define-key map "j" #'discoursel-open-topic)
|
2024-04-15 02:56:00 +00:00
|
|
|
(define-key map (kbd "TAB") #'discoursel-toggle-heading)
|
2024-04-09 01:20:39 +00:00
|
|
|
(define-key map "q" #'discoursel-exit)
|
|
|
|
(define-key map "g" #'discoursel-update)))
|
|
|
|
"Keymap for `discoursel-mode'.")
|
|
|
|
|
|
|
|
(defvar discoursel-topic-mode-map
|
|
|
|
(let ((map (make-sparse-keymap)))
|
|
|
|
(prog1 map
|
|
|
|
(suppress-keymap map)
|
2024-04-13 01:40:06 +00:00
|
|
|
(define-key map "n" #'next-line)
|
|
|
|
(define-key map "p" #'previous-line)
|
|
|
|
(define-key map (kbd "M-n") #'discoursel-topic-next-entry)
|
|
|
|
(define-key map (kbd "M-p") #'discoursel-topic-previous-entry)
|
2024-04-15 02:56:00 +00:00
|
|
|
(define-key map (kbd "C-c C-r") #'discoursel-reply-to-post)
|
2024-04-09 01:20:39 +00:00
|
|
|
(define-key map "q" #'discoursel-kill-buffer)))
|
|
|
|
"Keymap for `discoursel-topic-mode'.")
|
|
|
|
|
2024-04-15 02:56:00 +00:00
|
|
|
(defvar discoursel-post-reply-mode-map
|
|
|
|
(let ((map (make-sparse-keymap)))
|
|
|
|
(prog1 map
|
|
|
|
(define-key map (kbd "C-c C-k") #'kill-buffer-and-window)))
|
|
|
|
"Keymap for `discoursel-post-reply-mode.")
|
|
|
|
|
2024-04-09 01:20:39 +00:00
|
|
|
(define-derived-mode discoursel-mode special-mode "Discourse client"
|
|
|
|
"Major mode for interacting with Discourse instances.
|
|
|
|
\\{discoursel-mode-map}"
|
|
|
|
(use-local-map discoursel-mode-map))
|
|
|
|
|
|
|
|
(define-derived-mode discoursel-topic-mode discoursel-mode "Discoursel topic"
|
|
|
|
"Major mode for interacting with Discourse topics.
|
|
|
|
\\{discoursel-topic-mode-map}"
|
|
|
|
(use-local-map discoursel-topic-mode-map))
|
|
|
|
|
2024-04-15 02:56:00 +00:00
|
|
|
(define-derived-mode discoursel-post-reply-mode markdown-mode "Discoursel post reply"
|
|
|
|
"Major mode for replying to Discourse posts.
|
|
|
|
\\{discoursel-post-reply-mode-map}"
|
|
|
|
(use-local-map discoursel-post-reply-mode-map))
|
|
|
|
|
2024-04-09 01:20:39 +00:00
|
|
|
(defun discoursel ()
|
|
|
|
"Enter discoursel."
|
|
|
|
(interactive)
|
|
|
|
(switch-to-buffer (discoursel-get-create-buffer))
|
|
|
|
(unless (eq major-mode 'discoursel-mode)
|
|
|
|
(discoursel-mode))
|
2024-04-15 02:56:00 +00:00
|
|
|
(setq buffer-invisibility-spec nil)
|
2024-04-09 01:20:39 +00:00
|
|
|
(discoursel-update))
|
|
|
|
|
|
|
|
|
|
|
|
;; TODO
|
|
|
|
;; (with-current-buffer (get-buffer-create "test-discourse")
|
|
|
|
;; (insert (plz 'get "https://discourse.ubuntu.com/raw/39765"))
|
|
|
|
;; (markdown-mode)
|
|
|
|
;; (setq buffer-read-only t))
|
|
|
|
|
|
|
|
;; (with-current-buffer (get-buffer-create "test-discourse4")
|
|
|
|
;; (let* ((ttt (plz 'get "https://discourse.ubuntu.com/latest.json" :as #'json-read))
|
|
|
|
;; (topics (alist-get 'topics (alist-get 'topic_list ttt)))
|
|
|
|
;; (users (alist-get 'users ttt)))
|
|
|
|
;; (cl-map 'vector
|
|
|
|
;; (lambda (topic)
|
|
|
|
;; (let* ((uid (alist-get 'user_id (aref (alist-get 'posters topic) 0)))
|
|
|
|
;; (user (seq-find (lambda (v) (= (alist-get 'id v) uid))
|
|
|
|
;; users)))
|
|
|
|
;; (insert
|
|
|
|
;; (propertize (format "%s [%s]\n" (alist-get 'title topic) (alist-get 'name user))
|
|
|
|
;; 'discoursel-slug (alist-get 'slug topic)))))
|
|
|
|
;; topics)))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(provide 'discoursel)
|
|
|
|
|
|
|
|
;;; discoursel.el ends here
|