gph-mode.el (6509B)
1 ;;; gph-mode.el --- major mode for gph files -*- lexical-binding: t; -*- 2 3 ;; Copyright (C) Troels Henriksen (athas@sigkill.dk) 2022 4 ;; 5 ;; URL: gopher://sigkill.dk/1/programming 6 ;; Keywords: gopher 7 ;; Version: 1.1 8 ;; Package-Requires: ((emacs "25.1")) 9 10 ;; This file is not part of GNU Emacs. 11 12 ;;; License: 13 ;; GPL-3+ 14 15 ;;; Commentary: 16 ;; .gph is the map file format used by the geomyidae Gopher daemon. 17 ;; This Emacs mode provides basic understanding of the link syntax, 18 ;; such that highlighting and folding works properly. It also 19 ;; highlights tab characters in pink because these are not allowed in 20 ;; .gph files. 21 ;; 22 ;; Files with the ".gph" extension are automatically handled by this mode. 23 ;; 24 ;; For extensions: Define local keybindings in `gph-mode-map'. Add 25 ;; startup functions to `gph-mode-hook'. 26 27 ;;; Code: 28 29 (eval-when-compile 30 (require 'rx)) 31 32 (defface gph-tabs-face 33 '((((class color)) (:background "hotpink")) 34 (t (:reverse-video t))) 35 "Face to use for highlighting tabs in Font-Lock mode.") 36 37 (defvar gph-tabs 'gph-tabs-face 38 "Face to use for highlighting tabs in Font-Lock mode.") 39 40 (defvar gph--font-lock-defaults 41 (let* ((type-rx '(or "0" "1" "3" "7" "8" "9" "g" "I" "h" "i")) 42 (desc-rx '(* (not "|"))) 43 (path-rx '(* (not "|"))) 44 (host-rx '(* (not "|"))) 45 (port-rx '(+ digit)) 46 (link-rx `(: line-start "[" ,type-rx "|" ,desc-rx "|" ,path-rx "|" ,host-rx "|" ,port-rx "]")) 47 (badlink-rx `(: line-start "[" (* anything)))) 48 `((,(rx-to-string link-rx) 0 font-lock-doc-markup-face) 49 (,(rx-to-string badlink-rx) 0 font-lock-warning-face) 50 ("\t" 0 gph-tabs)))) 51 52 (defvar gph-mode-hook nil 53 "Hook for `gph-mode'. Is run whenever the mode is entered.") 54 55 (defvar gph-mode-map 56 (let ((map (make-keymap))) 57 map) 58 "Keymap for `gph-mode'.") 59 60 ;;;###autoload 61 (add-to-list 'auto-mode-alist '("\\.gph" . gph-mode)) 62 63 ;;;###autoload 64 (define-derived-mode gph-mode text-mode "gph" 65 "Major mode for .gph files as used by geomyidae." 66 (setq-local paragraph-start (concat "^\\[|\\|[ \t]*$\\|" page-delimiter)) 67 (setq-local paragraph-separate (concat "^\\[\\|[ \t]*$\\|" page-delimiter)) 68 (setq-local font-lock-defaults '(gph--font-lock-defaults))) 69 70 71 (defun gph-insert-link (c label path server port) 72 73 "Easily insert a gopher link. You will be prompted for: 74 - C: a character representing the link type (see list below) 75 - LABEL: a string to be used as the link label 76 - PATH: path to the item you wish to link to 77 - SERVER: the domain of the server (defaults to 'server') 78 - PORT: port to use (defaults to '70') 79 80 the possible options for C are: 81 key type gopher equivalent 82 - f file 0 83 - d directory 1 84 - b binary 9 85 - g gif g 86 - I image I 87 - s search 7 88 - h http h 89 - t telnet 8 90 - e error 3 91 - i info i 92 93 the resulting link looks something like: 94 [0|my file|./file.txt|example.com|70] 95 96 see also `gph-generate-link', which converts a gopher url to a link" 97 98 (interactive "c[f]ile [d]ir [b]inary [g]if [I]mage [s]earch [h]ttp [t]elnet [e]rror [i]nfo: \nMLabel: \nMPath: \nsServer (server): \nsPort (70): ") 99 100 (cond ((char-equal c ?f) 101 (insert "[0|")) 102 ((char-equal c ?d) 103 (insert "[1|")) 104 ((char-equal c ?b) 105 (insert "[9|")) 106 ((char-equal c ?g) 107 (insert "[g|")) 108 ((char-equal c ?I) 109 (insert "[I|")) 110 ((char-equal c ?s) 111 (insert "[7|")) 112 ((char-equal c ?h) 113 (insert "[h|")) 114 ((char-equal c ?t) 115 (insert "[8|")) 116 ((char-equal c ?e) 117 (insert "[3|")) 118 ((char-equal c ?i) 119 (insert "[i|"))) 120 (insert (concat label "|" path "|" 121 (if (string-equal server "") 122 "server" server) 123 "|" 124 (if (string-equal port "") 125 "70" port) 126 "]"))) 127 128 ;; these are some functions that break up a url 129 ;; for when automagically transforming a gopher:// url 130 ;; into a proper geomyidae link 131 132 (defun gph--remove-proto (url) 133 "Remove 'gopher://' from a URL string." 134 (string-trim url "gopher://" "")) 135 136 (defun gph--get-domain (url) 137 "Return only the domain name of a URL." 138 (car (string-split (gph--remove-proto url) "/"))) 139 140 (defun gph--get-path (url) 141 "Return only the resource path of the URL" 142 (string-trim-left 143 (string-remove-prefix (gph--get-domain url) 144 (gph--remove-proto url)) 145 "/[^z-a]")) 146 147 (defun gph--get-type (url) 148 "Attempt to get the link type of the URL (like 0 for file, 1 for dir etc)." 149 (caddr (string-split 150 (string-remove-prefix (gph--get-domain url) 151 (gph--remove-proto url)) 152 ""))) 153 154 (defun gph-url-to-link (url label) 155 "Convert a gopher:// or http:// url to the geomyidae link format. 156 this function prompts you for a URL, then for a link label. it tries to 157 parse the URL you provide and output a correct link to that resource. 158 159 The URL tries to default to whatever is in your clipboard. I don't think 160 this works in non-graphical environments. 161 162 gopher://example.com/0/some/file.txt 163 becomes 164 [0|my label|/some/file.txt|example.com|70]" 165 166 ;; this interactive funciton is kinda big but it works okay 167 (interactive (let* ((selection (string-truncate-left (gui-get-selection) 70)) 168 (prompt (if (gui-get-selection) 169 (format "URL (%s): " selection) 170 "URL: ")) 171 (url (read-string prompt nil nil selection nil)) 172 (label (read-string "Label: " nil nil nil nil))) 173 (list url label))) 174 175 (if (or (string-prefix-p "gopher://" url 1) 176 (string-prefix-p "gophers://" url 1)) 177 (insert (concat 178 "[" (gph--get-type url) 179 "|" label 180 "|" (gph--get-path url) 181 "|" (gph--get-domain url) 182 "|70]")) 183 184 ;; else, it's probably an http:// link 185 (insert (concat 186 "[h|" label "|URL:" url "|server|70]")))) 187 188 ;;gph-mode-map 189 (define-key gph-mode-map (kbd "C-c C-,") 'gph-insert-link) 190 (define-key gph-mode-map (kbd "C-c C-l") 'gph-url-to-link) 191 192 ;; add a reminder about these bindings in the minibuffer 193 (add-hook 'gph-mode-hook 194 (lambda () 195 (message "[C-c C-,] insert link. [C-c C-l] convert URL to link. "))) 196 197 (provide 'gph-mode) 198 199 ;;; gph-mode.el ends here