I was not satisfied with the behavior of standard C-arrow and C-S-arrow, so I wrote this functions to replace standard ones.

(setq separators-regexp "[\-'\"();:,.\\/?!@#%&*+=]")

(defun forward-to-separator()
    "Move to the next separator like in the every NORMAL editor"
    (let ((my-pos (re-search-forward separators-regexp)))
        (goto-char my-pos)))

(defun backward-to-separator()
    "Move to the previous separator like in the every NORMAL editor"
    (let ((my-pos (re-search-backward separators-regexp)))
        (goto-char my-pos)))

(global-set-key (kbd "C-<right>") 'forward-to-separator)
(global-set-key (kbd "C-<left>") 'backward-to-separator)

But now I am facing a problem with shift selection: it actually just not working at all. So my question is: how can I select text using my new functions and shift key(C-S-arrow)?

My final code is here: https://gist.github.com/FirstTimeInForever/8303ac1918c81365991d7acf7a926bd8 I added C-<backspace> support and changed my regex. Now I can move cursor like in sublime text!

  • Why not just set a mark (C-<SPC>) from where you want to select and proceed to use your cursor movement functions? Aug 20 '16 at 14:32
  • I know I can set a mark, but selecting text with shift is more comfortable for me.
    – user13047
    Aug 20 '16 at 14:40
  • Excelent, do you know how to implement jumps over black characters ?
    – dev1223
    Nov 26 '17 at 21:47

Use (interactive "^") instead of (interactive).

C-h f interactive tells you, way down near the bottom:

If the string begins with ^ and shift-select-mode is non-nil, Emacs first calls the function handle-shift-selection.

In your case, the commands do not take an argument. But you can still use a string argument to interactive, in this case "^", to make the command respect shift selection.


I upgraded your code so cursor navigation is now behaving a lot better then default setting in Atom editor. Mainly I added skipping blank spaces in file.

I highly recommend binding those to C-<right> and C-<left> because in CUA mode, C-S-<right> and C-S-<left> is automatically binded to skipped text selection so you can easily copy/remove as in other editors.

(require 'cl-macs)

(setq custom/separators-regexp

(defun custom/position-normalize-forward (saved-pos my-pos)
  (if (>= (- my-pos saved-pos) 2)
        (goto-char my-pos)
        (if (not (eq my-pos (point-max)))
      (goto-char my-pos))))

(defun custom/forward-symbol ()
  (interactive "^")
  (block custom/forward-symbol
    (let* ((saved-pos (point))
           (my-pos (re-search-forward custom/separators-regexp))
           (my-pos2 (custom/find-blank-space t saved-pos)))
      (if (> my-pos2 my-pos)
            (goto-char (- my-pos2 1))
            (return-from custom/forward-symbol)))
      (if (eq my-pos 1)
            (goto-char (+ my-pos 1))
            (setf my-pos (re-search-forward custom/separators-regexp))
            (custom/position-normalize-forward saved-pos my-pos))
        (custom/position-normalize-forward saved-pos my-pos)))))

(defun custom/position-normalize-backward (saved-pos my-pos)
  (if (>= (- saved-pos my-pos) 2)
        (goto-char my-pos)
        (if (not (eq my-pos 1))
      (goto-char my-pos))))

(defun custom/backward-symbol ()
  (interactive "^")
  (block custom/backward-symbol
    (let* ((saved-pos (point))
           (my-pos (re-search-backward custom/separators-regexp))
           (my-pos2 (custom/find-blank-space nil saved-pos)))
      (if (< my-pos2 my-pos)
            (goto-char (+ my-pos2 1))
            (return-from custom/backward-symbol)))
      (if (eq my-pos (point-max))
            (goto-char (- my-pos 1))
            (setf my-pos (re-search-backward custom/separators-regexp))
            (custom/position-normalize-backward saved-pos my-pos))
        (custom/position-normalize-backward saved-pos my-pos)))))

(defun custom/find-blank-space (go-forward starting-pos)
  (let ((starting-char (string (char-after starting-pos)))
        (skipped-count 0)
        (increment (if go-forward 1 -1))
        (should-loop t))
    (if (or (equal starting-char " ")
            (equal starting-char "\t")
            (equal starting-char "\n"))
          (while should-loop
            (if (or (equal (string (char-after (+ saved-pos skipped-count))) " ")
                    (equal (string (char-after (+ saved-pos skipped-count))) "\t")
                    (equal (string (char-after (+ saved-pos skipped-count))) "\n"))
                (setq skipped-count (+ skipped-count increment))
              (setq should-loop nil)))
          (+ starting-pos skipped-count))

Hope it will help.

Your Answer

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