;;; electric-case.el --- insert camelCase, snake_case words without "Shift"ing ;; Copyright (C) 2013-2015 zk_phi ;; 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 2 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, write to the Free Software ;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ;; Version: 2.2.2 ;; Package-Version: 20150417.412 ;; Author: zk_phi ;; URL: http://hins11.yu-yake.com/ ;;; Commentary: ;; Load this script ;; ;; (require 'electric-case) ;; ;; and initialize in major-mode hooks. ;; ;; (add-hook 'java-mode-hook 'electric-case-java-init) ;; ;; And when you type the following in java-mode for example, ;; ;; public class test-class{ ;; public void test-method(void){ ;; ;; =electric-case= automatically converts it into : ;; ;; public class TestClass{ ;; public void testMethod(void){ ;; ;; Preconfigured settings for some other languages are also ;; provided. Try: ;; ;; (add-hook 'c-mode-hook electric-case-c-init) ;; (add-hook 'ahk-mode-hook electric-case-ahk-init) ;; (add-hook 'scala-mode-hook electric-case-scala-init) ;; ;; For more informations, see Readme.org. ;;; Change Log: ;; 1.0.0 first released ;; 1.0.1 fixed java settings ;; 1.0.2 minor fixes ;; 1.0.3 fixed java settings ;; 1.0.4 fixed java settings ;; 1.0.5 fixed C settings ;; 1.1.0 added electric-case-convert-calls ;; 1.1.1 modified arguments for criteria function ;; 1.1.2 added ahk-mode settings ;; 1.1.3 added scala-mode settings, and refactord ;; 1.1.4 fixes and improvements ;; 2.0.0 added pending-overlays ;; 2.0.1 added electric-case-trigger to post-command-hook ;; deleted variable "convert-calls" ;; 2.0.2 minow fixes for criterias ;; 2.0.3 removed electric-case-trigger from post-command-hook ;; 2.0.4 fixed trigger and added hook again ;; 2.1.0 added 2 custom variables, minor fixes ;; 2.1.1 added 2 custom variables ;; 2.2.0 changed behavior ;; now only symbols overlayd are converted ;; 2.2.1 fixed bug that words without overlay may converted ;; 2.2.2 fixed bug that electric-case-convert-end is ignored ;;; Code: (eval-when-compile (require 'cl)) ;; * constants (defconst electric-case-version "2.2.2") ;; * customs (defgroup electric-case nil "Insert camelCase, snake_case words without \"Shift\"ing" :group 'emacs) (defcustom electric-case-pending-overlay 'shadow "Face used to highlight pending symbols" :group 'electric-case) (defcustom electric-case-convert-calls nil "When nil, only declarations are converted." :group 'electric-case) (defcustom electric-case-convert-nums nil "When non-nil, hyphens around numbers are also counted as a part of the symbol." :group 'electric-case) (defcustom electric-case-convert-beginning nil "When non-nil, hyphens at the beginning of symbols are also counted as a part of the symbol." :group 'electric-case) (defcustom electric-case-convert-end nil "When non-nil, hyphens at the end of symbols are also counted as a part of the symbol." :group 'electric-case) ;; * mode variables (define-minor-mode electric-case-mode "insert camelCase, snake_case words without \"Shift\"ing" :init-value nil :lighter "eCase" :global nil (if electric-case-mode (add-hook 'post-command-hook 'electric-case--post-command-function nil t) (remove-hook 'post-command-hook 'electric-case--post-command-function t))) ;; * buffer-local variables (defvar electric-case-criteria (lambda (b e) 'camel)) (make-variable-buffer-local 'electric-case-criteria) (defvar electric-case-max-iteration 1) (make-variable-buffer-local 'electric-case-max-iteration) ;; * utilities ;; ** motion (defun electric-case--range (n) (save-excursion (let* ((pos (point)) (beg (ignore-errors (dotimes (_ n) (when (bobp) (error "beginning of buffer")) (backward-word) (if electric-case-convert-nums (skip-chars-backward "[:alnum:]-") (skip-chars-backward "[:alpha:]-")) (unless electric-case-convert-beginning (skip-chars-forward "-"))) (point))) (end (when beg (goto-char beg) (if electric-case-convert-nums (skip-chars-forward "[:alnum:]-") (skip-chars-forward "[:alpha:]-")) (unless electric-case-convert-end (skip-chars-backward "-")) (point)))) ;; inside-lo|ng-symbol => nil ;; b p e (when (and end (<= end pos)) (cons beg end))))) ;; ** replace buffer (defun electric-case--replace-buffer (beg end str) "(replace 1 2 \"aa\") buffer-string => aaffer-string" (when (not (string= (buffer-substring-no-properties beg end) str)) (let ((pos (point)) (oldlen (- end beg)) (newlen (length str))) (kill-region beg end) (goto-char beg) (insert str) (remove-overlays beg (+ beg newlen)) (goto-char (+ pos (- newlen oldlen)))))) ;; ** overlay management (defvar electric-case--overlays nil) (make-variable-buffer-local 'electric-case--overlays) (defun electric-case--put-overlay (n) (let ((range (electric-case--range n))) (when range (let ((ov (make-overlay (car range) (cdr range)))) (overlay-put ov 'face electric-case-pending-overlay) (add-to-list 'electric-case--overlays ov))))) (defun electric-case--remove-overlays () (mapc 'delete-overlay electric-case--overlays) (setq electric-case--overlays nil)) (defun electric-case--not-on-overlay-p () (let ((res t) (pos (point))) (dolist (ov electric-case--overlays res) (setq res (and res (or (< pos (overlay-start ov)) (< (overlay-end ov) pos))))))) ;; * commands (defun electric-case--convert-all () (dolist (ov electric-case--overlays) (let ((beg (overlay-start ov)) (end (overlay-end ov))) ;; vvv i dont remember why i added whis line vvv (when (string-match "[a-z]" (buffer-substring-no-properties beg end)) (let* ((type (apply electric-case-criteria (list beg end))) (str (buffer-substring-no-properties beg end)) (wlst (split-string str "-")) (convstr (case type ('ucamel (mapconcat (lambda (w) (upcase-initials w)) wlst "")) ('camel (concat (car wlst) (mapconcat (lambda (w) (upcase-initials w)) (cdr wlst) ""))) ('usnake (mapconcat (lambda (w) (upcase w)) wlst "_")) ('snake (mapconcat 'identity wlst "_")) (t nil)))) (when convstr (electric-case--replace-buffer beg end convstr)))))) (electric-case--remove-overlays)) (defun electric-case--post-command-function () ;; update overlay (when (and (eq 'self-insert-command (key-binding (this-single-command-keys))) (characterp last-command-event) (string-match (if electric-case-convert-nums "[a-zA-Z0-9]" "[a-zA-Z]") (char-to-string last-command-event))) (electric-case--remove-overlays) (let (n) (dotimes (n electric-case-max-iteration) (electric-case--put-overlay (- electric-case-max-iteration n))))) ;; electric-case trigger (when (and (electric-case--not-on-overlay-p) (not mark-active)) (electric-case--convert-all))) ;; * settings ;; ** utilities (defun electric-case--possible-properties (beg end) (let* ((ret (point)) (str (buffer-substring beg end)) (convstr (replace-regexp-in-string "-" "" str)) (val (progn (electric-case--replace-buffer beg end convstr) (font-lock-fontify-buffer) (sit-for 0) (text-properties-at beg)))) (electric-case--replace-buffer beg (+ beg (length convstr)) str) (font-lock-fontify-buffer) val)) (defun electric-case--this-line-string () (buffer-substring (save-excursion (beginning-of-line) (point)) (save-excursion (end-of-line) (point)))) ;; ** c-mode (defun electric-case-c-init () (electric-case-mode 1) (setq electric-case-max-iteration 2) (setq electric-case-criteria (lambda (b e) (let ((proper (electric-case--possible-properties b e)) (key (key-description (this-single-command-keys)))) (cond ((member 'font-lock-variable-name-face proper) ;; #ifdef A_MACRO / int variable_name; (if (member '(cpp-macro) (c-guess-basic-syntax)) 'usnake 'snake)) ((member 'font-lock-string-face proper) nil) ((member 'font-lock-comment-face proper) nil) ((member 'font-lock-keyword-face proper) nil) ((member 'font-lock-function-name-face proper) 'snake) ((member 'font-lock-type-face proper) 'snake) (electric-case-convert-calls 'snake) (t nil))))) (defadvice electric-case-trigger (around electric-case-c-try-semi activate) (when (and electric-case-mode (eq major-mode 'c-mode)) (if (not (string= (key-description (this-single-command-keys)) ";")) ad-do-it (insert ";") (backward-char) ad-do-it (delete-char 1)))) ) ;; ** java-mode (defconst electric-case-java-primitives '("boolean" "char" "byte" "short" "int" "long" "float" "double" "void")) (defun electric-case-java-init () (electric-case-mode 1) (setq electric-case-max-iteration 2) (setq electric-case-criteria (lambda (b e) ;; do not convert primitives (when (not (member (buffer-substring b e) electric-case-java-primitives)) (let ((proper (electric-case--possible-properties b e)) (str (electric-case--this-line-string))) (cond ((string-match "^import" str) ;; import java.util.ArrayList; (if (= (char-before) ?\;) 'ucamel nil)) ;; annotation ((save-excursion (goto-char b) (and (not (= (point) (point-min))) (= (char-before) ?@))) 'camel) ((member 'font-lock-string-face proper) nil) ((member 'font-lock-comment-face proper) nil) ((member 'font-lock-keyword-face proper) nil) ((member 'font-lock-type-face proper) 'ucamel) ((member 'font-lock-function-name-face proper) 'camel) ((member 'font-lock-variable-name-face proper) 'camel) (electric-case-convert-calls 'camel) (t nil)))))) (defadvice electric-case-trigger (around electric-case-java-try-semi activate) (when (and electric-case-mode (eq major-mode 'java-mode)) (if (not (string= (key-description (this-single-command-keys)) ";")) ad-do-it (insert ";") (backward-char) ad-do-it (delete-char 1)))) ) ;; ** scala-mode (defun electric-case-scala-init () (electric-case-mode 1) (setq electric-case-max-iteration 2) (setq electric-case-criteria (lambda (b e) (when (not (member (buffer-substring b e) electric-case-java-primitives)) (let ((proper (electric-case--possible-properties b e))) (cond ((member 'font-lock-string-face proper) nil) ((member 'font-lock-comment-face proper) nil) ((member 'font-lock-keyword-face proper) nil) ((member 'font-lock-type-face proper) 'ucamel) ((member 'font-lock-function-name-face proper) 'camel) ((member 'font-lock-variable-name-face proper) 'camel) (electric-case-convert-calls 'camel) (t nil)))))) ) ;; ** ahk-mode (defun electric-case-ahk-init () (electric-case-mode 1) (setq electric-case-max-iteration 1) (setq electric-case-criteria (lambda (b e) (let ((proper (electric-case--possible-properties b e))) (cond ((member 'font-lock-string-face proper) nil) ((member 'font-lock-comment-face proper) nil) ((member 'font-lock-keyword-face proper) 'ucamel) (electric-case-convert-calls 'camel) (t nil))))) ) ;; * provide (provide 'electric-case) ;;; electric-case.el ends here