+ (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
+ ;; LF-only servers sadly exist, hence the following.
+ (let ((str-processed (elpher-preprocess-text-response string)))
+ (dolist (line (split-string str-processed "\n"))
+ (ignore-errors
+ (unless (= (length line) 0)
+ (let* ((type (elt line 0))
+ (fields (split-string (substring line 1) "\t"))
+ (display-string (elt fields 0))
+ (selector (elt fields 1))
+ (host (elt fields 2))
+ (port (if (elt fields 3)
+ (string-to-number (elt fields 3))
+ nil))
+ (address (elpher-make-gopher-address type selector host port)))
+ (elpher-insert-index-record display-string address)))))))
+
+(defun elpher-insert-margin (&optional type-name)
+ "Insert index margin, optionally containing the TYPE-NAME, into the current buffer."
+ (if type-name
+ (progn
+ (insert (format (concat "%" (number-to-string (- elpher-margin-width 1)) "s")
+ (concat
+ (propertize "[" 'face 'elpher-margin-brackets)
+ (propertize type-name 'face 'elpher-margin-key)
+ (propertize "]" 'face 'elpher-margin-brackets))))
+ (insert " "))
+ (insert (make-string elpher-margin-width ?\s))))
+
+(defun elpher-node-button-help (node)
+ "Return a string containing the help text for a button corresponding to NODE."
+ (let ((address (elpher-node-address node)))
+ (format "mouse-1, RET: open '%s'" (elpher-address-to-url address))))
+
+(defun elpher-insert-index-record (display-string &optional address)
+ "Function to insert an index record into the current buffer.
+The contents of the record are dictated by DISPLAY-STRING and ADDRESS.
+If ADDRESS is not supplied or nil the record is rendered as an
+'information' line."
+ (let* ((type (if address (elpher-address-type address) nil))
+ (type-map-entry (cdr (assoc type elpher-type-map))))
+ (if type-map-entry
+ (let* ((margin-code (elt type-map-entry 2))
+ (face (elt type-map-entry 3))
+ (node (elpher-make-node display-string address)))
+ (elpher-insert-margin margin-code)
+ (insert-text-button display-string
+ 'face face
+ 'elpher-node node
+ 'action #'elpher-click-link
+ 'follow-link t
+ 'help-echo (elpher-node-button-help node)))
+ (pcase type
+ ((or '(gopher ?i) 'nil) ;; Information
+ (elpher-insert-margin)
+ (let ((propertized-display-string
+ (propertize display-string 'face 'elpher-info)))
+ (insert (if elpher-buttonify-urls-in-directories
+ (elpher-buttonify-urls propertized-display-string)
+ propertized-display-string))))
+ (`(gopher ,selector-type) ;; Unknown
+ (elpher-insert-margin (concat (char-to-string selector-type) "?"))
+ (insert (propertize display-string
+ 'face 'elpher-unknown)))))
+ (insert "\n")))
+
+(defun elpher-click-link (button)
+ "Function called when the gopher link BUTTON is activated (via mouse or keypress)."
+ (let ((node (button-get button 'elpher-node)))
+ (elpher-visit-node node)))
+
+(defun elpher-render-index (data &optional _mime-type-string)
+ "Render DATA as an index. MIME-TYPE-STRING is unused."
+ (elpher-with-clean-buffer
+ (if (not data)
+ t
+ (elpher-insert-index data)
+ (elpher-cache-content (elpher-node-address elpher-current-node)
+ (buffer-string)))))