;;; smart-tab.el --- Intelligent tab completion and indentation. ;; This file is NOT part of GNU Emacs. ;; Copyright (C) 2009-2019 John SJ Anderson, ;; Sebastien Rocca Serra, ;; Daniel Hackney ;; Author: John SJ Anderson , ;; Sebastien Rocca Serra , ;; Daniel Hackney ;; Maintainer: John SJ Anderson ;; Keywords: extensions ;; Created: 2009-05-24 ;; URL: http://github.com/genehack/smart-tab/tree/master ;; Version: 0.5 ;; ;; 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, 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: ;; INSTALL ;; ;; To install, put this file along your Emacs-Lisp `load-path' and add ;; the following into your ~/.emacs startup file or set ;; `global-smart-tab-mode' to non-nil with customize: ;; ;; (require 'smart-tab) ;; (global-smart-tab-mode 1) ;; ;; DESCRIPTION ;; ;; Try to 'do the smart thing' when tab is pressed. `smart-tab' ;; attempts to expand the text before the point or indent the current ;; line or selection. ;; ;; See . ;; There are a number of available customizations on that page. ;; ;; Features that might be required by this library: ;; ;; `easy-mmmode' ;;; Change Log: ;;; Code: (require 'easy-mmode) (defgroup smart-tab nil "Options for `smart-tab-mode'." :group 'tools) (defvar smart-tab-debug nil "Turn on for logging about which `smart-tab' function ends up being called.") (defcustom smart-tab-using-hippie-expand nil "Use `hippie-expand' to expand text. Use either `hippie-expand' or `dabbrev-expand' for expanding text when we don't have to indent." :type '(choice (const :tag "hippie-expand" t) (const :tag "dabbrev-expand" nil)) :group 'smart-tab) (defcustom smart-tab-completion-functions-alist '((emacs-lisp-mode . lisp-complete-symbol) (text-mode . dabbrev-completion)) "A-list of major modes in which to use a mode specific completion function. If current major mode is not found in this alist, fall back to `hippie-expand' or `dabbrev-expand', depending on the value of `smart-tab-using-hippie-expand'" :type '(alist :key-type (symbol :tag "Major mode") :value-type (function :tag "Completion function to use in this mode")) :group 'smart-tab) (defcustom smart-tab-disabled-major-modes '(org-mode term-mode eshell-mode w3m-mode magit-mode) "List of major modes that should not use `smart-tab'." :type 'sexp :group 'smart-tab) (defcustom smart-tab-user-provided-completion-function nil "Use a function provided by a completion framework to attempt expansion. By default, we check for the presence of `auto-complete-mode' and use it as the completion framework. Set this variable if you want to use a different completion framework. Eg: For `company-mode', you can set this var to `company-complete'." :type '(function) :group 'smart-tab) (defun smart-tab-call-completion-function () "Completion is attempted as follows: 1. Check if a mode-specific completion function is defined in `smart-tab-completion-functions-alist'. If yes, then use it. 2. Check if the user has plugged in a custom completion function in `smart-tab-user-provided-completion-function'. If yes, then use it. 3. Check if `auto-complete-mode' is installed. If yes, then use it. 4. Check if user prefers `hippie-expand' instead of `dabbrev-expand' by referring to `smart-tab-using-hippie-expand'. Use one of these default methods to attempt completion." (if smart-tab-debug (message "complete")) (let ((smart-tab-mode-specific-completion-function (cdr (assq major-mode smart-tab-completion-functions-alist)))) (cond ((fboundp smart-tab-mode-specific-completion-function) (funcall smart-tab-mode-specific-completion-function)) ((and (not (minibufferp)) (fboundp smart-tab-user-provided-completion-function)) (funcall smart-tab-user-provided-completion-function)) ((and (not (minibufferp)) (memq 'auto-complete-mode minor-mode-list) (boundp 'auto-complete-mode) auto-complete-mode (fboundp 'ac-start)) (ac-start :force-init t nil)) (smart-tab-using-hippie-expand (hippie-expand nil)) (t (dabbrev-expand nil))))) (defcustom smart-tab-expand-eolp nil "Controls whether `smart-tab' should offer completion when point is at EOL. The default behaviour is to do nothing. Set this to t to enable (for example) method completions." :type '(boolean) :group 'smart-tab) (defun smart-tab-must-expand (&optional prefix) "If PREFIX is \\[universal-argument] or the mark is active, do not expand. Otherwise, uses the user's preferred expansion function to expand the text at point." (unless (or (consp prefix) (use-region-p)) (looking-at "\\_>"))) (defun smart-tab-default () "Indent region if mark is active, or current line otherwise." (interactive) (if smart-tab-debug (message "default")) (let* ((smart-tab-mode nil) (global-smart-tab-mode nil) (ev last-command-event) (triggering-key (cl-case (type-of ev) (integer (char-to-string ev)) (symbol (vector ev)))) (original-func (or (key-binding triggering-key) (key-binding (lookup-key local-function-key-map triggering-key)) 'indent-for-tab-command))) (call-interactively original-func))) ;;;###autoload (defun smart-tab (&optional prefix) "Try to 'do the smart thing' when tab is pressed. `smart-tab' attempts to expand the text before the point or indent the current line or selection. In a regular buffer, `smart-tab' will attempt to expand with either `hippie-expand' or `dabbrev-expand', depending on the value of `smart-tab-using-hippie-expand'. Alternatively, if a `smart-tab-user-provided-completion-function' is defined, it will be used to attempt expansion. If the mark is active, or PREFIX is \\[universal-argument], then `smart-tab' will indent the region or the current line (if the mark is not active)." (interactive "P") (cond (buffer-read-only (smart-tab-default)) ((use-region-p) (indent-region (region-beginning) (region-end))) ((smart-tab-must-expand prefix) (smart-tab-call-completion-function)) ((and smart-tab-expand-eolp (eolp)) (smart-tab-call-completion-function)) (t (smart-tab-default)))) ;;;###autoload (defun smart-tab-mode-on () "Turn on `smart-tab-mode'." (smart-tab-mode 1)) (defun smart-tab-mode-off () "Turn off `smart-tab-mode'." (smart-tab-mode -1)) ;;;###autoload (define-minor-mode smart-tab-mode "Enable `smart-tab' to be used in place of tab. With no argument, this command toggles the mode. Non-null prefix argument turns on the mode. Null prefix argument turns off the mode." :lighter " Smrt" :group 'smart-tab :require 'smart-tab :keymap '(("\t" . smart-tab) ([(tab)] . smart-tab)) (if smart-tab-mode (progn ;; Don't start `smart-tab-mode' when in the minibuffer or a read-only ;; buffer. (when (or (minibufferp) buffer-read-only (member major-mode smart-tab-disabled-major-modes)) (smart-tab-mode-off))))) ;;;###autoload (define-globalized-minor-mode global-smart-tab-mode smart-tab-mode smart-tab-mode-on :group 'smart-tab) (provide 'smart-tab) ;;; smart-tab.el ends here