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.

327 lines
9.9 KiB

  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. ;;;###autoload
  170. (define-derived-mode ssh-config-mode
  171. fundamental-mode
  172. "ssh-config"
  173. "Major mode for fontifiying ssh config files.
  174. \\{ssh-config-mode-map}"
  175. (setq
  176. comment-start "#"
  177. comment-end "")
  178. (make-local-variable 'font-lock-defaults)
  179. (setq
  180. font-lock-defaults '(ssh-config-font-lock-keywords nil t))
  181. ;;
  182. (setq-local indent-line-function 'ssh-config-indent-line)
  183. (setq-local imenu-generic-expression ssh-config-imenu-generic-expression))
  184. ;;;###autoload
  185. (progn
  186. (add-to-list 'auto-mode-alist '("/\\.ssh/config\\'" . ssh-config-mode))
  187. (add-to-list 'auto-mode-alist '("/sshd?_config\\'" . ssh-config-mode))
  188. (add-to-list 'auto-mode-alist '("/known_hosts\\'" . ssh-known-hosts-mode))
  189. (add-to-list 'auto-mode-alist '("/authorized_keys\\'" . ssh-authorized-keys-mode)))
  190. ;;;;;
  191. (defvar ssh-known-hosts-mode-hook nil
  192. "*Hook to setup `ssh-config-mode'.")
  193. (defvar ssh-known-hosts-mode-map
  194. (let ((map (make-sparse-keymap)))
  195. ;; Ctrl bindings
  196. map)
  197. "The local keymap for `ssh-known-hosts-mode'.")
  198. (defvar ssh-known-hosts-mode-syntax-table
  199. (let ((table (make-syntax-table)))
  200. (modify-syntax-entry ?# "<" table)
  201. (modify-syntax-entry ?\n ">" table)
  202. table)
  203. "Syntax table for `ssh-known-hosts-mode'.
  204. Just sets the comment syntax.")
  205. ;; NOTE: font-lock-studio might be of help when making changes.
  206. (defvar ssh-known-hosts-font-lock-keywords
  207. ;; We want to match lines like the following:
  208. ;; Short list in ./tests/known_hosts_short
  209. ;; Full list in ./tests/known_hosts
  210. `(
  211. (,(concat
  212. "^"
  213. ;; @marker (optional):
  214. "\\(@[-a-z]+ +\\|\\)"
  215. ;; hostnames & hashes:
  216. ;; host.example.com,1.1.1.1
  217. ;; |1|hash|hash|
  218. ;; Just checking for chars, not parsing it.
  219. "\\("
  220. "[-0-9A-Za-z|=.,:*/+]+"
  221. "\\)"
  222. "[ \t]+"
  223. ;; key type:
  224. ;; ssh-rsa, ecdsa-sha2-nistp384, ...
  225. "\\(\\(?:ecdsa\\|ssh\\)[-0-9A-Za-z]*\\)"
  226. "[ \t]+"
  227. ;; public key:
  228. ;; base64data==
  229. "\\(AA[0-9A-Za-z/+]+=*\\)"
  230. )
  231. (1 font-lock-warning-face)
  232. (2 font-lock-function-name-face)
  233. (3 font-lock-keyword-face)
  234. (4 font-lock-string-face)
  235. ))
  236. "Expressions to hilight in `ssh-known-hosts-mode'.")
  237. ;;;###autoload
  238. (defun ssh-known-hosts-mode ()
  239. "Major mode for fontifiying ssh known_hosts files.
  240. \\{ssh-known-hosts-mode}"
  241. (interactive)
  242. (kill-all-local-variables)
  243. (set-syntax-table ssh-known-hosts-mode-syntax-table)
  244. (setq
  245. mode-name "ssh-known-hosts"
  246. major-mode 'ssh-known-hosts-mode
  247. comment-start "#"
  248. comment-end "")
  249. (use-local-map ssh-known-hosts-mode-map)
  250. ;;
  251. (make-local-variable 'font-lock-defaults)
  252. (setq font-lock-defaults '(ssh-known-hosts-font-lock-keywords))
  253. ;;
  254. (run-hooks 'ssh-known-hosts-mode-hook)
  255. nil)
  256. ;;;;;
  257. ;;;###autoload (autoload 'ssh-authorized-keys-mode "ssh-config-mode" nil t)
  258. (define-generic-mode ssh-authorized-keys-mode
  259. '(?\#)
  260. nil
  261. (eval-when-compile
  262. (list
  263. (list
  264. (concat
  265. ;; ignore options
  266. ;; double quoted string will be fontified by generic mode.
  267. ;; key type
  268. "\\(\\(?:ecdsa\\|ssh\\)-[^[:space:]]+\\)\\s-+"
  269. ;; base64
  270. "\\([0-9A-Za-z+/]+=*\\)"
  271. ;; comment in public key
  272. "\\(?: \\(.*\\)\\)?"
  273. "$")
  274. '(1 font-lock-keyword-face)
  275. '(2 font-lock-string-face)
  276. '(3 font-lock-comment-face nil t)
  277. )))
  278. ;; Not define `auto-mode-alist' obey the other mode in this elisp.
  279. nil
  280. nil)
  281. ;; done loading
  282. (run-hooks 'ssh-config-mode-load-hook)
  283. (provide 'ssh-config-mode)
  284. ;;; ssh-config-mode.el ends here