Skip to content

Commit 2d85f51

Browse files
committed
Add skeletons for types & statements (closes #31)
These skeletons insert common types and statements using mnemonic keybinding. The "C-c C-t" prefix is used for types and the "C-c C-k" prefix is used for keywords. Some of the statement skeletons ("if", "elsif", "else" & "unless") wrap around the region if it is defined. Turning an existing block of code into a conditional block of code is now an easy task with braces and indentation handled automatically.
1 parent ab25cf3 commit 2d85f51

File tree

3 files changed

+670
-14
lines changed

3 files changed

+670
-14
lines changed

README.md

+34-14
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Features
2323
4. Manual validation and linting of manifests (see [Flycheck][] for on-the-fly
2424
validation and linting)
2525
5. Integration with [Puppet Debugger][]
26+
6. Skeletons for many standard Puppet statements and resource declarations
2627

2728
Installation
2829
------------
@@ -48,20 +49,39 @@ manifests with the extension `.pp`.
4849

4950
The following key bindings are available in Puppet Mode:
5051

51-
Key | Command
52-
-------------------|--------------------------------------------------
53-
<kbd>C-M-a</kbd> | Move to the beginning of the current block
54-
<kbd>C-M-e</kbd> | Move to the end of the current block
55-
<kbd>C-c C-a</kbd> | Align parameters in the current block
56-
<kbd>C-c C-'</kbd> | Toggle string quoting between single and double
57-
<kbd>C-c C-;</kbd> | Blank the string at point
58-
<kbd>C-c C-j</kbd> | Jump to a `class`, `define`, variable or resource
59-
<kbd>C-c C-c</kbd> | Apply the current manifest in dry-run mode
60-
<kbd>C-c C-v</kbd> | Validate the syntax of the current manifest
61-
<kbd>C-c C-l</kbd> | Check the current manifest for semantic issues
62-
<kbd>C-c C-z</kbd> | Launch a puppet-debugger REPL
63-
<kbd>C-c C-r</kbd> | Send the currently marked region to the REPL
64-
<kbd>C-c C-b</kbd> | Send the current buffer to the REPL
52+
Key | Command
53+
---------------------|--------------------------------------------------
54+
<kbd>C-M-a</kbd> | Move to the beginning of the current block
55+
<kbd>C-M-e</kbd> | Move to the end of the current block
56+
<kbd>C-c C-a</kbd> | Align parameters in the current block
57+
<kbd>C-c C-'</kbd> | Toggle string quoting between single and double
58+
<kbd>C-c C-;</kbd> | Blank the string at point
59+
<kbd>C-c C-j</kbd> | Jump to a `class`, `define`, variable or resource
60+
<kbd>C-c C-c</kbd> | Apply the current manifest in dry-run mode
61+
<kbd>C-c C-v</kbd> | Validate the syntax of the current manifest
62+
<kbd>C-c C-l</kbd> | Check the current manifest for semantic issues
63+
<kbd>C-c C-z</kbd> | Launch a puppet-debugger REPL
64+
<kbd>C-c C-r</kbd> | Send the currently marked region to the REPL
65+
<kbd>C-c C-b</kbd> | Send the current buffer to the REPL
66+
<kbd>C-c C-k c</kbd> | Insert `class` definition skeleton
67+
<kbd>C-c C-k d</kbd> | Insert `define` definition skeleton
68+
<kbd>C-c C-k n</kbd> | Insert `node` definition skeleton
69+
<kbd>C-c C-k i</kbd> | Insert `if` statement skeleton
70+
<kbd>C-c C-k e</kbd> | Insert `elsif` statement skeleton
71+
<kbd>C-c C-k o</kbd> | Insert `else` statement skeleton
72+
<kbd>C-c C-k u</kbd> | Insert `unless` statement skeleton
73+
<kbd>C-c C-k s</kbd> | Insert `case` statement skeleton
74+
<kbd>C-c C-k ?</kbd> | Insert `selector` statement skeleton
75+
<kbd>C-c C-t a</kbd> | Insert `anchor` resource skeleton
76+
<kbd>C-c C-t c</kbd> | Insert `class` resource skeleton
77+
<kbd>C-c C-t e</kbd> | Insert `exec` resource skeleton
78+
<kbd>C-c C-t f</kbd> | Insert `file` resource skeleton
79+
<kbd>C-c C-t g</kbd> | Insert `group` resource skeleton
80+
<kbd>C-c C-t h</kbd> | Insert `host` resource skeleton
81+
<kbd>C-c C-t n</kbd> | Insert `notify` resource skeleton
82+
<kbd>C-c C-t p</kbd> | Insert `package` resource skeleton
83+
<kbd>C-c C-t s</kbd> | Insert `service` resource skeleton
84+
<kbd>C-c C-t u</kbd> | Insert `user` resource skeleton
6585

6686

6787
For the integration with puppet-debugger to work, the puppet-debugger gem needs

puppet-mode.el

+208
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ buffer-local wherever it is set."
105105
(declare-function pkg-info-version-info "pkg-info" (library))
106106

107107
(eval-when-compile
108+
(require 'cl-macs)
109+
(require 'skeleton)
108110
(require 'rx))
109111

110112
(require 'align)
@@ -1127,6 +1129,189 @@ With a prefix argument SUPPRESS it simply inserts $."
11271129
(delete-region (+ min 1) (- max 1)))))
11281130

11291131

1132+
1133+
;;; Skeletons
1134+
1135+
(defun puppet-dissect-filename (file)
1136+
"Return list of path components for the Puppet manifest FILE.
1137+
The first element of the list will be the module name and the
1138+
remaining elements are the relative path components below the
1139+
‘manifests’ subdirectory. The names of the path components are
1140+
only derived from the file name by using the Puppet auto-loader
1141+
rules. FILE must be an absolute file name.
1142+
1143+
The module name \"unidentified\" is returned if a module name
1144+
can't be inferred from the file name.
1145+
1146+
If the directory name contains characters that are not legal for
1147+
a Puppet module name, then all leading characters including the
1148+
last illegal character are removed from the module name. The
1149+
function will for example return ‘foo’ as module name even if the
1150+
module is using the ‘puppet-foo’ directory (e.g. for module
1151+
development in a user's home directory)."
1152+
(if (stringp file)
1153+
(let* ((parts (cl-loop for path = file then (directory-file-name
1154+
(file-name-directory path))
1155+
;; stop iteration at the root of the directory
1156+
;; tree (should work for Windows & Unix/Linux)
1157+
until (or (string-suffix-p ":" path)
1158+
(string-equal (file-name-directory path)
1159+
path))
1160+
collect (file-name-base path)))
1161+
;; Remove "init" if it is the first element
1162+
(compact (if (string-equal (car parts) "init")
1163+
(cdr parts)
1164+
parts)))
1165+
(cons
1166+
;; module name with illegal prefixes removed or "unidentified" if
1167+
;; path is not compliant with the standard Puppet file hierarchy
1168+
(replace-regexp-in-string
1169+
"^.*[^a-z0-9_]" "" (or (cadr (member "manifests" parts))
1170+
"unidentified"))
1171+
;; remaining path components
1172+
(cdr (member "manifests" (reverse compact)))))
1173+
'("unidentified")))
1174+
1175+
(defun puppet-file-module-name (file)
1176+
"Return the module name for the Puppet class in FILE."
1177+
(car (puppet-dissect-filename file)))
1178+
1179+
(defun puppet-file-class-name (file)
1180+
"Return the class name for the Puppet class in FILE."
1181+
(mapconcat #'identity (puppet-dissect-filename file) "::"))
1182+
1183+
(define-skeleton puppet-keyword-class
1184+
"Insert \"class\" skeleton."
1185+
nil
1186+
"class " (puppet-file-class-name (buffer-file-name)) " (" > \n
1187+
") {" > \n
1188+
> _ "}" > \n)
1189+
1190+
(define-skeleton puppet-keyword-define
1191+
"Insert \"class\" skeleton."
1192+
nil
1193+
"define " (puppet-file-class-name (buffer-file-name)) " (" > \n
1194+
") {" > \n
1195+
> _ "}" > \n)
1196+
1197+
(define-skeleton puppet-keyword-node
1198+
"Insert \"node\" skeleton."
1199+
nil
1200+
"node " > - " {" \n
1201+
> _ "}" > \n)
1202+
1203+
(define-skeleton puppet-keyword-if
1204+
"Insert \"if\" statement."
1205+
nil
1206+
"if " > - " {" \n
1207+
> _ "}" > \n)
1208+
1209+
(define-skeleton puppet-keyword-elsif
1210+
"Insert \"elsif\" statement."
1211+
nil
1212+
"elsif " > - " {" \n
1213+
> _ "}" > \n)
1214+
1215+
(define-skeleton puppet-keyword-else
1216+
"Insert \"else\" statement."
1217+
nil
1218+
"else {" > \n
1219+
> _ "}" > \n)
1220+
1221+
(define-skeleton puppet-keyword-unless
1222+
"Insert \"unless\" statement."
1223+
nil
1224+
"unless " > - " {" \n
1225+
> _ "}" > \n)
1226+
1227+
(define-skeleton puppet-keyword-case
1228+
"Insert \"case\" statement."
1229+
nil
1230+
"case " > - " {" \n
1231+
"default: {" > \n
1232+
"}" > \n
1233+
"}" > \n)
1234+
1235+
(define-skeleton puppet-keyword-selector
1236+
"Insert \"?\" selector."
1237+
nil
1238+
"? {" > \n
1239+
"default => " > - "," \n
1240+
"}" > \n)
1241+
1242+
(define-skeleton puppet-type-anchor
1243+
"Insert the \"anchor\" resource type."
1244+
nil
1245+
"anchor { " > - ": }" \n)
1246+
1247+
(define-skeleton puppet-type-class
1248+
"Insert the \"class\" resource type."
1249+
nil
1250+
"class { " > - ":" \n
1251+
"}" > \n)
1252+
1253+
(define-skeleton puppet-type-exec
1254+
"Insert the \"exec\" resource type."
1255+
nil
1256+
"exec { " > - ":" \n
1257+
"path => [ '/bin', '/sbin', '/usr/bin', '/usr/sbin', ]," > \n
1258+
"user => 'root'," > \n
1259+
"cwd => '/'," > \n
1260+
"}" > \n)
1261+
1262+
(define-skeleton puppet-type-file
1263+
"Insert the \"file\" resource type."
1264+
nil
1265+
"file { " > - ":" \n
1266+
"ensure => file," > \n
1267+
"owner => 'root'," > \n
1268+
"group => 'root'," > \n
1269+
"mode => '0644'," > \n
1270+
"}" > \n)
1271+
1272+
(define-skeleton puppet-type-group
1273+
"Insert the \"group\" resource type."
1274+
nil
1275+
"group { " > - ":" \n
1276+
"ensure => present," > \n
1277+
"}" > \n)
1278+
1279+
(define-skeleton puppet-type-host
1280+
"Insert the \"host\" resource type."
1281+
nil
1282+
"host { " > - ":" \n
1283+
"ensure => present," > \n
1284+
"}" > \n)
1285+
1286+
(define-skeleton puppet-type-notify
1287+
"Insert the \"notify\" resource type."
1288+
nil
1289+
"notify { " > - ": }" \n)
1290+
1291+
(define-skeleton puppet-type-package
1292+
"Insert the \"package\" resource type."
1293+
nil
1294+
"package { " > - ":" \n
1295+
"ensure => present," > \n
1296+
"}" > \n)
1297+
1298+
(define-skeleton puppet-type-service
1299+
"Insert the \"service\" resource type."
1300+
nil
1301+
"service { " > - ":" \n
1302+
"ensure => running," > \n
1303+
"enable => true," > \n
1304+
"}" > \n)
1305+
1306+
(define-skeleton puppet-type-user
1307+
"Insert the \"user\" resource type."
1308+
nil
1309+
"user { " > - ":" \n
1310+
"ensure => present," > \n
1311+
"shell => '/bin/bash'," > \n
1312+
"password => '*'," > \n
1313+
"}" > \n)
1314+
11301315

11311316
;;; Imenu
11321317

@@ -1220,6 +1405,27 @@ for each entry."
12201405
;; Linting and validation
12211406
(define-key map (kbd "C-c C-v") #'puppet-validate)
12221407
(define-key map (kbd "C-c C-l") #'puppet-lint)
1408+
;; Skeletons for types
1409+
(define-key map (kbd "C-c C-t a") #'puppet-type-anchor)
1410+
(define-key map (kbd "C-c C-t c") #'puppet-type-class)
1411+
(define-key map (kbd "C-c C-t e") #'puppet-type-exec)
1412+
(define-key map (kbd "C-c C-t f") #'puppet-type-file)
1413+
(define-key map (kbd "C-c C-t g") #'puppet-type-group)
1414+
(define-key map (kbd "C-c C-t h") #'puppet-type-host)
1415+
(define-key map (kbd "C-c C-t n") #'puppet-type-notify)
1416+
(define-key map (kbd "C-c C-t p") #'puppet-type-package)
1417+
(define-key map (kbd "C-c C-t s") #'puppet-type-service)
1418+
(define-key map (kbd "C-c C-t u") #'puppet-type-user)
1419+
;; Skeletons for keywords
1420+
(define-key map (kbd "C-c C-k c") #'puppet-keyword-class)
1421+
(define-key map (kbd "C-c C-k d") #'puppet-keyword-define)
1422+
(define-key map (kbd "C-c C-k n") #'puppet-keyword-node)
1423+
(define-key map (kbd "C-c C-k i") #'puppet-keyword-if)
1424+
(define-key map (kbd "C-c C-k e") #'puppet-keyword-elsif)
1425+
(define-key map (kbd "C-c C-k o") #'puppet-keyword-else)
1426+
(define-key map (kbd "C-c C-k u") #'puppet-keyword-unless)
1427+
(define-key map (kbd "C-c C-k s") #'puppet-keyword-case)
1428+
(define-key map (kbd "C-c C-k ?") #'puppet-keyword-selector)
12231429
;; The menu bar
12241430
(easy-menu-define puppet-menu map "Puppet Mode menu"
12251431
`("Puppet"
@@ -1278,6 +1484,8 @@ for each entry."
12781484
;; Alignment
12791485
(setq align-mode-rules-list puppet-mode-align-rules)
12801486
(setq align-mode-exclude-rules-list puppet-mode-align-exclude-rules)
1487+
;; Skeletons
1488+
(setq-local skeleton-end-newline nil)
12811489
;; IMenu
12821490
(setq imenu-create-index-function #'puppet-imenu-create-index))
12831491

0 commit comments

Comments
 (0)