From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: Ian Dunn Newsgroups: gmane.emacs.devel Subject: Re: [ELPA] New package: Captain (was [ELPA] New package: Auto Capitalize Mode) Date: Sat, 02 Sep 2017 15:23:20 -0400 Message-ID: <87a82c7vmf.fsf_-_@escafil> References: <87d17hdmnm.fsf@escafil> <87fucapnci.fsf@mat.ucm.es> <877exlsxhs.fsf@escafil> <874lspa4ym.fsf@mat.ucm.es> <878thzma90.fsf@escafil> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Trace: blaine.gmane.org 1504380297 20046 195.159.176.226 (2 Sep 2017 19:24:57 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Sat, 2 Sep 2017 19:24:57 +0000 (UTC) User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/26.0.50 (gnu/linux) Cc: Uwe Brauer , Stefan Monnier To: emacs-devel@gnu.org Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Sat Sep 02 21:24:42 2017 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by blaine.gmane.org with esmtp (Exim 4.84_2) (envelope-from ) id 1doE1r-0003ua-CV for ged-emacs-devel@m.gmane.org; Sat, 02 Sep 2017 21:24:27 +0200 Original-Received: from localhost ([::1]:48175 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1doE1y-0004Cc-Bh for ged-emacs-devel@m.gmane.org; Sat, 02 Sep 2017 15:24:34 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:59551) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1doE1l-0004A8-KC for emacs-devel@gnu.org; Sat, 02 Sep 2017 15:24:26 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1doE1g-0002Hg-VD for emacs-devel@gnu.org; Sat, 02 Sep 2017 15:24:21 -0400 Original-Received: from fencepost.gnu.org ([2001:4830:134:3::e]:55782) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1doE1O-0001mr-Fx; Sat, 02 Sep 2017 15:23:58 -0400 Original-Received: from [2604:6000:1010:176:da4d:3352:bae5:f50e] (port=49116 helo=escafil) by fencepost.gnu.org with esmtpsa (TLS1.2:RSA_AES_256_CBC_SHA1:256) (Exim 4.82) (envelope-from ) id 1doE1O-0003lE-3j; Sat, 02 Sep 2017 15:23:58 -0400 In-Reply-To: (Stefan Monnier's message of "Thu, 31 Aug 2017 22:29:57 -0400") X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 2001:4830:134:3::e X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.org gmane.emacs.devel:217905 Archived-At: --=-=-= Content-Type: text/plain >>>>> "Stefan" == Stefan Monnier writes: >> I agree that having two packages that provide >> "auto-capitalize-mode" will be confusing to users. Stefan> Unless one of them is abandoned and the other can be Stefan> considered as a newer version (no idea if it's the case, Stefan> here). I don't know for sure that the old one's abandoned, although it looks like little has been done on it in the past few years. That could be because it was determined to be in a stable state, or it could be because it's been abandoned. In any case, I've chosen to rename the package and add two new features to it to mostly match what the old package did. Specifically, I added the ability to capitalize a word once and have it automatically capitalized in the future, and the ability to capitalize the current sentence using the function it was already using to find the start of the sentence. The latter is implemented to open the possibility of capitalizing yanked text in the future, a feature of the old package, although I see no need for it right now. I also mention the old package in the commentary, in case other users may want to try it instead. --=-=-= Content-Type: application/emacs-lisp Content-Disposition: attachment; filename=captain.el Content-Transfer-Encoding: quoted-printable ;;; captain.el --- CAPiTalization is Automatic IN emacs -*- lexical-binding= : t; -*- ;; Copyright (C) 2017 Ian Dunn ;; Author: Ian Dunn ;; Maintainer: Ian Dunn ;; Keywords: editing ;; Version: 1.0 ;; This file is 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: ;; The captain handles capitalizing text as you type, freeing you to do oth= er ;; things. ;; Invoke the captain across the globe with `global-captain-mode', or just = on ;; one ship (buffer) at a time with `captain-mode'. ;; For normal people: ;; Automatically capitalizes the first word of a sentence as you type. The ;; bounds of a sentence are determined by local variable ;; `captain-sentence-start-function'. For example, you can set it to find = the ;; start of a list or heading in `org-mode'. ;; Further control can be obtained by modifying the local variable ;; `captain-predicate', which tells the captain when to work and when to re= lax. ;; It's also possible to automatically capitalize individual words using the ;; command `captain-capitalize-word'. This will capitalize the word at poi= nt ;; and tell the captain about it, so he knows that you want it capitalized = from ;; then on. ;; This solves a similar problem to that of Kevin Rodgers's auto-capitalize ;; package, but using more modern Emacs features. ;;; Code: (eval-when-compile (require 'subr-x)) (require 'thingatpt) (defvar-local captain-predicate nil "Predicate to check for whether auto-capitalization should be handled. Should be a function of no arguments, and return non-nil if auto-capitalization should be performed at the current point. For example, this could be a function to check if point is in a comment.") (defun captain-should-capitalize-p () "Return non-nil if the captain should auto-capitalize your work." (when captain-predicate (funcall captain-predicate))) (defun captain--default-sentence-start () "Default value of `captain-sentence-start-function'. Just runs `bounds-of-thing-at-point' for a sentence." (car (bounds-of-thing-at-point 'sentence))) (defvar-local captain-sentence-start-function #'captain--default-sentence-start "Function to determine the start of the current sentence. This should be a function of no arguments that returns the point at which the current function begins.") (defun captain--run () "Automatically capitalize the word preceding point if it is the start of = a sentence." ;; Only do this if the last inserted character isn't part of a word, ;; (preceding-char) is the previously inserted character; ;; We also need the character before that (It must be a word constituent,= so ;; enter or space don't keep trying to capitalize the sentence). (when (and (captain-should-capitalize-p) (not (or (bobp) (eq (char-syntax (preceding-char)) ?w))) (eq (char-syntax (save-excursion (forward-char -1) (preceding-char))) ?w)) (save-excursion ;; Move back to the word that was just finished, and determine if it ;; starts a sentence. (backward-word-strictly) ;; Ensure we're still expected to capitalize. Case: New character pu= t us ;; inside a string, but backward-word-strictly brought us outside the ;; string. (when (captain-should-capitalize-p) (when-let ((word-bounds (bounds-of-thing-at-point 'word)) (sentence-start (funcall captain-sentence-start-function= ))) (cond ((let ((case-fold-search nil)) (string-match-p "[[:upper:]]" (word-at-point))) nil) ;; This word does start a sentence, so capitalize it ((eq (car word-bounds) sentence-start) (capitalize-word 1)) ;; Word bound will only ever be one greater than the sentence bo= und if ;; the sentence begins with some sort of punctuation. Remember,= spaces ;; don=E2=80=99t count, so if we have a sentence starting with "= A ball...", ;; =E2=80=99ball=E2=80=99 wont trigger this, only =E2=80=99A=E2= =80=99. ((eq (car word-bounds) (1+ sentence-start)) (capitalize-word 1)))))))) (defun captain-capitalize-sentence () "Tell the captain to capitalize the start of the current sentence." (interactive) (save-excursion (goto-char (funcall captain-sentence-start-function)) (capitalize-word 1))) ;;;###autoload (define-minor-mode captain-mode "Call the captain to automatically capitalize the start of every sentence. The captain will also automatically capitalize words you've told him you want capitalized with `captain-capitalize-word'. \\{captain-mode-map}" :init-value nil :lighter " Captain" :global nil (if captain-mode (progn (add-hook 'post-self-insert-hook 'captain--run nil t)) (remove-hook 'post-self-insert-hook 'captain--run t))) ;;;###autoload (define-globalized-minor-mode global-captain-mode captain-mode captain-mode) ;; Support for capitalizing individual words automatically (define-abbrev-table 'captain-mode-abbrev-table nil "Abbrev table where words that should be automatically capitalized are st= ored." :enable-function #'captain-should-capitalize-p) ;; Only enable the abbrev table when captain-mode is active (add-to-list 'abbrev-minor-mode-table-alist `(captain-mode ,captain-mode-abbrev-table) 'append #'equal) (defun captain--start-of-word-p () "Return non-nil if at the start of a word." (and ;; looking at a word constituent... (eq (char-syntax (following-char)) ?w) ;; ...but before us isn't a word constituent (not (eq (char-syntax (preceding-char)) ?w)))) (defun captain-capitalize-word () "Tell the captain to capitalize the word at point. After that, the captain will remember your choice, and automatically capitalize the word from then on. If not looking at a word, move forward to find the next word." (interactive) (save-excursion ;; Cases for where we are: (cond ;; Looking at the start of a word, so don't move ((captain--start-of-word-p)) ((word-at-point) ;; If we're in the middle of a word, then move back to the start. (backward-word-strictly)) (t ;; Otherwise, we're not on a word, so move forward to find the next o= ne. ;; This is to remain consistent with the behavior of `capitalize-word= '. (forward-to-word 1))) (when-let ((old-word (substring-no-properties (word-at-point))) (new-word (capitalize old-word))) ;; Store the abbrev so this word is automatically capitalized later. (define-abbrev captain-mode-abbrev-table old-word new-word nil :count 1)) ;; Finally, capitalize the word at point. (capitalize-word 1))) (provide 'captain) ;;; captain.el ends here --=-=-= Content-Type: text/plain -- Ian Dunn --=-=-=--