-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathsmart-delete.el
150 lines (124 loc) · 5.2 KB
/
smart-delete.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
;;; smart-delete.el --- IntelliJ-like backspace/delete -*- lexical-binding: t -*-
;; Copyright (C) 2020 Leonardo Dagnino
;; Author: Leonardo Schripsema
;; Created: 2020-06-13
;; Version: 0.1.0
;; Package-Requires: ((emacs "24.1"))
;; Keywords: emulations, wp
;; URL: https://github.com/leodag/smart-delete
;; 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 <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Handle backspace/delete like IntelliJ IDEs.
;; When `smart-delete-mode' is enabled, do a smart delete, unless:
;; * Called with an argument
;; * Inside a string literal
;; * Have non-spacing characters before/after point (for
;; backward/forward deletes, respectively)
;; Deletes region when region is active and delete-active-region
;; is non-nil.
;; A smart delete consists of:
;; When smart deleting backward:
;; If after indentation level, go back to indentation
;; If at or before indentation level, delete to end of previous
;; line
;; When smart deleting forward:
;; Delete spaces after point
;; Smart delete backward at next line
;; Usage:
;; (smart-delete-mode 1)
;; Some ideas taken from https://github.com/itome/smart-backspace
;;; Code:
(defun smart-delete--only-space-before-point ()
"Nil if there is a non-space character before point.
Returns the distance traveled if there isn't."
(save-excursion
;; LIM is needed in case \n has " " syntax (as in SML mode)
(let ((skip (skip-syntax-backward " " (line-beginning-position))))
(if (bolp)
skip))))
(defun smart-delete--only-space-after-point ()
"Nil if there is a non-space character after point.
Returns the distance traveled if there isn't."
(save-excursion
(let ((skip (skip-syntax-forward " " (line-end-position))))
(if (eolp)
skip))))
(defun smart-delete-backward (_n &optional killflag)
"Does a smart delete backwards.
Does not check for the conditions on whether a smart delete
should be run - that is left to the keymap's filter. So don't
bind this to a key. Optional argument KILLFLAG is passed to
`delete-char'."
(interactive "p\nP")
;; must operate relative to point - if you only consider
;; columns, there will be problems when indent-tabs-mode
(let* ((indentation (smart-delete--only-space-before-point))
(current-point (point))
(indent-offset (progn
(indent-according-to-mode)
(- (point) current-point))))
(when (>= indent-offset 0)
(delete-char (- indentation indent-offset 1) killflag)
(when (smart-delete--only-space-before-point)
(indent-according-to-mode)))))
(defun smart-delete-forward (n &optional killflag)
"Does a smart delete forward.
Does not check for the conditions on whether a smart delete
should be run - that is left to the keymap's filter. So don't
bind this to a key. Argument N is passed to
`smart-delete-backward' but is ultimately ignored. Optional
argument KILLFLAG is passed to `delete-char'."
(interactive "p\nP")
(delete-char (smart-delete--only-space-after-point) killflag)
(let ((pre-column (current-column)))
(forward-char 1)
(smart-delete-backward n killflag)
(let ((offset (- pre-column (current-column))))
(when (> offset 0)
(insert-char ?\s offset)))))
(defun smart-delete--should-smart-delete-backward-filter (cmd)
"Filter function to decide whether a smart delete should be run.
CMD is the real binding, see Info node `(elisp) Extended Menu Items'."
(and (not prefix-arg)
(not (and (use-region-p)
delete-active-region))
(not (nth 3 (syntax-ppss)))
(smart-delete--only-space-before-point)
cmd))
(defun smart-delete--should-smart-delete-forward-filter (cmd)
"Filter function to decide whether a smart delete should be run.
CMD is the real binding, see Info node `(elisp) Extended Menu Items'."
(and (not prefix-arg)
(not (and (use-region-p)
delete-active-region))
(not (nth 3 (syntax-ppss)))
(smart-delete--only-space-after-point)
cmd))
(defvar smart-delete-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "DEL")
`(menu-item
"" smart-delete-backward
:filter smart-delete--should-smart-delete-backward-filter))
(define-key map (kbd "<deletechar>")
`(menu-item
"" smart-delete-forward
:filter smart-delete--should-smart-delete-forward-filter))
map))
;;;###autoload
(define-minor-mode smart-delete-mode
"Does a smart delete when at the end or start of a line.
When deleting forward in a line where there is only space after
point and when deleting backward when there is only space before
point.")
(provide 'smart-delete)
;;; smart-delete.el ends here