Personal emacs config
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

346 lines
11 KiB

5 years ago
5 years ago
5 years ago
5 years ago
  1. ;;; ssh-config-mode.el --- Mode for fontification of ~/.ssh/config
  2. ;;
  3. ;; ssh-config-mode-el/ssh-config-mode.el ---
  4. ;;
  5. ;; $Id: ssh-config-mode.el,v 1.14 2012/05/14 05:29:26 harley Exp $
  6. ;;
  7. ;; Author: Harley Gorrell <harley@panix.com>
  8. ;; URL: https://github.com/jhgorrell/ssh-config-mode-el
  9. ;; Github: https://raw.github.com/jhgorrell/ssh-config-mode-el/master/ssh-config-mode.el
  10. ;; License: GPL v3+ (https://www.gnu.org/licenses/gpl-3.0.txt)
  11. ;; Keywords: ssh, config, emacs
  12. ;; Version: $Revision: 1.14 $
  13. ;; Tag: 20170413T0010
  14. ;;; Commentary:
  15. ;; * Fontifys the ssh config keywords.
  16. ;; * keys for skipping from host section to host section.
  17. ;; * Add the following to your startup file.
  18. ;; (autoload 'ssh-config-mode "ssh-config-mode" t)
  19. ;; (add-to-list 'auto-mode-alist '("/\\.ssh/config\\'" . ssh-config-mode))
  20. ;; (add-to-list 'auto-mode-alist '("/sshd?_config\\'" . ssh-config-mode))
  21. ;; (add-to-list 'auto-mode-alist '("/knownhosts\\'" . ssh-known-hosts-mode))
  22. ;; (add-to-list 'auto-mode-alist '("/authorized_keys2?\\'" . ssh-authorized-keys-mode))
  23. ;; (add-hook 'ssh-config-mode-hook 'turn-on-font-lock)
  24. ;;; History:
  25. ;; * This keeps checkdoc happy.
  26. ;;; License
  27. ;;
  28. ;; This file is free software; you can redistribute it and/or modify
  29. ;; it under the terms of the GNU General Public License as published by
  30. ;; the Free Software Foundation; either version 3, or (at your option)
  31. ;; any later version.
  32. ;; This file is distributed in the hope that it will be useful,
  33. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  34. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  35. ;; GNU General Public License for more details.
  36. ;; You should have received a copy of the GNU General Public License
  37. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  38. ;;; Code:
  39. ;; (eval-buffer)
  40. ;; (progn (delete-trailing-whitespace) (indent-region 0 (point-max)))
  41. ;; (byte-compile-file "ssh-config-mode.el" t)
  42. ;; (load "ssh-config-mode")
  43. ;; We use eval-and-compile so we can use them at compile
  44. ;; time and call regexp-opt during compliation.
  45. (eval-and-compile
  46. (defun ssh-config-ssh-config-keywords-path ()
  47. "Find the path to 'ssh-config-keywords.txt'.
  48. It should be next to 'ssh-config-mode.el'.
  49. When testing add '.' to load-path so you find the local copy."
  50. (let ((path (locate-library "ssh-config-keywords.txt" nil)))
  51. ;;(message "ssh-config-keywords.txt is %s" path)
  52. path)))
  53. (eval-and-compile
  54. (defun ssh-config-read-keywords (&optional file-path)
  55. "Read the list of ssh keywords, returning them as a list."
  56. ;; (message "ssh-config-read-keywords")
  57. (let ((file-path
  58. (or file-path
  59. (ssh-config-ssh-config-keywords-path))))
  60. (with-temp-buffer
  61. (insert-file-contents file-path)
  62. (split-string (buffer-string) "\n" t)))))
  63. (eval-and-compile
  64. (defvar ssh-config-keywords
  65. (eval-when-compile
  66. (ssh-config-read-keywords)))
  67. "A list of keywords allowed in a user ssh config file.")
  68. (eval-and-compile
  69. (defvar ssh-config-font-lock-keywords
  70. (eval-when-compile
  71. `((
  72. ,(regexp-opt ssh-config-keywords 'words)
  73. (1 font-lock-keyword-face)
  74. )))
  75. "Expressions to hilight in `ssh-config-mode'."))
  76. ;; ssh-config-font-lock-keywords
  77. ;; Setup
  78. (defvar ssh-config-mode-load-hook nil
  79. "*Hook to run when `ssh-config-mode' is loaded.")
  80. ;;
  81. (defvar ssh-config-mode-hook nil
  82. "*Hook to setup `ssh-config-mode'.")
  83. (defvar ssh-config-host-regexp "^\\s-*Host\\b"
  84. "Regexp to match the start of a host entry.")
  85. (defvar ssh-config-match-regexp "^\\s-*Match\\b"
  86. "Regexp to match the start of a match entry.")
  87. (defvar ssh-config-hostname-regexp
  88. "[-_.a-zA-Z0-9]+"
  89. "Regexp to match one hostname. (rfc1123 2.1).")
  90. (defcustom ssh-config-mode-indent 2
  91. "The width of indentation to use.
  92. By default it's set to 2 as that is what man page
  93. ssh_config(5) shows it as."
  94. :type 'integer
  95. :group 'ssh-config-mode)
  96. (defun ssh-config-host-next ()
  97. "Skip to the next host entry."
  98. (interactive "^")
  99. (search-forward-regexp ssh-config-host-regexp))
  100. (defun ssh-config-host-prev ()
  101. "Skip to the previous host entry."
  102. (interactive "^")
  103. (search-backward-regexp ssh-config-host-regexp))
  104. (defun ssh-config-in-host-block-p ()
  105. "Are we inside a Host block?"
  106. (save-excursion
  107. (search-backward-regexp ssh-config-host-regexp nil t)))
  108. (defun ssh-config-in-match-block-p ()
  109. "Are we inside a Match block?"
  110. (save-excursion
  111. (search-backward-regexp ssh-config-match-regexp nil t)))
  112. (defun ssh-config-compute-indent ()
  113. "Compute the target indent for the current line.
  114. Comments right above a 'Host' are considered to be about that Host."
  115. (save-excursion
  116. (beginning-of-line)
  117. (cond
  118. ;; Start of file and "Host" and "Match" should be at 0
  119. ((or (looking-at ssh-config-host-regexp)
  120. (looking-at ssh-config-match-regexp)
  121. (and (not (ssh-config-in-host-block-p))
  122. (not (ssh-config-in-match-block-p))))
  123. 0)
  124. ;; Comment line
  125. ((looking-at "\\s-*#")
  126. ;; comments right before a "Host" or "Match" should be at 0
  127. (while (looking-at "\\s-*#")
  128. (forward-line))
  129. (if (or (looking-at ssh-config-host-regexp)
  130. (looking-at ssh-config-match-regexp))
  131. 0
  132. ssh-config-mode-indent))
  133. ;; default.
  134. (t
  135. ssh-config-mode-indent))))
  136. (defun ssh-config-indent-line ()
  137. "Indent lines in the SSH config file."
  138. (interactive)
  139. (let ((target (ssh-config-compute-indent)))
  140. (save-excursion
  141. (indent-line-to target))))
  142. ;;
  143. (defvar ssh-config-mode-map
  144. (let ((map (make-sparse-keymap)))
  145. ;; Ctrl bindings
  146. (define-key map [C-down] 'ssh-config-host-next)
  147. (define-key map [C-up] 'ssh-config-host-prev)
  148. ;;
  149. (define-key map "\C-c}" 'ssh-config-host-next)
  150. (define-key map "\C-c{" 'ssh-config-host-prev)
  151. ;;
  152. (define-key map (kbd "TAB") 'indent-for-tab-command)
  153. map)
  154. "The local keymap for `ssh-config-mode'.")
  155. ;;
  156. (defvar ssh-config-mode-syntax-table nil)
  157. (unless ssh-config-mode-syntax-table
  158. (let ((table (make-syntax-table)))
  159. (modify-syntax-entry ?# "<" table)
  160. (modify-syntax-entry ?\n ">" table)
  161. (setq ssh-config-mode-syntax-table table)))
  162. (defvar ssh-config-imenu-generic-expression
  163. `(("Hosts"
  164. ,(concat ssh-config-host-regexp "\\s-+\\(" ssh-config-hostname-regexp "\\)") 1)
  165. ("Matches"
  166. ,(concat ssh-config-match-regexp "\\s-+\\(.*\\)") 1))
  167. "Value for `imenu-generic-expression' in `ssh-config-mode'.
  168. Only show the first hostname in the menu.")
  169. (defun ssh-config-completion-at-point ()
  170. "Function used for `completion-at-point-functions' in `ssh-config-mode'."
  171. (interactive)
  172. (let* (
  173. (bds (bounds-of-thing-at-point 'symbol))
  174. (start (car bds))
  175. (end (cdr bds)))
  176. (list start end ssh-config-keywords . nil )))
  177. ;;;###autoload
  178. (define-derived-mode ssh-config-mode
  179. fundamental-mode
  180. "ssh-config"
  181. "Major mode for fontifiying ssh config files.
  182. \\{ssh-config-mode-map}"
  183. (setq
  184. comment-start "#"
  185. comment-end "")
  186. (make-local-variable 'font-lock-defaults)
  187. (setq
  188. font-lock-defaults '(ssh-config-font-lock-keywords nil t))
  189. ;;
  190. (setq-local indent-line-function 'ssh-config-indent-line)
  191. (setq-local imenu-generic-expression ssh-config-imenu-generic-expression)
  192. (add-hook 'completion-at-point-functions 'ssh-config-completion-at-point nil 'local))
  193. ;;;###autoload
  194. (progn
  195. (add-to-list 'auto-mode-alist '("/\\.ssh/config\\'" . ssh-config-mode))
  196. (add-to-list 'auto-mode-alist '("/sshd?_config\\'" . ssh-config-mode))
  197. (add-to-list 'auto-mode-alist '("/known_hosts\\'" . ssh-known-hosts-mode))
  198. (add-to-list 'auto-mode-alist '("/authorized_keys\\'" . ssh-authorized-keys-mode)))
  199. ;;;;;
  200. (defvar ssh-known-hosts-mode-hook nil
  201. "*Hook to setup `ssh-config-mode'.")
  202. (defvar ssh-known-hosts-mode-map
  203. (let ((map (make-sparse-keymap)))
  204. ;; Ctrl bindings
  205. map)
  206. "The local keymap for `ssh-known-hosts-mode'.")
  207. (defvar ssh-known-hosts-mode-syntax-table
  208. (let ((table (make-syntax-table)))
  209. (modify-syntax-entry ?# "<" table)
  210. (modify-syntax-entry ?\n ">" table)
  211. table)
  212. "Syntax table for `ssh-known-hosts-mode'.
  213. Just sets the comment syntax.")
  214. ;; NOTE: font-lock-studio might be of help when making changes.
  215. (defvar ssh-known-hosts-font-lock-keywords
  216. ;; We want to match lines like the following:
  217. ;; Short list in ./tests/known_hosts_short
  218. ;; Full list in ./tests/known_hosts
  219. `(
  220. (,(concat
  221. "^"
  222. ;; @marker (optional):
  223. "\\(@[-a-z]+ +\\|\\)"
  224. ;; hostnames & hashes:
  225. ;; Just checking for chars, not parsing it.
  226. "\\("
  227. ;; host.example.com,1.1.1.1
  228. ;; |1|hash|hash|
  229. "[-0-9A-Za-z|=.,:*/+]+"
  230. "\\|"
  231. ;; [136.24.83.19]:2222
  232. "\\[[0-9]+.[0-9]+.[0-9]+.[0-9]+\\]:[0-9]+"
  233. "\\|"
  234. ;; fe80::3285:a9ff:fea7:6de3%en0
  235. "[0-9a-f:]+\\(?:%[a-z0-9]+\\)?"
  236. "\\|"
  237. ;; [fe80::3285:a9ff:fea7:6de3%en0]:2222
  238. "\\[[0-9a-f:]+\\(?:%[a-z0-9]+\\)?\\]:[0-9]+"
  239. "\\)"
  240. "[ \t]+"
  241. ;; key type:
  242. ;; ssh-rsa, ecdsa-sha2-nistp384, ...
  243. "\\(\\(?:ecdsa\\|ssh\\)[-0-9A-Za-z]*\\)"
  244. "[ \t]+"
  245. ;; public key:
  246. ;; base64data==
  247. "\\(AA[0-9A-Za-z/+]+=*\\)"
  248. )
  249. (1 font-lock-warning-face)
  250. (2 font-lock-function-name-face)
  251. (3 font-lock-keyword-face)
  252. (4 font-lock-string-face)
  253. ))
  254. "Expressions to hilight in `ssh-known-hosts-mode'.")
  255. ;;;###autoload
  256. (defun ssh-known-hosts-mode ()
  257. "Major mode for fontifiying ssh known_hosts files.
  258. \\{ssh-known-hosts-mode}"
  259. (interactive)
  260. (kill-all-local-variables)
  261. (set-syntax-table ssh-known-hosts-mode-syntax-table)
  262. (setq
  263. mode-name "ssh-known-hosts"
  264. major-mode 'ssh-known-hosts-mode
  265. comment-start "#"
  266. comment-end "")
  267. (use-local-map ssh-known-hosts-mode-map)
  268. ;;
  269. (make-local-variable 'font-lock-defaults)
  270. (setq font-lock-defaults '(ssh-known-hosts-font-lock-keywords))
  271. ;;
  272. (run-hooks 'ssh-known-hosts-mode-hook)
  273. nil)
  274. ;;;;;
  275. ;;;###autoload (autoload 'ssh-authorized-keys-mode "ssh-config-mode" nil t)
  276. (define-generic-mode ssh-authorized-keys-mode
  277. '(?\#)
  278. nil
  279. (eval-when-compile
  280. (list
  281. (list
  282. (concat
  283. ;; ignore options
  284. ;; double quoted string will be fontified by generic mode.
  285. ;; key type
  286. "\\(\\(?:ecdsa\\|ssh\\)-[^[:space:]]+\\)\\s-+"
  287. ;; base64
  288. "\\([0-9A-Za-z+/]+=*\\)"
  289. ;; comment in public key
  290. "\\(?: \\(.*\\)\\)?"
  291. "$")
  292. '(1 font-lock-keyword-face)
  293. '(2 font-lock-string-face)
  294. '(3 font-lock-comment-face nil t)
  295. )))
  296. ;; Not define `auto-mode-alist' obey the other mode in this elisp.
  297. nil
  298. nil)
  299. ;; done loading
  300. (run-hooks 'ssh-config-mode-load-hook)
  301. (provide 'ssh-config-mode)
  302. ;;; ssh-config-mode.el ends here