4

I'd like to modify the behavior of kill-sentence (which I have bound to M-k) according to context, in the following way:

  1. If the point is at the beginning of a sentence, then kill the sentence in the usual way, deleting all the words of the sentence, as well as the period and the space after the period.
  2. If the point is in the middle of a sentence somewhere, it makes no sense to delete the period and space, because this unwantedly joins two sentences. So if point is inside a sentence, I want Emacs to run kill-sentence-to-period, below, so as to preserve the period.

i.e. something like this:

(defun kill-sentence-to-period ()
    "Kill the rest of the sentence but leave the period."
    (interactive)
    (kill-sentence)
    (push-mark)
    (insert ".")
    (backward-char))

Is it possible to change the behavior of M-k depending on the context, conditional on whether or not the point is inside a sentence?

2

Here's an attempt at defining a custom kill-sentence-dwim command that will either kill the entire sentence or kill up to the sentence-ending punctuation.

(defun my/forward-to-sentence-end ()
  "Move point to just before the end of the current sentence."
  (forward-sentence)
  (backward-char)
  (unless (looking-back "[[:alnum:]]")
    (backward-char)))

(defun my/beginning-of-sentence-p ()
  "Return  t if point is at the beginning of a sentence."
  (let ((start (point))
        (beg (save-excursion (forward-sentence) (forward-sentence -1))))
    (eq start beg)))

(defun my/kill-sentence-dwim ()
  "Kill the current sentence up to and possibly including the punctuation.
When point is at the beginning of a sentence, kill the entire
sentence. Otherwise kill forward but preserve any punctuation at the sentence end."
  (interactive)
  (if (my/beginning-of-sentence-p)
      (progn
        (kill-sentence)
        (just-one-space)
        (when (looking-back "^[[:space:]]+") (delete-horizontal-space)))
      (kill-region (point) (progn (my/forward-to-sentence-end) (point)))
      (just-one-space 0)))

You can bind my/kill-sentence-dwim to a key binding of your choice. If you want to replace the existing kill-sentence binding you could use this:

(define-key (current-global-map) [remap kill-sentence] 'my/kill-sentence-dwim)

A couple notes:

  • Instead of just assuming a period, my/forward-to-sentence-end moves to the end of the sentence and then backs up to just after the last alphanumeric character. That should preserve any closing punctuation including any quotes or parens.

  • I'm checking whether point is at the beginning of the sentence by calling forward-sentence to jump to the end and then back to the beginning, then seeing if point changed. Not sure how reliable this will be but it seems correct in the simple cases I tried.

  • As requested in the comments, this kill command also attempts to fix up whitespace. When deleting a partial sentence it removes whitespace before the ending punctuation. When deleting an entire sentence it leaves one space between sentences or no spaces at the beginning of the line. There will likely still be edge cases...

  • 1
    I've updated the code to remove spaces at the end of the sentence using just-one-space. – glucas May 11 '15 at 20:12
  • 1
    You can use just-one-space again. So if you want to leave one space between sentences you could replace (kill-sentence) in the code above with (progn (kill-sentence) (just-one-space 1)). – glucas May 11 '15 at 21:41
  • 1
    For the record, you can also just call just-one-space directly. It is bound to M-SPC by default, so you would probably get the behavior you want by hitting M-k followed by M-SPC. – glucas May 11 '15 at 21:42
  • 1
    Oh, right -- just-one-space will in fact always leave a space. I've fixed it by checking specifically for space at the beginning of the line, but we may be reaching the point of diminishing returns in tweaking this answer. If you run in to more cases it may be a good opportunity to experiment with elisp. Good luck and hope this has been helpful! – glucas May 12 '15 at 13:44
  • 1
    @PeterSalazar Since we're straying from the original question, let's move this discussion to a chat: chat.stackexchange.com/rooms/23774/… – glucas May 14 '15 at 16:30
1

A quick-and-dirty (i.e., too simplistic) answer is this:

(defun at-sentence-beginning-p ()
  "Return non-nil if at the beginning of a sentence."
  (looking-back (sentence-end)))

It really tests whether point is after a sentence end. There are plenty of cases where it does not do the right thing, including, for example, a sentence enclosed in parens. But it might help you get started.

  • Great, and how can I make it so that kill-sentence uses that information to do the right thing based on context, i.e. to do one or the other of the two cases above? – incandescentman May 9 '15 at 5:31
  • I don't understand; what two cases? What I was trying to say is that "there are plenty of cases" where it does not do the right thing. You would likely need to experiment and see what happens in different contexts. And then special-case some of the contexts where it doesn't do what you want. Looking back at sentence-end is likely good for whenever it returns non-nil (but maybe not always?), but when it returns nil you might still be at a sentence start (e.g. at bobp). Experiment on real text that you might care about, to see where it fails. – Drew May 9 '15 at 6:08
  • @PeterSalazar I think what you're asking is how to change the behavior of kill-sentence? You don't. What Drew is suggesting is you need to rebind the key to your own custom command that uses looking-back to decide whether to call kill-sentence or do something else. – glucas May 10 '15 at 11:22
  • @glucas Exactly. How should I rephrase the question to reflect that? As you said, what I need is to create a new function that (1) uses looking-back to determine the position of the point and (2) decides whether to call kill-sentence or kill-sentence-to-period (3) calls one or the other. – incandescentman May 10 '15 at 18:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.