emacs-sentence-navigation icon indicating copy to clipboard operation
emacs-sentence-navigation copied to clipboard

(Broken) Better Sentence Movement Commands and Evil Text Objects

  • About [[http://melpa.org/#/sentence-navigation][file:http://melpa.org/packages/sentence-navigation-badge.svg]] =sentence-navigation.el= is inspired by [[https://github.com/reedes/vim-textobj-sentence][vim-textobj-sentence]] and uses a mostly the same default abbreviation list. It provides alternatives to ~forward-sentence~, ~backward-sentence~, and sentence text objects that work with sentences separated by one (or two) space(s) and is aware of abbreviations.

  • Installation If you use MELPA, =sentence-navigation.el= can be installed with ~package-install~. Here's an example [[https://github.com/jwiegley/use-package][use-package]] configuration: #+begin_src emacs-lisp (use-package sentence-navigation :ensure t ;; autoloads will be created for all commands and text objects ;; when installed with package.el :defer t) #+end_src

  • Example Configuration There are no default key bindings. Unlike emacs' normal forward and backward sentence commands, this package provides four commands that will move either to the start or end of a sentence instead of to the space in between sentences.

** Evil Note that =)= and =(= are bound in motion state by default. Keys bound in motion state are inherited by the normal, visual, and operator states if they are not explicitly bound in those states.

#+begin_src emacs-lisp (define-key evil-motion-state-map ")" 'sentence-nav-evil-forward) (define-key evil-motion-state-map "(" 'sentence-nav-evil-backward) (define-key evil-motion-state-map "g)" 'sentence-nav-evil-forward-end) (define-key evil-motion-state-map "g(" 'sentence-nav-evil-backward-end) (define-key evil-outer-text-objects-map "s" 'sentence-nav-evil-a-sentence) (define-key evil-inner-text-objects-map "s" 'sentence-nav-evil-inner-sentence) #+end_src ** No Evil #+begin_src emacs-lisp (global-set-key (kbd "M-e") 'sentence-nav-forward) (global-set-key (kbd "M-a") 'sentence-nav-backward) #+end_src Both ~sentence-nav-forward-end~ and ~sentence-nav-backward-end~ can also be bound.

** Abbreviations The list of abbreviations to ignore can be customized. The defaults are mostly the same as vim-textobj-sentence's. #+begin_src emacs-lisp (setq sentence-nav-abbreviation-list '("[ABCDIMPSUabcdegimpsv]" "l[ab]" "[eRr]d" "Ph" "[Ccp]l" "[Ll]n" "[c]o" "[Oe]p" "[DJMSh]r" "[MVv]s" "[CFMPScfpw]t" "alt" "[Ee]tc" "div" "es[pt]" "[Ll]td" "min" "[MD]rs" "[Aa]pt" "[Aa]ve?" "[Ss]tr?" "e\.g" "[Aa]ssn" "[Bb]lvd" "[Dd]ept" "incl" "Inst" "Prof" "Univ")) #+end_src ** Jump Locations By default, any delimiters that can surround a sentence such as quotation marks will be considered as part of the start or end of a sentence. This means that the jump commands will jump to quotation marks. If you would prefer to have the commands always jump to the capital letter at the start of the sentence or the period at the end, you can set =sentence-nav-jump-to-syntax= to =nil=.

For evil users, =sentence-nav-syntax-text-objects= can be set to a non-nil value to change the distinction between the inner and outer text objects. When this variable is non-nil, the outer text object will include any delimiters such as quotation marks while the inner text object will not.

** Accurate Jumps for Hard-Wrapped Sentences By default, this package assumes that a capital letter at the beginning of the line starts a sentence. While this works fine for soft-wrapped sentences, it can result in incorrect jumps when using hard-wrapped sentences (e.g. when a line begins with a name that is in the middle of a sentence). To fix this, users must set =sentence-nav-hard-wrapping= to a non-nil value and ensure that =sentence-nav-non-sentence-line-alist= is properly set for the =major-mode= being used. This is necessary to ensure that sentences that start the line after some markup (e.g. a markdown or org heading) are handled correctly.

  • Definition of a Sentence Sentences start with a capital letter (possibly preceded some type of a left quotation mark). They end with a period (or other characters like "!", "?", or an ellipsis) possibly followed by some right quotation mark(s). In addition to quotes, org and markdown markup characters (such as =_= and =*=) can be around sentences. Like with quotes, the point will be moved to just before or after the last surrounding markup character. Abbreviations are irrelevant for two-spaced sentences, and this plugin will work fine for them.

  • Impossible Cases For the most part, this plugin can distinguish accurately between one-spaced sentences and abbreviations. In the rare case that a sentence actually does end in an abbreviation (not followed by "?", "!", etc.), this plugin will skip over that sentence. Because of this, I have removed the abbreviations "in" and "no" from =vim-textobj-sentence='s list since I think a sentence is more likely to end in either "in" or "no" than for either to be used as an abbreviation.

  • FAQ ** Why doesn't it work with commented sentences? This packages uses regular expressions to check for comment starting characters, and not all major modes with comments add syntax table entries for their comment characters (e.g. org mode). It is recommended that you file an issue with the relevant package if you believe that it should make a syntax table entry. As a workaround, you can modify the syntax table yourself in your init file: #+begin_src emacs-lisp (with-eval-after-load 'org (modify-syntax-entry ?# "<" org-mode-syntax-table))

;; alternatively after starting emacs in the relevant buffer (modify-syntax-entry ?# "<") #+end_src

Another example for text mode: #+begin_src emacs-lisp (modify-syntax-entry ?# "<" text-mode-syntax-table) #+end_src