;;; gnome-c-snippet.el --- GNOME-style code generation -*- lexical-binding: t; -*- ;; Copyright (C) 2016 Free Software Foundation, Inc. ;; Author: Daiki Ueno ;; Keywords: GNOME, C, coding style ;; This file is not part of GNU Emacs. ;; This program is free software: you can redistribute it and/or ;; modify it under the terms of the GNU General Public License as ;; published by the Free Software Foundation, either version 3 of the ;; License, or (at your option) any later version. ;; This program is distributed in the hope that it will be useful, but ;; WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;; General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see ;; . ;;; Commentary: ;; FIXME: The snippets defined here could be rewritten in yasnippet ;;; Code: (require 'gnome-c-align) (eval-when-compile (require 'subword)) (declare-function subword-forward "subword.el" (&optional arg)) (defvar gnome-c-snippet-package nil) (make-variable-buffer-local 'gnome-c-snippet-package) (defvar gnome-c-snippet-class nil) (make-variable-buffer-local 'gnome-c-snippet-class) (defvar gnome-c-snippet-parent-package nil) (make-variable-buffer-local 'gnome-c-snippet-parent-package) (defvar gnome-c-snippet-parent-class nil) (make-variable-buffer-local 'gnome-c-snippet-parent-class) (defcustom gnome-c-snippet-align-arglist t "Whether to align argument list of the inserted snippet" :type 'boolean :group 'gnome-c-style) (make-variable-buffer-local 'gnome-c-snippet-align-arglist) (defun gnome-c-snippet--parse-name (name) (require 'subword) (with-temp-buffer (let (words) (insert name) (goto-char (point-min)) (while (not (eobp)) ;; Skip characters not recognized by subword-mode. (if (looking-at "[^[:lower:][:upper:][:digit:]]+") (goto-char (match-end 0))) (push (buffer-substring (point) (progn (subword-forward 1) (point))) words)) (nreverse words)))) (defun gnome-c-snippet--read-package-and-class (package-prompt class-prompt package-symbol class-symbol) (when (or current-prefix-arg (not (and (symbol-value package-symbol) (symbol-value class-symbol)))) (set package-symbol (gnome-c-snippet--parse-name (read-string (or package-prompt "Package (CamelCase): ") (if (symbol-value package-symbol) (gnome-c-snippet--format-Package (symbol-value package-symbol)))))) (set class-symbol (gnome-c-snippet--parse-name (read-string (or class-prompt "Class (CamelCase): ") (if (symbol-value class-symbol) (gnome-c-snippet--format-Class (symbol-value class-symbol))))))) (list (symbol-value package-symbol) (symbol-value class-symbol))) (defun gnome-c-snippet--format-PACKAGE (package) (mapconcat #'upcase package "_")) (defalias 'gnome-c-snippet--format-CLASS 'gnome-c-snippet--format-PACKAGE) (defun gnome-c-snippet--format-PACKAGE_CLASS (package class) (concat (gnome-c-snippet--format-PACKAGE package) "_" (gnome-c-snippet--format-CLASS class))) (defun gnome-c-snippet--format-package (package) (mapconcat #'downcase package "_")) (defalias 'gnome-c-snippet--format-class 'gnome-c-snippet--format-package) (defun gnome-c-snippet--format-package_class (package class) (concat (gnome-c-snippet--format-package package) "_" (gnome-c-snippet--format-class class))) (defun gnome-c-snippet--format-Package (package) (mapconcat #'identity package "")) (defalias 'gnome-c-snippet--format-Class 'gnome-c-snippet--format-Package) (defun gnome-c-snippet--format-PackageClass (package class) (concat (gnome-c-snippet--format-Package package) (gnome-c-snippet--format-Class class))) ;;;###autoload (defun gnome-c-snippet-insert-package_class (package class) "Insert the class name before the current point." (interactive (gnome-c-snippet--read-package-and-class nil nil 'gnome-c-snippet-package 'gnome-c-snippet-class)) (insert (gnome-c-snippet--format-package_class package class))) ;;;###autoload (defun gnome-c-snippet-insert-PACKAGE_CLASS (package class) "Insert the class name before the current point." (interactive (gnome-c-snippet--read-package-and-class nil nil 'gnome-c-snippet-package 'gnome-c-snippet-class)) (insert (gnome-c-snippet--format-PACKAGE_CLASS package class))) ;;;###autoload (defun gnome-c-snippet-insert-PackageClass (package class) "Insert the class name (in CamelCase) before the current point." (interactive (gnome-c-snippet--read-package-and-class nil nil 'gnome-c-snippet-package 'gnome-c-snippet-class)) (insert (gnome-c-snippet--format-PackageClass package class))) (defun gnome-c-snippet-insert-interface-declaration (package iface parent-package parent-class) "Insert interface declaration for PACKAGE and IFACE" (interactive (append (gnome-c-snippet--read-package-and-class nil "Interface (CamelCase): " 'gnome-c-snippet-package 'gnome-c-snippet-class) (gnome-c-snippet--read-package-and-class "Parent package (CamelCase): " "Parent class (CamelCase): " 'gnome-c-snippet-parent-package 'gnome-c-snippet-parent-class))) (insert "\ #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 ()) G_DECLARE_INTERFACE (" (gnome-c-snippet--format-PackageClass package iface) ", " (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) ") ")) (defun gnome-c-snippet--insert-class-declaration (package class parent-package parent-class derivable) (insert "\ #define " (gnome-c-snippet--format-PACKAGE package) "_TYPE_" (gnome-c-snippet--format-CLASS class) " (" (gnome-c-snippet--format-package_class package class) "_get_type ()) G_DECLARE_" (if derivable "DERIVABLE" "FINAL") "_TYPE (" (gnome-c-snippet--format-PackageClass package class) ", " (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) ") ")) (defun gnome-c-snippet-insert-final-class-declaration (package class parent-package parent-class) "Insert final class declaration for PACKAGE and CLASS." (interactive (append (gnome-c-snippet--read-package-and-class nil nil 'gnome-c-snippet-package 'gnome-c-snippet-class) (gnome-c-snippet--read-package-and-class "Parent package (CamelCase): " "Parent class (CamelCase): " 'gnome-c-snippet-parent-package 'gnome-c-snippet-parent-class))) (gnome-c-snippet--insert-class-declaration package class parent-package parent-class nil)) (defun gnome-c-snippet-insert-derivable-class-declaration (package class parent-package parent-class) "Insert derivable class declaration for PACKAGE and CLASS." (interactive (append (gnome-c-snippet--read-package-and-class nil nil 'gnome-c-snippet-package 'gnome-c-snippet-class) (gnome-c-snippet--read-package-and-class "Parent package (CamelCase): " "Parent class (CamelCase): " 'gnome-c-snippet-parent-package 'gnome-c-snippet-parent-class))) (gnome-c-snippet--insert-class-declaration package class parent-package parent-class t)) (defun gnome-c-snippet-insert-interface-definition (package iface parent-package parent-class) "Insert class definition for PACKAGE and CLASS." (interactive (append (gnome-c-snippet--read-package-and-class nil "Interface (CamelCase): " 'gnome-c-snippet-package 'gnome-c-snippet-class) (gnome-c-snippet--read-package-and-class "Parent package (CamelCase): " "Parent class (CamelCase): " 'gnome-c-snippet-parent-package 'gnome-c-snippet-parent-class))) (insert "\ static void " (gnome-c-snippet--format-package_class package iface) "_default_init (" (gnome-c-snippet--format-PackageClass package iface) "Interface *iface) { } G_DEFINE_INTERFACE (" (gnome-c-snippet--format-PackageClass package iface) ", " (gnome-c-snippet--format-package_class package iface) ", " (gnome-c-snippet--format-PACKAGE parent-package) "_TYPE_" (gnome-c-snippet--format-CLASS parent-class) ") ")) (defun gnome-c-snippet--insert-class-definition (package class parent-package parent-class abstract) (insert "\ G_DEFINE_" (if abstract "ABSTRACT_" "") "TYPE (" (gnome-c-snippet--format-PackageClass package class) ", " (gnome-c-snippet--format-package_class package class) ", " (gnome-c-snippet--format-PACKAGE parent-package) "_TYPE_" (gnome-c-snippet--format-CLASS parent-class) ") static void " (gnome-c-snippet--format-package_class package class) "_class_init (" (gnome-c-snippet--format-PackageClass package class) "Class *klass) { } static void " (gnome-c-snippet--format-package_class package class) "_init (" (gnome-c-snippet--format-PackageClass package class) " *self) { } ")) (defun gnome-c-snippet-insert-class-definition (package class parent-package parent-class) "Insert class definition for PACKAGE and CLASS." (interactive (append (gnome-c-snippet--read-package-and-class nil nil 'gnome-c-snippet-package 'gnome-c-snippet-class) (gnome-c-snippet--read-package-and-class "Parent package (CamelCase): " "Parent class (CamelCase): " 'gnome-c-snippet-parent-package 'gnome-c-snippet-parent-class))) (gnome-c-snippet--insert-class-definition package class parent-package parent-class nil)) (defun gnome-c-snippet-insert-abstract-class-definition (package class parent-package parent-class) "Insert abstract class definition for PACKAGE and CLASS." (interactive (append (gnome-c-snippet--read-package-and-class nil nil 'gnome-c-snippet-package 'gnome-c-snippet-class) (gnome-c-snippet--read-package-and-class "Parent package (CamelCase): " "Parent class (CamelCase): " 'gnome-c-snippet-parent-package 'gnome-c-snippet-parent-class))) (gnome-c-snippet--insert-class-definition package class parent-package parent-class t)) (defun gnome-c-snippet-insert-constructor (package class) "Insert 'constructor' vfunc of GObjectClass for PACKAGE and CLASS." (interactive (gnome-c-snippet--read-package-and-class nil nil 'gnome-c-snippet-package 'gnome-c-snippet-class)) (let (arglist-start body-start) (insert "\ static GObject * " (gnome-c-snippet--format-package_class package class) "_constructor (") (setq arglist-start (point-marker)) (insert "GType *object, guint n_construct_properties, GObjectConstructParam *construct_properties)\n") (setq body-start (point-marker)) (if gnome-c-snippet-align-arglist (progn (goto-char arglist-start) (gnome-c-align-arglist-at-point)) (indent-region arglist-start (point))) (goto-char body-start) (insert "{ " (gnome-c-snippet--format-PackageClass package class) " *self = " (gnome-c-snippet--format-PACKAGE_CLASS package class) " (object); G_OBJECT_CLASS (" (gnome-c-snippet--format-package_class package class) "_parent_class)->constructed (type, n_construct_properties, construct_properties); } ") (indent-region body-start (point)))) (defun gnome-c-snippet-insert-set_property (package class) "Insert 'set_property' vfunc of GObjectClass for PACKAGE and CLASS." (interactive (gnome-c-snippet--read-package-and-class nil nil 'gnome-c-snippet-package 'gnome-c-snippet-class)) (let (arglist-start body-start) (insert "\ static void " (gnome-c-snippet--format-package_class package class) "_set_property (") (setq arglist-start (point-marker)) (insert "GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)\n") (setq body-start (point-marker)) (if gnome-c-snippet-align-arglist (progn (goto-char arglist-start) (gnome-c-align-arglist-at-point)) (indent-region arglist-start (point))) (goto-char body-start) (insert "{ " (gnome-c-snippet--format-PackageClass package class) " *self = " (gnome-c-snippet--format-PACKAGE_CLASS package class) " (object); switch (prop_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } ") (indent-region body-start (point)))) (defun gnome-c-snippet-insert-get_property (package class) "Insert 'get_property' vfunc of GObjectClass for PACKAGE and CLASS." (interactive (gnome-c-snippet--read-package-and-class nil nil 'gnome-c-snippet-package 'gnome-c-snippet-class)) (let (arglist-start body-start) (insert "\ static void " (gnome-c-snippet--format-package_class package class) "_get_property (") (setq arglist-start (point-marker)) (insert "GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)\n") (setq body-start (point-marker)) (if gnome-c-snippet-align-arglist (progn (goto-char arglist-start) (gnome-c-align-arglist-at-point)) (indent-region arglist-start (point))) (goto-char body-start) (insert "{ " (gnome-c-snippet--format-PackageClass package class) " *self = " (gnome-c-snippet--format-PACKAGE_CLASS package class) " (object); switch (prop_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } ") (indent-region body-start (point)))) (defun gnome-c-snippet-insert-dispose (package class) "Insert 'dispose' vfunc of GObjectClass for PACKAGE and CLASS." (interactive (gnome-c-snippet--read-package-and-class nil nil 'gnome-c-snippet-package 'gnome-c-snippet-class)) (let (body-start) (insert "\ static void " (gnome-c-snippet--format-package_class package class) "_dispose (GObject *object)\n") (setq body-start (point-marker)) (insert "{ " (gnome-c-snippet--format-PackageClass package class) " *self = " (gnome-c-snippet--format-PACKAGE_CLASS package class) " (object); G_OBJECT_CLASS (" (gnome-c-snippet--format-package_class package class) "_parent_class)->dispose (object); } ") (indent-region body-start (point)))) (defun gnome-c-snippet-insert-finalize (package class) "Insert 'finalize' vfunc of GObjectClass for PACKAGE and CLASS." (interactive (gnome-c-snippet--read-package-and-class nil nil 'gnome-c-snippet-package 'gnome-c-snippet-class)) (let (body-start) (insert "\ static void " (gnome-c-snippet--format-package_class package class) "_finalize (GObject *object)\n") (setq body-start (point-marker)) (insert "{ " (gnome-c-snippet--format-PackageClass package class) " *self = " (gnome-c-snippet--format-PACKAGE_CLASS package class) " (object); G_OBJECT_CLASS (" (gnome-c-snippet--format-package_class package class) "_parent_class)->finalize (object); } ") (indent-region body-start (point)))) (defun gnome-c-snippet-insert-dispatch_properties_changed (package class) "Insert 'dispatch_properties_changed vfunc of GObjectClass for PACKAGE and CLASS." (interactive (gnome-c-snippet--read-package-and-class nil nil 'gnome-c-snippet-package 'gnome-c-snippet-class)) (let (arglist-start body-start) (insert "\ static void " (gnome-c-snippet--format-package_class package class) "_dispatch_properties_changed (") (setq arglist-start (point-marker)) (insert "GObject *object, guint n_pspecs, GParamSpec **pspecs)\n") (setq body-start (point-marker)) (if gnome-c-snippet-align-arglist (progn (goto-char arglist-start) (gnome-c-align-arglist-at-point)) (indent-region arglist-start (point))) (goto-char body-start) (insert "{ " (gnome-c-snippet--format-PackageClass package class) " *self = " (gnome-c-snippet--format-PACKAGE_CLASS package class) " (object); G_OBJECT_CLASS (" (gnome-c-snippet--format-package_class package class) "_parent_class)->dispatch_properties_changed (object, n_pspecs, pspecs); } ") (indent-region body-start (point)))) (defun gnome-c-snippet-insert-notify (package class) "Insert 'notify' vfunc of GObjectClass for PACKAGE and CLASS." (interactive (gnome-c-snippet--read-package-and-class nil nil 'gnome-c-snippet-package 'gnome-c-snippet-class)) (let (arglist-start body-start) (insert "\ static void " (gnome-c-snippet--format-package_class package class) "_notify (") (setq arglist-start (point-marker)) (insert "GObject *object, GParamSpec *pspec)\n") (setq body-start (point-marker)) (if gnome-c-snippet-align-arglist (progn (goto-char arglist-start) (gnome-c-align-arglist-at-point)) (indent-region arglist-start (point))) (insert "{ " (gnome-c-snippet--format-PackageClass package class) " *self = " (gnome-c-snippet--format-PACKAGE_CLASS package class) " (object); G_OBJECT_CLASS (" (gnome-c-snippet--format-package_class package class) "_parent_class)->notify (object, pspec); } ") (indent-region body-start (point)))) (defun gnome-c-snippet-insert-constructed (package class) "Insert 'constructed' vfunc of GObjectClass for PACKAGE and CLASS." (interactive (gnome-c-snippet--read-package-and-class nil nil 'gnome-c-snippet-package 'gnome-c-snippet-class)) (let (body-start) (insert "\ static void " (gnome-c-snippet--format-package_class package class) "_constructed (GObject *object)\n") (setq body-start (point-marker)) (insert "{ " (gnome-c-snippet--format-PackageClass package class) " *self = " (gnome-c-snippet--format-PACKAGE_CLASS package class) " (object); G_OBJECT_CLASS (" (gnome-c-snippet--format-package_class package class) "_parent_class)->constructed (object); } ") (indent-region body-start (point)))) (defvar gnome-c-snippet-snippet-commands '(("G_DECLARE_INTERFACE" . gnome-c-snippet-insert-interface-declaration) ("G_DECLARE_FINAL_TYPE" . gnome-c-snippet-insert-final-class-declaration) ("G_DECLARE_DERIVABLE_TYPE" . gnome-c-snippet-insert-derivable-class-declaration) ("G_DEFINE_INTERFACE" . gnome-c-snippet-insert-interface-definition) ("G_DEFINE_TYPE" . gnome-c-snippet-insert-class-definition) ("G_DEFINE_ABSTRACT_TYPE" . gnome-c-snippet-insert-abstract-class-definition) ("GObjectClass.constructor" . gnome-c-snippet-insert-constructor) ("GObjectClass.set_property" . gnome-c-snippet-insert-set_property) ("GObjectClass.get_property" . gnome-c-snippet-insert-get_property) ("GObjectClass.dispose" . gnome-c-snippet-insert-dispose) ("GObjectClass.finalize" . gnome-c-snippet-insert-finalize) ("GObjectClass.dispatch_properties_changed" . gnome-c-snippet-insert-dispatch_properties_changed) ("GObjectClass.notify" . gnome-c-snippet-insert-notify) ("GObjectClass.contructed" . gnome-c-snippet-insert-constructed))) ;;;###autoload (defun gnome-c-snippet-insert (snippet) (interactive (list (completing-read "Snippet: " gnome-c-snippet-snippet-commands nil t))) (let ((entry (assoc snippet gnome-c-snippet-snippet-commands))) (unless entry (error "Unknown snippet: %s" snippet)) (call-interactively (cdr entry)))) (provide 'gnome-c-snippet) ;;; gnome-c-snippet.el ends here