1 ;;; gnome-c-snippet.el --- GNOME-style code generation -*- lexical-binding: t; -*-
2 ;; Copyright (C) 2016 Free Software Foundation, Inc.
4 ;; Author: Daiki Ueno <ueno@gnu.org>
5 ;; Keywords: GNOME, C, coding style
7 ;; This file is part of GNU Emacs.
9 ;; GNU Emacs is free software: you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation, either version 3 of the License, or
12 ;; (at your option) any later version.
14 ;; GNU Emacs is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
24 ;; FIXME: The snippets defined here could be rewritten in yasnippet
28 (require 'gnome-c-align)
31 (defvar gnome-c-snippet-package nil)
32 (make-variable-buffer-local 'gnome-c-snippet-package)
34 (defvar gnome-c-snippet-class nil)
35 (make-variable-buffer-local 'gnome-c-snippet-class)
37 (defvar gnome-c-snippet-parent-package nil)
38 (make-variable-buffer-local 'gnome-c-snippet-parent-package)
40 (defvar gnome-c-snippet-parent-class nil)
41 (make-variable-buffer-local 'gnome-c-snippet-parent-class)
43 (defconst gnome-c-snippet-guess-name-functions
44 '(gnome-c-snippet--guess-name-from-header-buffer
45 gnome-c-snippet--guess-name-from-declaration
46 gnome-c-snippet--guess-name-from-file-name))
48 (defcustom gnome-c-snippet-align-arglist t
49 "Whether to align argument list of the inserted snippet"
51 :group 'gnome-c-style)
53 (make-variable-buffer-local 'gnome-c-snippet-align-arglist)
55 (defun gnome-c-snippet--find-declaration ()
58 (goto-char (point-min))
59 (when (re-search-forward
60 "^G_DECLARE_\\(?:FINAL\\|DERIVABLE\\)_TYPE\\s-*("
62 (setq beg (match-beginning 0))
63 (goto-char (match-end 0))
73 (defun gnome-c-snippet--extract-names-from-declaration (beg end)
75 (narrow-to-region beg end)
76 (goto-char (point-min))
78 (c-forward-syntactic-ws)
79 (let ((capitalized-package-class
80 (buffer-substring-no-properties (point)
83 (c-backward-syntactic-ws)
85 uppercased-package uppercased-class
86 capitalized-package capitalized-class)
87 (c-forward-syntactic-ws)
89 (setq uppercased-package (split-string
90 (buffer-substring (point)
93 (c-backward-syntactic-ws)
96 (c-forward-syntactic-ws)
98 (setq uppercased-class (split-string
99 (buffer-substring (point)
102 (c-backward-syntactic-ws)
107 (dolist (uppercased uppercased-package)
108 (let* ((length (length uppercased))
110 (substring capitalized-package-class
111 index (+ index length))))
112 (unless (equal (upcase capitalized) uppercased)
114 (push capitalized capitalized-package)
115 (setq index (+ index length))))
116 (dolist (uppercased uppercased-class)
117 (let* ((length (length uppercased))
119 (substring capitalized-package-class
120 index (+ index length))))
121 (unless (equal (upcase capitalized) uppercased)
123 (push capitalized capitalized-class)
124 (setq index (+ index length))))))
125 (list (nreverse capitalized-package)
126 (nreverse capitalized-class)))))
128 (defun gnome-c-snippet--find-header-buffer ()
129 (when (equal (file-name-extension buffer-file-name) "c")
130 (let ((header-file-name
131 (concat (file-name-sans-extension buffer-file-name) ".h")))
134 (with-current-buffer buffer
135 (equal buffer-file-name header-file-name)))
138 (defun gnome-c-snippet--guess-name-from-header-buffer (symbol)
139 (let ((header-buffer (gnome-c-snippet--find-header-buffer)))
141 (with-current-buffer header-buffer
142 (symbol-value (intern (format "gnome-c-snippet-%S" symbol)))))))
144 (defun gnome-c-snippet--guess-name-from-declaration (symbol)
145 (when (memq symbol '(package class))
146 (let ((header-buffer (gnome-c-snippet--find-header-buffer)))
148 (with-current-buffer header-buffer
149 (let ((region (gnome-c-snippet--find-declaration))
153 (apply #'gnome-c-snippet--extract-names-from-declaration
157 ('package (car names))
158 ('class (nth 1 names)))))))))))
160 (defun gnome-c-snippet--guess-name-from-file-name (symbol)
161 (when (memq symbol '(package class))
162 (let ((filename (file-name-sans-extension
163 (file-name-nondirectory buffer-file-name))))
164 (when (string-match-p "-" filename)
165 (let ((names (split-string filename "-")))
167 ('package (list (upcase-initials (car names))))
168 ('class (mapcar #'upcase-initials (cdr names)))))))))
170 (defun gnome-c-snippet--parse-name (name)
173 (insert (upcase-initials name))
174 (goto-char (point-min))
176 ;; Skip characters not recognized by subword-mode.
177 (if (looking-at "[^[:lower:][:upper:][:digit:]]+")
178 (goto-char (match-end 0)))
179 (push (buffer-substring (point) (progn (subword-forward 1)
184 (defun gnome-c-snippet--read-name (prompt symbol &optional default)
185 (when (or current-prefix-arg
186 (not (symbol-value symbol)))
188 (gnome-c-snippet--parse-name
190 (or (if (symbol-value symbol)
191 (gnome-c-snippet--format-Package
192 (symbol-value symbol)))
194 (symbol-value symbol))
196 (defun gnome-c-snippet--read-package-and-class (parent)
197 (append (list (gnome-c-snippet--read-name
198 "Package (CamelCase): "
199 'gnome-c-snippet-package
200 (gnome-c-snippet--format-Package
201 (run-hook-with-args-until-success
202 'gnome-c-snippet-guess-name-functions
204 (gnome-c-snippet--read-name
205 "Class (CamelCase): "
206 'gnome-c-snippet-class
207 (gnome-c-snippet--format-Class
208 (run-hook-with-args-until-success
209 'gnome-c-snippet-guess-name-functions
212 (list (gnome-c-snippet--read-name
213 "Parent package (CamelCase): "
214 'gnome-c-snippet-parent-package
215 (gnome-c-snippet--format-Package
216 (run-hook-with-args-until-success
217 'gnome-c-snippet-guess-name-functions
219 (gnome-c-snippet--read-name
220 "Parent class (CamelCase): "
221 'gnome-c-snippet-parent-class
222 (gnome-c-snippet--format-Class
223 (run-hook-with-args-until-success
224 'gnome-c-snippet-guess-name-functions
227 (defun gnome-c-snippet--read-package-and-interface (parent)
228 (list (gnome-c-snippet--read-name
229 "Package (CamelCase): "
230 'gnome-c-snippet-package
231 (gnome-c-snippet--format-Package
232 (run-hook-with-args-until-success
233 'gnome-c-snippet-guess-name-functions
235 (gnome-c-snippet--read-name
236 "Interface (CamelCase): "
237 'gnome-c-snippet-class
238 (gnome-c-snippet--format-Class
239 (run-hook-with-args-until-success
240 'gnome-c-snippet-guess-name-functions
243 (list (gnome-c-snippet--read-name
244 "Parent package (CamelCase): "
245 'gnome-c-snippet-parent-package
246 (gnome-c-snippet--format-Package
247 (run-hook-with-args-until-success
248 'gnome-c-snippet-guess-name-functions
250 (gnome-c-snippet--read-name
251 "Parent class (CamelCase): "
252 'gnome-c-snippet-parent-class
253 (gnome-c-snippet--format-Class
254 (run-hook-with-args-until-success
255 'gnome-c-snippet-guess-name-functions
258 (defun gnome-c-snippet--format-PACKAGE (package)
259 (mapconcat #'upcase package "_"))
260 (defalias 'gnome-c-snippet--format-CLASS 'gnome-c-snippet--format-PACKAGE)
262 (defun gnome-c-snippet--format-PACKAGE_CLASS (package class)
263 (concat (gnome-c-snippet--format-PACKAGE package)
265 (gnome-c-snippet--format-CLASS class)))
267 (defun gnome-c-snippet--format-package (package)
268 (mapconcat #'downcase package "_"))
269 (defalias 'gnome-c-snippet--format-class 'gnome-c-snippet--format-package)
271 (defun gnome-c-snippet--format-package_class (package class)
272 (concat (gnome-c-snippet--format-package package)
274 (gnome-c-snippet--format-class class)))
276 (defun gnome-c-snippet--format-Package (package)
277 (mapconcat #'identity package ""))
278 (defalias 'gnome-c-snippet--format-Class 'gnome-c-snippet--format-Package)
280 (defun gnome-c-snippet--format-PackageClass (package class)
281 (concat (gnome-c-snippet--format-Package package)
282 (gnome-c-snippet--format-Class class)))
285 (defun gnome-c-snippet-insert-package_class (package class)
286 "Insert the class name before the current point."
287 (interactive (gnome-c-snippet--read-package-and-class nil))
288 (insert (gnome-c-snippet--format-package_class package class)))
291 (defun gnome-c-snippet-insert-PACKAGE_CLASS (package class)
292 "Insert the class name before the current point."
293 (interactive (gnome-c-snippet--read-package-and-class nil))
294 (insert (gnome-c-snippet--format-PACKAGE_CLASS package class)))
297 (defun gnome-c-snippet-insert-PackageClass (package class)
298 "Insert the class name (in CamelCase) before the current point."
299 (interactive (gnome-c-snippet--read-package-and-class nil))
300 (insert (gnome-c-snippet--format-PackageClass package class)))
302 (defun gnome-c-snippet-insert-interface-declaration (package iface
303 parent-package parent-class)
304 "Insert interface declaration for PACKAGE and IFACE"
305 (interactive (gnome-c-snippet--read-package-and-interface t))
307 #define " (gnome-c-snippet--format-PACKAGE package) "_TYPE_" (gnome-c-snippet--format-CLASS iface) " (" (gnome-c-snippet--format-package package) "_" (gnome-c-snippet--format-class iface) "_get_type ())
308 G_DECLARE_INTERFACE (" (gnome-c-snippet--format-PackageClass package iface) ", "
309 (gnome-c-snippet--format-package_class package iface) ", " (gnome-c-snippet--format-PACKAGE package) ", " (gnome-c-snippet--format-CLASS iface) ", " (gnome-c-snippet--format-PackageClass parent-package parent-class) ")
312 (defun gnome-c-snippet--insert-class-declaration (package
318 #define " (gnome-c-snippet--format-PACKAGE package) "_TYPE_" (gnome-c-snippet--format-CLASS class) " (" (gnome-c-snippet--format-package_class package class) "_get_type ())
319 G_DECLARE_" (if derivable "DERIVABLE" "FINAL") "_TYPE (" (gnome-c-snippet--format-PackageClass package class) ", "
320 (gnome-c-snippet--format-package_class package class) ", " (gnome-c-snippet--format-PACKAGE package) ", " (gnome-c-snippet--format-CLASS class) ", " (gnome-c-snippet--format-PackageClass parent-package parent-class) ")
323 (defun gnome-c-snippet-insert-final-class-declaration (package
327 "Insert final class declaration for PACKAGE and CLASS."
328 (interactive (gnome-c-snippet--read-package-and-class t))
329 (gnome-c-snippet--insert-class-declaration package
335 (defun gnome-c-snippet-insert-derivable-class-declaration (package
339 "Insert derivable class declaration for PACKAGE and CLASS."
340 (interactive (gnome-c-snippet--read-package-and-class t))
341 (gnome-c-snippet--insert-class-declaration package
347 (defun gnome-c-snippet-insert-interface-definition (package
351 "Insert class definition for PACKAGE and CLASS."
352 (interactive (gnome-c-snippet--read-package-and-interface t))
355 " (gnome-c-snippet--format-package_class package iface) "_default_init (" (gnome-c-snippet--format-PackageClass package iface) "Interface *iface) {
358 G_DEFINE_INTERFACE (" (gnome-c-snippet--format-PackageClass package iface) ", "
359 (gnome-c-snippet--format-package_class package iface) ", " (gnome-c-snippet--format-PACKAGE parent-package) "_TYPE_" (gnome-c-snippet--format-CLASS parent-class) ")
362 (defun gnome-c-snippet--insert-class-definition (package
368 G_DEFINE_" (if abstract "ABSTRACT_" "") "TYPE (" (gnome-c-snippet--format-PackageClass package class) ", "
369 (gnome-c-snippet--format-package_class package class) ", " (gnome-c-snippet--format-PACKAGE parent-package) "_TYPE_" (gnome-c-snippet--format-CLASS parent-class) ")
372 " (gnome-c-snippet--format-package_class package class) "_class_init (" (gnome-c-snippet--format-PackageClass package class) "Class *klass)
377 " (gnome-c-snippet--format-package_class package class) "_init (" (gnome-c-snippet--format-PackageClass package class) " *self)
382 (defun gnome-c-snippet-insert-class-definition (package
386 "Insert class definition for PACKAGE and CLASS."
387 (interactive (gnome-c-snippet--read-package-and-class t))
388 (gnome-c-snippet--insert-class-definition package
394 (defun gnome-c-snippet-insert-abstract-class-definition (package
398 "Insert abstract class definition for PACKAGE and CLASS."
399 (interactive (gnome-c-snippet--read-package-and-class t))
400 (gnome-c-snippet--insert-class-definition package
406 (defun gnome-c-snippet-insert-constructor (package class)
407 "Insert 'constructor' vfunc of GObjectClass for PACKAGE and CLASS."
408 (interactive (gnome-c-snippet--read-package-and-class nil))
409 (let (arglist-start body-start)
412 " (gnome-c-snippet--format-package_class package class) "_constructor (")
413 (setq arglist-start (point-marker))
414 (insert "GType *object,
415 guint n_construct_properties,
416 GObjectConstructParam *construct_properties)\n")
417 (setq body-start (point-marker))
418 (if gnome-c-snippet-align-arglist
420 (goto-char arglist-start)
421 (gnome-c-align-arglist-at-point))
422 (indent-region arglist-start (point)))
423 (goto-char body-start)
425 " (gnome-c-snippet--format-PackageClass package class) " *self = "
426 (gnome-c-snippet--format-PACKAGE_CLASS package class) " (object);
428 G_OBJECT_CLASS (" (gnome-c-snippet--format-package_class package class) "_parent_class)->constructor (type, n_construct_properties, construct_properties);
431 (indent-region body-start (point))))
433 (defun gnome-c-snippet-insert-set_property (package class)
434 "Insert 'set_property' vfunc of GObjectClass for PACKAGE and CLASS."
435 (interactive (gnome-c-snippet--read-package-and-class nil))
436 (let (arglist-start body-start)
439 " (gnome-c-snippet--format-package_class package class) "_set_property (")
440 (setq arglist-start (point-marker))
441 (insert "GObject *object,
444 GParamSpec *pspec)\n")
445 (setq body-start (point-marker))
446 (if gnome-c-snippet-align-arglist
448 (goto-char arglist-start)
449 (gnome-c-align-arglist-at-point))
450 (indent-region arglist-start (point)))
451 (goto-char body-start)
453 " (gnome-c-snippet--format-PackageClass package class) " *self = "
454 (gnome-c-snippet--format-PACKAGE_CLASS package class) " (object);
459 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
464 (indent-region body-start (point))))
466 (defun gnome-c-snippet-insert-get_property (package class)
467 "Insert 'get_property' vfunc of GObjectClass for PACKAGE and CLASS."
468 (interactive (gnome-c-snippet--read-package-and-class nil))
469 (let (arglist-start body-start)
472 " (gnome-c-snippet--format-package_class package class) "_get_property (")
473 (setq arglist-start (point-marker))
474 (insert "GObject *object,
477 GParamSpec *pspec)\n")
478 (setq body-start (point-marker))
479 (if gnome-c-snippet-align-arglist
481 (goto-char arglist-start)
482 (gnome-c-align-arglist-at-point))
483 (indent-region arglist-start (point)))
484 (goto-char body-start)
486 " (gnome-c-snippet--format-PackageClass package class) " *self = "
487 (gnome-c-snippet--format-PACKAGE_CLASS package class) " (object);
492 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
497 (indent-region body-start (point))))
499 (defun gnome-c-snippet-insert-dispose (package class)
500 "Insert 'dispose' vfunc of GObjectClass for PACKAGE and CLASS."
501 (interactive (gnome-c-snippet--read-package-and-class nil))
505 " (gnome-c-snippet--format-package_class package class) "_dispose (GObject *object)\n")
506 (setq body-start (point-marker))
508 " (gnome-c-snippet--format-PackageClass package class) " *self = "
509 (gnome-c-snippet--format-PACKAGE_CLASS package class) " (object);
511 G_OBJECT_CLASS (" (gnome-c-snippet--format-package_class package class) "_parent_class)->dispose (object);
514 (indent-region body-start (point))))
516 (defun gnome-c-snippet-insert-finalize (package class)
517 "Insert 'finalize' vfunc of GObjectClass for PACKAGE and CLASS."
518 (interactive (gnome-c-snippet--read-package-and-class nil))
522 " (gnome-c-snippet--format-package_class package class) "_finalize (GObject *object)\n")
523 (setq body-start (point-marker))
525 " (gnome-c-snippet--format-PackageClass package class) " *self = "
526 (gnome-c-snippet--format-PACKAGE_CLASS package class) " (object);
528 G_OBJECT_CLASS (" (gnome-c-snippet--format-package_class package class) "_parent_class)->finalize (object);
531 (indent-region body-start (point))))
533 (defun gnome-c-snippet-insert-dispatch_properties_changed (package class)
534 "Insert 'dispatch_properties_changed vfunc of GObjectClass for
536 (interactive (gnome-c-snippet--read-package-and-class nil))
537 (let (arglist-start body-start)
540 " (gnome-c-snippet--format-package_class package class) "_dispatch_properties_changed (")
541 (setq arglist-start (point-marker))
542 (insert "GObject *object,
544 GParamSpec **pspecs)\n")
545 (setq body-start (point-marker))
546 (if gnome-c-snippet-align-arglist
548 (goto-char arglist-start)
549 (gnome-c-align-arglist-at-point))
550 (indent-region arglist-start (point)))
551 (goto-char body-start)
553 " (gnome-c-snippet--format-PackageClass package class) " *self = "
554 (gnome-c-snippet--format-PACKAGE_CLASS package class) " (object);
556 G_OBJECT_CLASS (" (gnome-c-snippet--format-package_class package class) "_parent_class)->dispatch_properties_changed (object, n_pspecs, pspecs);
559 (indent-region body-start (point))))
561 (defun gnome-c-snippet-insert-notify (package class)
562 "Insert 'notify' vfunc of GObjectClass for PACKAGE and CLASS."
563 (interactive (gnome-c-snippet--read-package-and-class nil))
564 (let (arglist-start body-start)
567 " (gnome-c-snippet--format-package_class package class) "_notify (")
568 (setq arglist-start (point-marker))
569 (insert "GObject *object,
570 GParamSpec *pspec)\n")
571 (setq body-start (point-marker))
572 (if gnome-c-snippet-align-arglist
574 (goto-char arglist-start)
575 (gnome-c-align-arglist-at-point))
576 (indent-region arglist-start (point)))
578 " (gnome-c-snippet--format-PackageClass package class) " *self = "
579 (gnome-c-snippet--format-PACKAGE_CLASS package class) " (object);
581 G_OBJECT_CLASS (" (gnome-c-snippet--format-package_class package class) "_parent_class)->notify (object, pspec);
584 (indent-region body-start (point))))
586 (defun gnome-c-snippet-insert-constructed (package class)
587 "Insert 'constructed' vfunc of GObjectClass for PACKAGE and CLASS."
588 (interactive (gnome-c-snippet--read-package-and-class nil))
592 " (gnome-c-snippet--format-package_class package class) "_constructed (GObject *object)\n")
593 (setq body-start (point-marker))
595 " (gnome-c-snippet--format-PackageClass package class) " *self = "
596 (gnome-c-snippet--format-PACKAGE_CLASS package class) " (object);
598 G_OBJECT_CLASS (" (gnome-c-snippet--format-package_class package class) "_parent_class)->constructed (object);
601 (indent-region body-start (point))))
603 (defvar gnome-c-snippet-snippet-commands
604 '(("G_DECLARE_INTERFACE" . gnome-c-snippet-insert-interface-declaration)
605 ("G_DECLARE_FINAL_TYPE" . gnome-c-snippet-insert-final-class-declaration)
606 ("G_DECLARE_DERIVABLE_TYPE" .
607 gnome-c-snippet-insert-derivable-class-declaration)
608 ("G_DEFINE_INTERFACE" . gnome-c-snippet-insert-interface-definition)
609 ("G_DEFINE_TYPE" . gnome-c-snippet-insert-class-definition)
610 ("G_DEFINE_ABSTRACT_TYPE" .
611 gnome-c-snippet-insert-abstract-class-definition)
612 ("GObjectClass.constructor" . gnome-c-snippet-insert-constructor)
613 ("GObjectClass.set_property" . gnome-c-snippet-insert-set_property)
614 ("GObjectClass.get_property" . gnome-c-snippet-insert-get_property)
615 ("GObjectClass.dispose" . gnome-c-snippet-insert-dispose)
616 ("GObjectClass.finalize" . gnome-c-snippet-insert-finalize)
617 ("GObjectClass.dispatch_properties_changed" .
618 gnome-c-snippet-insert-dispatch_properties_changed)
619 ("GObjectClass.notify" . gnome-c-snippet-insert-notify)
620 ("GObjectClass.constructed" . gnome-c-snippet-insert-constructed)))
623 (defun gnome-c-snippet-insert (snippet)
625 (list (completing-read "Snippet: " gnome-c-snippet-snippet-commands nil t)))
626 (let ((entry (assoc snippet gnome-c-snippet-snippet-commands)))
628 (error "Unknown snippet: %s" snippet))
629 (call-interactively (cdr entry))))
631 (provide 'gnome-c-snippet)
633 ;;; gnome-c-snippet.el ends here