From: Tim Vaughan Date: Wed, 11 Sep 2019 09:08:01 +0000 (+0200) Subject: Completed draft of refactor. X-Git-Tag: v2.0.0~6 X-Git-Url: https://thelambdalab.xyz/gitweb/index.cgi?p=elpher.git;a=commitdiff_plain;h=ec368d41d17a7c06cd6dd9ac4cae719d0d324445 Completed draft of refactor. --- diff --git a/elpher.el b/elpher.el index f0d84df..c22f7b8 100644 --- a/elpher.el +++ b/elpher.el @@ -510,18 +510,21 @@ up to the calling function." (defun elpher-get-gopher-node (renderer) (let* ((address (elpher-node-address elpher-current-node)) (content (elpher-get-cached-content address))) - (if (not (and content (funcall renderer nil content))) + (if (and content (funcall renderer nil)) + (progn + (elpher-with-clean-buffer) + (insert content) + (elpher-restore-pos)) (elpher-with-clean-buffer (insert "LOADING... (use 'u' to cancel)")) (elpher-get-selector address (lambda (proc event) (unless (string-prefix-p "deleted" event) - (funcall renderer elpher-selector-string)))))) - (elpher-restore-pos)) + (funcall renderer elpher-selector-string) + (elpher-restore-pos))))))) ;; Index rendering - (defun elpher-insert-index (string) "Insert the index corresponding to STRING into the current buffer." ;; Should be able to split directly on CRLF, but some non-conformant @@ -594,11 +597,11 @@ If ADDRESS is not supplied or nil the record is rendered as an (let ((node (button-get button 'elpher-node))) (elpher-visit-node node))) -(defun elpher-render-index (data &optional cached-data) - "Render DATA as an index, using CACHED-DATA instead if supplied." +(defun elpher-render-index (data) + "Render DATA as an index." (elpher-with-clean-buffer - (if cached-data - (progn (insert cached-data) t) + (if (not data) + t (elpher-insert-index data) (elpher-cache-content (elpher-node-address elpher-current-node) (buffer-string))))) @@ -625,11 +628,11 @@ If ADDRESS is not supplied or nil the record is rendered as an 'help-echo (elpher-node-button-help node)))) (buffer-string))) -(defun elpher-render-text (data &optional cached-data) - "Render DATA as text, using CACHED-DATA instead if supplied." +(defun elpher-render-text (data) + "Render DATA as text." (elpher-with-clean-buffer - (if cached-data - (progn (insert cached-data) t) + (if (not data) + t (insert (elpher-buttonify-urls (elpher-preprocess-text-response) elpher-selector-string)) @@ -639,9 +642,9 @@ If ADDRESS is not supplied or nil the record is rendered as an ;; Image retrieval -(defun elpher-render-image (data &optional cached-data) - "Display DATA as image, CACHED-DATA is ignored." - (if cached-data +(defun elpher-render-image (data) + "Display DATA as image." + (if (not data) f (if (display-images-p) (progn @@ -659,11 +662,11 @@ If ADDRESS is not supplied or nil the record is rendered as an (let* ((address (elpher-node-address elpher-current-node)) (content (elpher-get-cached-content address)) (aborted t)) - (if content - (progn - (funcall renderer nil content) - (elpher-restore-pos) - (message "Displaying cached search results. Reload to perform a new search.")) + (if (and content (funcall renderer nil)) + (elpher-with-clean-buffer + (insert content) + (elpher-restore-pos) + (message "Displaying cached search results. Reload to perform a new search.")) (unwind-protect (let* ((query-string (read-string "Query: ")) (query-selector (concat (elpher-gopher-address-selector address) "\t" query-string)) @@ -686,20 +689,20 @@ If ADDRESS is not supplied or nil the record is rendered as an ;; Raw server response rendering -(defun elpher-render-raw (data &optional cached-data) - "Display raw DATA in buffer. CACHED-DATA is ignored." - (if cached-data +(defun elpher-render-raw (data) + "Display raw DATA in buffer." + (if (not data) f (elpher-with-clean-buffer (insert data) (goto-char (point-min))) (message "Displaying raw server response. Reload or redraw to return to standard view."))) -;; File save renderer +;; File save "rendering" -(defun elpher-render-download (data &optional cached-data) - "Save DATA to file. CACHED-DATA is ignored." - (if cached-data +(defun elpher-render-download (data) + "Save DATA to file." + (if (not data) f (let* ((address (elpher-node-address elpher-current-node)) (selector (elpher-gopher-address-selector address))) @@ -715,26 +718,25 @@ If ADDRESS is not supplied or nil the record is rendered as an (message (format "Saved to file %s." elpher-download-filename))))))) -;; HTML node retrieval +;; HTML rendering -(defun elpher-render-html (data &optional cached-data) - "Render DATA as HTML using shr, or insert CACHED-DATA if non-nil." +(defun elpher-render-html (data) + "Render DATA as HTML using shr." (elpher-with-clean-buffer - (if cached-data - (progn (insert cached-data) t) + (if (not data) + t (let ((dom (with-temp-buffer (insert string) (libxml-parse-html-region (point-min) (point-max))))) (shr-insert-document dom))))) -;;;;;;;;; ONLY COMPLETED TO HERE ;;;;;;;;;;;;;;;;;;;; - ;; Gemini node retrieval (defvar elpher-gemini-response) -(defun elpher-get-gemini (address after) - "Retrieve gemini ADDRESS, then execute AFTER. + +(defun elpher-get-gemini-response (address renderer) + "Retrieve gemini ADDRESS, then execute RENDERER on the result. The response is stored in the variable ‘elpher-gemini-response’." (setq elpher-gemini-response "") (if (not (gnutls-available-p)) @@ -752,41 +754,109 @@ The response is stored in the variable ‘elpher-gemini-response’." (lambda (proc string) (setq elpher-gemini-response (concat elpher-gemini-response string)))) - (set-process-sentinel proc after) + (set-process-sentinel proc + (lambda (proc event) + (unless (string-prefix-p "deleted" event) + (elpher-process-gemini-response #'after)))) (process-send-string proc (concat (elpher-address-to-url address) "\r\n"))))) -(defun elpher-render-gemini-response (mime-type-string) - (let* ((mime-type-string* (if (string-empty-p mime-type-string) - "text/gemini; charset=utf-8" - mime-type-string)) - (mime-type-split (split-string mime-type-string* ";")) - (mime-type (string-trim (car mime-type-split))) - (parameters (mapcar (lambda (s) - (let ((key-val (split-string s "="))) - (list (downcase (string-trim (car key-val))) - (downcase (string-trim (cadr key-val)))))) - (cdr mime-type-split)))) - (if (and (equal "text/gemini" mime-type) - (not (assoc "charset" parameters))) - (setq parameters (cons (list "charset" "utf-8") parameters))) - (when (string-prefix-p "text/" mime-type) - (if (assoc "charset" parameters) - (setq elpher-gemini-response - (decode-coding-string elpher-gemini-response - (intern (cadr (assoc "charset" parameters)))))) - (setq elpher-gemini-response - (replace-regexp-in-string "\r" "" elpher-gemini-response))) - (pcase mime-type - ((or "text/gemini" "") - (elpher-render--mimetype-text/gemini elpher-gemini-response parameters)) - ((pred (string-prefix-p "text/")) - (elpher-render--mimetype-text/plain elpher-gemini-response parameters)) - ((pred (string-prefix-p "image/")) - (elpher-render--mimetype-image/* elpher-gemini-response parameters)) - (other - (error "Unsupported MIME type %S" mime-type))))) +(defun elpher-process-gemini-response (renderer) + "Process the gemini response found in the variable elpher-gemini-response and +pass the result to RENDERER." + (condition-case the-error + (unless (string-prefix-p "deleted" event) + (let* ((response-header (car (split-string elpher-gemini-response "\r\n"))) + (response-body (substring elpher-gemini-response + (+ (string-match "\r\n" elpher-gemini-response) 2))) + (response-code (car (split-string response-header))) + (response-meta (string-trim + (substring response-header + (string-match "[ \t]+" response-header))))) + (pcase (elt response-code 0) + (?1 ; Input required + (elpher-with-clean-buffer + (insert "Gemini server is requesting input.")) + (let* ((query-string (read-string (concat response-meta ": "))) + (url (elpher-address-to-url (elpher-node-address elpher-current-node))) + (query-address (elpher-address-from-url (concat url "?" query-string)))) + (elpher-get-gemini query-address #'renderer))) + (?2 ; Normal response + (message response-header) + (funcall #'renderer elpher-gemini-response)) + (?3 ; Redirect + (message "Following redirect to %s" meta) + (let ((redirect-address (elpher-address-from-gemini-url meta))) + (elpher-get-gemini redirect-address #'renderer))) + (?4 ; Temporary failure + (error "Gemini server reports TEMPORARY FAILURE for this request")) + (?5 ; Permanent failure + (error "Gemini server reports PERMANENT FAILURE for this request")) + (?6 ; Client certificate required + (error "Gemini server requires client certificate (unsupported at this time)")) + (other + (error "Gemini server responded with unknown response code %S" + response-code))))) + (error + (elpher-network-error (elpher-node-address elpher-current-node) the-error)))) + +(defun elpher-get-gemini-node (renderer) + "Getter which retrieves and renders a Gemini node and renders it using RENDERER." + (let* ((address (elpher-node-address elpher-current-node)) + (content (elpher-get-cached-content address))) + (condition-case the-error + (if (and content (funcall renderer nil)) + (progn + (insert content) + (elpher-restore-pos)) + (elpher-with-clean-buffer + (insert "LOADING GEMINI... (use 'u' to cancel)")) + (elpher-get-gemini address + (lambda (proc event) + (unless (string-prefix-p "deleted" event) + (funcall renderer elpher-gemini-response) + (elpher-restore-pos))))) + (error + (elpher-network-error address the-error))))) + + +(defun elpher-render-gemini (data) + "Render gemini response DATA." + (if (not data) + t + (let* ((response-header (car (split-string data "\r\n"))) + (response-body (substring data (+ (string-match "\r\n" data) 2))) + (mime-type-string (string-trim (substring response-header 2))) + (mime-type-string* (if (string-empty-p mime-type-string) + "text/gemini; charset=utf-8" + mime-type-string)) + (mime-type-split (split-string mime-type-string* ";")) + (mime-type (string-trim (car mime-type-split))) + (parameters (mapcar (lambda (s) + (let ((key-val (split-string s "="))) + (list (downcase (string-trim (car key-val))) + (downcase (string-trim (cadr key-val)))))) + (cdr mime-type-split)))) + (if (and (equal "text/gemini" mime-type) + (not (assoc "charset" parameters))) + (setq parameters (cons (list "charset" "utf-8") parameters))) + (when (string-prefix-p "text/" mime-type) + (if (assoc "charset" parameters) + (setq elpher-gemini-response + (decode-coding-string elpher-gemini-response + (intern (cadr (assoc "charset" parameters)))))) + (setq elpher-gemini-response + (replace-regexp-in-string "\r" "" elpher-gemini-response))) + (pcase mime-type + ((or "text/gemini" "") + (elpher-render-gemini-text/gemini response-body parameters)) + ((pred (string-prefix-p "text/")) + (elpher-render-gemini-text/plain response-body parameters)) + ((pred (string-prefix-p "image/")) + (elpher-render-image response-body)) + (other + (error "Unsupported MIME type %S" mime-type)))))) (defun elpher-gemini-get-link-url (line) (string-trim (elt (split-string (substring line 2)) 0))) @@ -814,7 +884,7 @@ The response is stored in the variable ‘elpher-gemini-response’." (setf (url-type address) "gemini"))) address)) -(defun elpher-render--mimetype-text/gemini (data parameters) +(defun elpher-render-gemini-text/gemini (data parameters) (elpher-with-clean-buffer (dolist (line (split-string data "\n")) (if (string-prefix-p "=>" line) @@ -825,78 +895,17 @@ The response is stored in the variable ‘elpher-gemini-response’." (elpher-insert-index-record display-string address) (elpher-insert-index-record url address))) (elpher-insert-index-record line))) - (elpher-restore-pos) (elpher-cache-content (elpher-node-address elpher-current-node) (buffer-string)))) -(defun elpher-render--mimetype-text/plain (data parameters) +(defun elpher-render-gemini-text/plain (data parameters) (elpher-with-clean-buffer (insert (elpher-buttonify-urls (elpher-preprocess-text-response data))) - (elpher-restore-pos) (elpher-cache-content (elpher-node-address elpher-current-node) (buffer-string)))) -(defun elpher-render--mimetype-image/* (data parameters) - (let ((image (create-image data nil t))) - (elpher-with-clean-buffer - (insert-image image) - (elpher-restore-pos)))) - -(defun elpher-process-gemini-response (proc event) - (condition-case the-error - (unless (string-prefix-p "deleted" event) - (let ((response-code (car (split-string elpher-gemini-response-header))) - (meta (string-trim - (substring elpher-gemini-response-header - (string-match "[ \t]+" - elpher-gemini-response-header))))) - (pcase (elt response-code 0) - (?1 ; Input required - (elpher-with-clean-buffer - (insert "Gemini server is requesting input.")) - (let* ((query-string (read-string (concat meta ": "))) - (url (elpher-address-to-url (elpher-node-address elpher-current-node))) - (query-address (elpher-address-from-url (concat url "?" query-string)))) - (elpher-get-gemini query-address #'elpher-process-gemini-response))) - (?2 ; Normal response - (message elpher-gemini-response-header) - (elpher-render-gemini-response meta)) - (?3 ; Redirect - (message "Following redirect to %s" meta) - (let ((redirect-address (elpher-address-from-gemini-url meta))) - (elpher-get-gemini redirect-address #'elpher-process-gemini-response))) - (?4 ; Temporary failure - (error "Gemini server reports TEMPORARY FAILURE for this request")) - (?5 ; Permanent failure - (error "Gemini server reports PERMANENT FAILURE for this request")) - (?6 ; Client certificate required - (error "Gemini server requires client certificate (unsupported at this time)")) - (other - (error "Gemini server responded with unknown response code %S" - response-code))))) - (error - (elpher-network-error (elpher-node-address elpher-current-node) the-error)))) - - -(defun elpher-get-gemini-node (renderer) - "Getter which retrieves and renders a Gemini node." - (let* ((address (elpher-node-address elpher-current-node)) - (content (elpher-get-cached-content address))) - (condition-case the-error - (if content - (progn - (elpher-with-clean-buffer - (insert content) - (elpher-restore-pos))) - (elpher-with-clean-buffer - (insert "LOADING GEMINI... (use 'u' to cancel)")) - (elpher-get-gemini address #'elpher-process-gemini-response)) - (error - (elpher-network-error address the-error))))) - - ;; Other URL node opening (defun elpher-get-other-url-node (renderer)