Ensure certificate directory exists
[elpher.git] / elpher.el
index b2bc1c5..5b0f132 100644 (file)
--- a/elpher.el
+++ b/elpher.el
 (require 'url-util)
 (require 'subr-x)
 (require 'dns)
-(require 'ansi-color)
 (require 'nsm)
 (require 'gnutls)
 
+;;; ANSI colors or XTerm colors
+
+(or (require 'xterm-color nil t)
+    (require 'ansi-color))
+
+(defalias 'elpher-color-filter-apply
+  (if (fboundp 'xterm-color-filter)
+      (lambda (s)
+       (let ((xterm-color-render nil))
+         (xterm-color-filter s)))
+    'ansi-color-filter-apply)
+  "A function to filter out ANSI escape sequences.")
+(defalias 'elpher-color-apply
+  (if (fboundp 'xterm-color-filter)
+      'xterm-color-filter
+    'ansi-color-apply)
+  "A function to apply ANSI escape sequences.")
 
 ;;; Global constants
 ;;
@@ -513,8 +529,9 @@ unless NO-HISTORY is non-nil."
                                           '("gophers" "gemini")))
                              " [TLS encryption]"
                            ""))
-             (header (concat display-string
-                             (propertize tls-string 'face 'bold))))
+             (header (url-unhex-string
+                     (concat display-string
+                              (propertize tls-string 'face 'bold)))))
         (setq header-line-format header))))
 
 (defmacro elpher-with-clean-buffer (&rest args)
@@ -809,6 +826,8 @@ base for the installed key and certificate files."
 
 (defun elpher-list-existing-certificates ()
   "Return a list of the persistent certificates in `elpher-certificate-directory'."
+  (unless (file-directory-p elpher-certificate-directory)
+    (make-directory elpher-certificate-directory))
   (mapcar
    (lambda (file)
      (file-name-sans-extension file))
@@ -934,7 +953,7 @@ If ADDRESS is not supplied or nil the record is rendered as an
     (if type-map-entry
         (let* ((margin-code (elt type-map-entry 2))
                (face (elt type-map-entry 3))
-               (filtered-display-string (ansi-color-filter-apply display-string))
+               (filtered-display-string (elpher-color-filter-apply display-string))
                (page (elpher-make-page filtered-display-string address)))
           (elpher-insert-margin margin-code)
           (insert-text-button filtered-display-string
@@ -999,8 +1018,8 @@ If ADDRESS is not supplied or nil the record is rendered as an
   "Perform any desired processing of STRING prior to display as text.
 Currently includes buttonifying URLs and processing ANSI escape codes."
   (elpher-buttonify-urls (if elpher-filter-ansi-from-text
-                             (ansi-color-filter-apply string)
-                           (ansi-color-apply string))))
+                             (elpher-color-filter-apply string)
+                           (elpher-color-apply string))))
 
 (defun elpher-render-text (data &optional _mime-type-string)
   "Render DATA as text.  MIME-TYPE-STRING is unused."
@@ -1352,7 +1371,7 @@ treatment that a separate function is warranted."
       (insert elpher-gemini-link-string)
       (if type-map-entry
           (let* ((face (elt type-map-entry 3))
-                 (filtered-display-string (ansi-color-filter-apply display-string))
+                 (filtered-display-string (elpher-color-filter-apply display-string))
                  (page (elpher-make-page filtered-display-string address)))
             (insert-text-button filtered-display-string
                                 'face face
@@ -1368,17 +1387,20 @@ treatment that a separate function is warranted."
 The gemini map file line describing the header is given
 by HEADER-LINE."
   (when (string-match "^\\(#+\\)[ \t]*" header-line)
-    (let ((level (length (match-string 1 header-line)))
-          (header (substring header-line (match-end 0))))
+    (let* ((level (length (match-string 1 header-line)))
+           (header (substring header-line (match-end 0)))
+          (face (pcase level
+                   (1 'elpher-gemini-heading1)
+                   (2 'elpher-gemini-heading2)
+                   (3 'elpher-gemini-heading3)
+                   (_ 'default)))
+          (fill-column (/ (* fill-column
+                             (font-get (font-spec :name (face-font 'default)) :size))
+                          (font-get (font-spec :name (face-font face)) :size))))
       (unless (display-graphic-p)
         (insert (make-string level ?#) " "))
-      (insert (propertize header 'face
-                          (pcase level
-                            (1 'elpher-gemini-heading1)
-                            (2 'elpher-gemini-heading2)
-                            (3 'elpher-gemini-heading3)
-                            (_ 'default)))
-              "\n"))))
+      (insert (propertize header 'face face))
+      (newline))))
 
 (defun elpher-gemini-insert-text (text-line)
   "Insert a plain non-preformatted TEXT-LINE into a text/gemini document.
@@ -2026,6 +2048,7 @@ With an optional argument, add all the history items, too."
     (define-key map "\C-m" 'elpher-menu-this-window)
     (define-key map "o" 'elpher-menu-other-window)
     (define-key map "\C-o" 'elpher-menu-switch-other-window)
+    (define-key map "c" 'elpher-menu-copy-current-url)
     (define-key map "d" 'Buffer-menu-delete)
     (define-key map "k" 'Buffer-menu-delete)
     (define-key map "\C-k" 'Buffer-menu-delete)
@@ -2169,6 +2192,23 @@ and apply FUNC to ARGS."
          (t
           (error "There's no entry on this line of the menu")))))
 
+(defun elpher-menu-copy-current-url ()
+  "Copy the URL of the current menu item."
+  (interactive)
+  (let ((data (tabulated-list-get-id)))
+    (cond ((bufferp data)
+          (with-current-buffer data
+            (elpher-copy-page-url elpher-current-page)))
+         ((listp data)
+          (elpher-copy-page-url (nth 2 data)))
+         (t
+          (error "There's no entry on this line of the menu")))))
+
+  (defun elpher-copy-current-url ()
+  "Copy URL of current page to `kill-ring'."
+  (interactive)
+  (elpher-copy-page-url elpher-current-page))
+
 (defvar elpher-title nil)
 
 (defun elpher-find-title ()
@@ -2192,8 +2232,8 @@ that there are no buffers for history items so many of the buffer
 menu commands won't work on them."
     (setq tabulated-list-format
          (vector '("T" 1 t)
-                 '("URL" 40 t)
-                 '("Name" 30 t))
+                 '("Name" 30 t)
+                 '("URL" 40 t))
          tabulated-list-sort-key nil)
     ;; Collect info for each buffer we're interested in.
     (let (entries)
@@ -2217,25 +2257,25 @@ list established by `elpher-menu-refresh'. See
   (list (current-buffer)
        (cond ((eq major-mode 'elpher-mode)
               (vector "G"
-                      (or (elpher-address-to-url
-                           (elpher-page-address elpher-current-page))
-                          "none")
                       (or (elpher-find-title)
                           (elpher-page-display-string elpher-current-page)
-                          (buffer-name))))
+                          (buffer-name))
+                      (or (elpher-address-to-url
+                           (elpher-page-address elpher-current-page))
+                          "none")))
              ((eq major-mode 'gemini-mode)
               (vector "E"
+                      (or (elpher-page-display-string elpher-current-page)
+                          (buffer-name))
                       (or (elpher-address-to-url
                            (elpher-page-address elpher-current-page))
-                          "none")
-                      (or (elpher-page-display-string elpher-current-page)
-                          (buffer-name))))
+                          "none")))
              ((eq major-mode 'eww-mode)
               (vector "W"
-                      (or (eww-current-url)
-                          "none")
                       (or (plist-get eww-data :title)
-                          (buffer-name)))))))
+                          (buffer-name))
+                      (or (eww-current-url)
+                          "none"))))))
 
 (defun elpher-menu-refresh-history ()
   "Return current entries for `elpher-menu-refresh'.
@@ -2262,9 +2302,9 @@ list established by `elpher-menu-refresh'. See
                     (if (cdr pair)
                         (list (car pair)
                               (vector "G"
+                                      (or (elpher-page-display-string (cdr pair)) "?")
                                       (or (elpher-address-to-url
-                                           (elpher-page-address (cdr pair))) "none")
-                                      (or (elpher-page-display-string (cdr pair)) "?")))
+                                           (elpher-page-address (cdr pair))) "none")))
                       separator))
                   (cons (cons (current-buffer) elpher-current-page)
                         (mapcar (lambda (page)
@@ -2275,10 +2315,10 @@ list established by `elpher-menu-refresh'. See
           ;; No history means a list of one item. Add a separator.
           (list (list (current-buffer)
                       (vector "E"
-                              (or (elpher-address-to-url
-                                   (elpher-page-address elpher-current-page)))
                               (or (elpher-page-display-string elpher-current-page)
-                                  (buffer-name))))
+                                  (buffer-name))
+                              (or (elpher-address-to-url
+                                   (elpher-page-address elpher-current-page)))))
                 separator))
           ((eq major-mode 'eww-mode)
           ;; A pair is (BUFFER-OR-DATA . PAGE) where BUFFER-OR-DTA is
@@ -2287,14 +2327,14 @@ list established by `elpher-menu-refresh'. See
           ;; what function to call. Add the separator at the end.
            (nconc (cons (list (current-buffer)
                               (vector "W"
-                                      (or (plist-get eww-data :url) "none")
-                                      (or (plist-get eww-data :title) "none")))
+                                      (or (plist-get eww-data :title) "none")
+                                      (or (plist-get eww-data :url) "none")))
                         (mapcar (lambda (data)
                                   (list
                                    (list (current-buffer) 'eww-restore-history data)
                                    (vector "W"
-                                           (or (plist-get data :url) "none")
-                                           (or (plist-get data :title) "none"))))
+                                           (or (plist-get data :title) "none")
+                                           (or (plist-get data :url) "none"))))
                                 eww-history))
                   (list separator))))))