├── .ert-runner
├── .github
└── workflows
│ └── test.yml
├── .gitignore
├── CHANGELOG.md
├── CONTRIBUTING.md
├── COPYING
├── Cask
├── README.md
├── assets
└── emr.png
├── emr-c.el
├── emr-css.el
├── emr-elisp.el
├── emr-iedit.el
├── emr-js.el
├── emr-lisp.el
├── emr-prog.el
├── emr-ruby.el
├── emr-scheme.el
├── emr.el
└── test
├── emr-css-test.el
├── emr-elisp-test.el
└── test-helper.el
/.ert-runner:
--------------------------------------------------------------------------------
1 | -L .
2 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Run test suite
2 | on: [push, pull_request]
3 |
4 | jobs:
5 | test:
6 | runs-on: ubuntu-latest
7 | strategy:
8 | matrix:
9 | emacs_version:
10 | - '25.1'
11 | - '26.1'
12 | - '27.1'
13 |
14 | steps:
15 | - uses: actions/setup-python@v2
16 | - uses: purcell/setup-emacs@master
17 | with:
18 | version: ${{ matrix.emacs_version }}
19 | - uses: conao3/setup-cask@master
20 |
21 | - uses: actions/checkout@v2
22 |
23 | - name: Test
24 | env:
25 | COVERALLS_FLAG_NAME: Emacs ${{ matrix.emacs_version }}
26 | COVERALLS_PARALLEL: 1
27 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
28 | run: |
29 | cask install
30 | cask exec ert-runner
31 |
32 | finalize:
33 | runs-on: ubuntu-latest
34 | if: always()
35 | needs: test
36 | steps:
37 | - run: curl "https://coveralls.io/webhook?repo_name=$GITHUB_REPOSITORY&repo_token=${{ secrets.GITHUB_TOKEN }}" -d "payload[build_num]=$GITHUB_RUN_NUMBER&payload[status]=done"
38 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 | *.elc
3 | elpa
4 | *.tar
5 | *-pkg.el
6 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## v0.4.1 (unreleased)
2 |
3 | No changes yet.
4 |
5 | ## v0.4
6 |
7 | elisp:
8 |
9 | * 'autoload' refactorings have been renamed to 'add autoload' and
10 | 'autoload cookie' for clarity.
11 | * 'add autoload' is no longer pointlessly offered for functions
12 | defined in the current file.
13 | * Only offer one of 'rename in function' and 'rename in file', as
14 | appropriate.
15 | * Only offer 'rename in function' when point is on symbols that are
16 | bound in this function.
17 | * Don't offer to rename special forms.
18 | * 'Find unused' now considers interactive functions to be used, and is
19 | also more robust.
20 | * 'Extract to let' is now more robust for subexpressions and adds
21 | newlines correctly.
22 |
23 | iedit:
24 |
25 | * Fixed an issue with 'rename globally' where it only renamed the
26 | current instance of a symbol.
27 | * Changed 'rename globally' to 'rename in file' to reflect what it
28 | actually does.
29 | * EMR no longer modifies iedit-mode-map.
30 |
31 | ## v0.3.8
32 |
33 | Elisp:
34 |
35 | * Extracting let bindings is now much more generic and produces
36 | correct, equivalent code in many more circumstances.
37 | * Fixed a crash on inlining functions when ido-yes-or-no-p was not
38 | installed.
39 | * Fixed an issue when extracting let bindings inside `ert-deftest`
40 | forms.
41 | * Improved names of refactorings to clarify what they do.
42 | * Fixed an issue with extract refactorings from functions with
43 | autoload cookies, where the cookie got moved.
44 | * Fixed an issue where inlining let variables wasn't always offered.
45 | * Added a new refactoring for toggling between `let` and `let*`.
46 |
47 | Redshank is no longer a dependency: it was only used for extracting
48 | let bindings, and it was unmaintained.
49 |
50 | Lisp:
51 |
52 | * Remove comment/uncomment refactorings. These cluttered the refactor
53 | menu, as they were offered in all situations. There are many tools
54 | for commenting already, such as: `comment-or-uncomment-region`,
55 | `paredit-comment-dwim`, `comment-dwim`, `lispy-comment`.
56 |
57 | Please file a bug if you miss this feature.
58 |
59 | ## v0.3.7
60 |
61 | General:
62 |
63 | * Fixed an issue where calling `emr-show-refactor-menu` would say 'no
64 | refactorings available' when emr hadn't been used.
65 |
66 | Elisp:
67 |
68 | * Fixed an issue where `emr-el-extract-function` was confused by
69 | quoted symbols.
70 | * Fixed an issue where extract refactorings were not offered when they
71 | should be.
72 |
73 | CSS: Added `emr-css-toggle-important`.
74 |
75 | Ruby: Added `ruby-refactor-convert-post-conditional`.
76 |
77 | C/C++: Added `emr-cc-format-region` and `emr-cc-format-buffer` to
78 | format with clang-format according to `emr-clang-format-style`.
79 |
80 | iedit: Added `emr-iedit-in-function`, `emr-iedit-in-region` and
81 | `emr-iedit-in-region`.
82 |
83 | ## v0.3.6
84 |
85 | Not documented.
86 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Contributions are welcome. If you discover bugs or issues, or have ideas for
4 | improvements or new features, please file a report on the issue tracker for this
5 | repository. Follow the guidelines below to make sure everything goes smoothly.
6 |
7 | ## Issue reporting
8 |
9 | - Check that the issue has not already been reported
10 | - Check that the issue has not already been fixed in the latest code
11 | - Open an issue with a clear title
12 | - Write as grammatically correct as you can in the description.
13 |
14 | ## Pull requests
15 |
16 | - Perform all changes on a topic branch for easier merging
17 | - Follow the coding conventions already in use
18 | - Verify Emacs Lisp code with `checkdoc`
19 | - Add unit tests whenever possible
20 | - Open a [pull request][] relating to a single issue.
21 |
22 | ## Coding Conventions
23 |
24 | ### Naming
25 |
26 | - Use a `emr-` prefix for all public names.
27 | - Use a `emr--` prefix for all internal names.
28 |
29 | ### Docstrings
30 |
31 | Write meaningful docstrings for all functions and vars.
32 |
33 | - Document all functions and variables as directed by `checkdoc`.
34 | - Consider using [Flycheck]() to automate `checkdoc` while you're editing.
35 |
36 | ### Common Lisp functions
37 |
38 | Use `cl-lib` instead of `cl`. The `cl` library pollutes the global namespace and
39 | its usage is therefore discouraged.
40 |
41 | - Use `cl-lib`, which adds prefixes to all cl function names
42 | - Use [noflet][] instead of `flet` when you need to dynamically rebind functions.
43 |
44 | [pull request]: https://help.github.com/articles/using-pull-requests
45 | [Flycheck]: https://github.com/flycheck/flycheck
46 | [noflet]: https://github.com/nicferrier/emacs-noflet
47 |
--------------------------------------------------------------------------------
/COPYING:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/Cask:
--------------------------------------------------------------------------------
1 | (source melpa)
2 | (package-file "emr.el")
3 |
4 | (development
5 | (depends-on "f")
6 | (depends-on "ert-runner")
7 | (depends-on "el-mock")
8 | (depends-on "cask-package-toolset")
9 | (depends-on "undercover"))
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # emacs-refactor
2 |
3 | [](https://coveralls.io/r/Wilfred/emacs-refactor)
4 | [](http://melpa.org/#/emr)
5 | [](http://stable.melpa.org/#/emr)
6 |
7 | Emacs Refactor (EMR) is a framework for providing language-specific
8 | refactoring in Emacs. It includes refactoring commands for a variety
9 | of languages, including elisp itself!
10 |
11 | - [Summary](#user-content-summary)
12 | - [Installation](#user-content-installation)
13 | - [Language support](#user-content-language-support)
14 | - [General](#user-content-general)
15 | - [C](#user-content-c)
16 | - [Lisps](#user-content-lisps)
17 | - [Elisp](#user-content-elisp)
18 | - [JavaScript](#user-content-javascript)
19 | - [Ruby](#user-content-ruby)
20 | - [Scheme](#user-content-scheme)
21 | - [Extension](#user-content-extension)
22 |
23 | ## Summary
24 |
25 | To use EMR when editing, simply move point to an expression and
26 | `M-x emr-show-refactor-menu`.
27 |
28 | ![Example][example-pic]
29 |
30 | EMR ships with many refactoring commands, and pull requests for extensions are
31 | welcome. See [Extension](#user-content-extension) for details on extending EMR
32 | to other language modes. It's easy (honest!).
33 |
34 | ## Installation
35 |
36 | Install `emr` from [MELPA](http://www.melpa.org/), then configure your
37 | init.el:
38 |
39 | ```emacs-lisp
40 | (define-key prog-mode-map (kbd "M-RET") 'emr-show-refactor-menu)
41 | ```
42 |
43 | ## Language support
44 |
45 | Most EMR commands are context-sensitive and are available through the
46 | refactor menu. Some actions affect the whole buffer and are available in
47 | the menu bar.
48 |
49 | ### General
50 |
51 | These commands are available for all programming languages.
52 |
53 | The following context-sensitive refactoring commands are available:
54 |
55 | * *comment region*
56 | * *uncomment region*
57 |
58 | ### C
59 |
60 | The following context-sensitive refactoring commands are available:
61 |
62 | * *tidy includes*
63 |
64 | The following buffer-wide actions are available:
65 |
66 | * *insert include*
67 |
68 | Refactoring support for C is a work in progress. Contributions are welcome.
69 |
70 | ### Lisps
71 |
72 | These commands are available to all Lisp dialects, including Clojure, Elisp
73 | and Common Lisp.
74 |
75 | The following context-sensitive refactoring commands are available:
76 |
77 | * *comment form*
78 | * *uncomment block*
79 |
80 | ### Elisp
81 |
82 | The following context-sensitive refactoring commands are available:
83 |
84 | * *delete unused definition*
85 | * *delete unused let binding form*
86 | * *eval and replace*
87 | * *extract autoload*
88 | * *extract constant*
89 | * *extract function*
90 | * *extract to let*
91 | * *extract variable*
92 | * *implement function*
93 | * *inline function*
94 | * *inline let variable*
95 | * *inline variable*
96 | * *insert autoload directive*
97 | * *tidy autoloads*
98 |
99 | The following buffer-wide actions are available:
100 |
101 | * *find unused definitions*
102 |
103 | ### JavaScript
104 |
105 | JavaScript refactoring support requires [js2 refactor][].
106 |
107 | The following refactoring commands are available:
108 |
109 | * *extract function*
110 | * *extract method*
111 | * *extract variable*
112 | * *add parameter*
113 | * *local variable to instance variable*
114 | * *log region*
115 |
116 | ### Ruby
117 |
118 | Ruby refactoring support requires [ruby refactor][].
119 |
120 | The following refactoring commands are available:
121 |
122 | * *extract function*
123 | * *extract variable*
124 | * *extract constant*
125 | * *add parameter*
126 | * *extract to let*
127 |
128 | ### Scheme
129 |
130 | The following refactoring commands are available:
131 |
132 | * *extract function*
133 | * *extract variable*
134 |
135 | ## Extension
136 |
137 | Use the `emr-declare-command` function to declare a refactoring action. The
138 | action will automatically become available in the refactoring popup menu.
139 |
140 | This function supports predicate expressions, allowing the options displayed to
141 | be context-sensitive.
142 |
143 | As an example, here is the declaration for a refactoring command that ships with
144 | EMR:
145 |
146 | ```lisp
147 | (emr-declare-command 'emr-el-extract-constant
148 | :title "constant"
149 | :description "defconst"
150 | :modes 'emacs-lisp-mode
151 | :predicate (lambda ()
152 | (not (or (emr-el:looking-at-definition?)
153 | (emr-el:looking-at-let-binding-symbol?)))))
154 | ```
155 |
156 | This wires the `emr-el-extract-constant` function to be displayed in
157 | `emacs-lisp-mode`, provided point is not looking at an Elisp definition or
158 | let-binding form.
159 |
160 | If your favourite language mode already offers refactoring commands, it is
161 | simple to wire them up with EMR using this interface.
162 |
163 | [example-pic]: https://raw.github.com/Wilfred/emacs-refactor/master/assets/emr.png
164 | [js2 refactor]: https://github.com/magnars/js2-refactor.el
165 | [ruby refactor]: https://github.com/ajvargo/ruby-refactor
166 | [git]: http://git-scm.com
167 |
--------------------------------------------------------------------------------
/assets/emr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wilfred/emacs-refactor/cac1b52932926f56d7f6d2923732d20bbd20670d/assets/emr.png
--------------------------------------------------------------------------------
/emr-c.el:
--------------------------------------------------------------------------------
1 | ;;; emr-c.el --- Refactoring commands for C -*- lexical-binding: t; -*-
2 |
3 | ;; Copyright (C) 2013 Chris Barrett
4 |
5 | ;; Author: Chris Barrett
6 |
7 | ;; This file is not part of GNU Emacs.
8 |
9 | ;; This program is free software: you can redistribute it and/or modify
10 | ;; it under the terms of the GNU General Public License as published by
11 | ;; the Free Software Foundation, either version 3 of the License, or
12 | ;; (at your option) any later version.
13 |
14 | ;; This program is distributed in the hope that it will be useful,
15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | ;; GNU General Public License for more details.
18 |
19 | ;; You should have received a copy of the GNU General Public License
20 | ;; along with this program. If not, see .
21 |
22 | ;;; Commentary:
23 |
24 | ;; Refactoring commands for C and C-Based modes.
25 |
26 | ;;; Code:
27 |
28 | (require 'emr)
29 | (require 's)
30 | (require 'dash)
31 | (require 'thingatpt)
32 | (autoload 'c-mode-map "cc-mode")
33 | (autoload 'projectile-dir-files "projectile")
34 | (autoload 'projectile-project-p "projectile")
35 |
36 | (defvar emr-c:standard-headers
37 | '("aio.h" "arpa/inet.h" "assert.h" "complex.h" "cpio.h" "ctype.h"
38 | "curses.h" "dirent.h" "dlfcn.h" "errno.h" "fcntl.h" "fenv.h" "float.h"
39 | "fmtmsg.h" "fnmatch.h" "ftw.h" "glob.h" "grp.h" "iconv.h" "inttypes.h"
40 | "iso646.h" "langinfo.h" "libgen.h" "limits.h" "locale.h" "math.h"
41 | "monetary.h" "mqueue.h" "ndbm.h" "net/if.h" "netdb.h" "netinet/in.h"
42 | "netinet/tcp.h" "nl_types.h" "poll.h" "pthread.h" "pwd.h" "regex.h"
43 | "sched.h" "search.h" "semaphore.h" "setjmp.h" "signal.h" "spawn.h"
44 | "stdalign.h" "stdarg.h" "stdatomic.h" "stdbool.h" "stddef.h" "stdint.h"
45 | "stdio.h" "stdlib.h" "stdnoreturn.h" "string.h" "strings.h" "stropts.h"
46 | "sys/ipc.h" "sys/mman.h" "sys/msg.h" "sys/resource.h" "sys/select.h"
47 | "sys/sem.h" "sys/shm.h" "sys/socket.h" "sys/stat.h" "sys/statvfs.h"
48 | "sys/time.h" "sys/times.h" "sys/types.h" "sys/uio.h" "sys/un.h"
49 | "sys/utsname.h" "sys/wait.h" "syslog.h" "tar.h" "term.h" "termios.h"
50 | "tgmath.h" "threads.h" "time.h" "trace.h" "uchar.h" "ulimit.h"
51 | "uncntrl.h" "unistd.h" "utime.h" "utmpx.h" "wchar.h" "wctype.h"
52 | "wordexp.h"))
53 |
54 |
55 | (defcustom emr-clang-format-style 'Google
56 | "Style used to format codes with clang.
57 | Refer to http://clang.llvm.org/docs/ClangFormatStyleOptions.html for more
58 | detailed descriptions."
59 | :type '(radio (const :tag "Format with style suggested by Google." Google)
60 | (const :tag "Format used by LLVM project." LLVM)
61 | (const :tag "Format used by Chromium project." Chromium)
62 | (const :tag "Format used by Mozilla project." Mozilla)
63 | (const :tag "Format used by Webkit project." WebKit)
64 | (const :tag "Load style configuration from file." file)
65 | (repeat :tag "Customized alist." (cons (regexp :tag "Tag")
66 | (directory :tag "Format"))))
67 |
68 | :group 'emr)
69 |
70 | (defvar emr-c-format-fallback-func 'indent-region
71 | "Function to indent a buffer region.
72 | Will be passed start and end positions of region to be formatted.")
73 |
74 | ; ------------------
75 |
76 | (defconst emr-c:rx-include
77 | (rx "#include" (+ space)
78 | (group-n 1
79 | (or "\"" "<") (* (not space)) (or "\"" ">"))))
80 |
81 | (defun emr-c:looking-at-include? ()
82 | (thing-at-point-looking-at emr-c:rx-include))
83 |
84 | (defun emr-c:bob-after-comments ()
85 | "Move to the first non-comment character in the buffer."
86 | (goto-char (point-min))
87 | (while (emr-looking-at-comment?)
88 | (forward-line 1))
89 | (point))
90 |
91 | (defun emr-c:goto-includes-or-buf-start ()
92 | (goto-char (point-min))
93 | (or (search-forward-regexp emr-c:rx-include nil t)
94 | (emr-c:bob-after-comments))
95 | (beginning-of-line)
96 | (point))
97 |
98 | ;;;###autoload
99 | (defun emr-c-tidy-includes ()
100 | "Collate and reorder include directives in the current buffer.
101 | Library and project includes are kept separate."
102 | (interactive "*")
103 | (let (includes)
104 | (save-excursion
105 | (emr-c:goto-includes-or-buf-start)
106 |
107 | ;; Collect include statements in buffer.
108 | (save-excursion
109 | (goto-char (point-min))
110 | (while (search-forward-regexp emr-c:rx-include nil t)
111 | (push (match-string 1) includes)
112 | (replace-match "")
113 | (when (emr-blank-line?)
114 | (ignore-errors
115 | (kill-line)))))
116 | ;; Partition includes by type, subsort alphabetically and insert into
117 | ;; buffer.
118 | (->> includes
119 | (--separate (s-starts-with? "<" it))
120 | (--map (sort it 'string<))
121 | (-flatten)
122 | (--map (concat "#include " it))
123 | (s-join "\n")
124 | (s-append "\n")
125 | (insert)))))
126 |
127 | ; ------------------
128 |
129 | (defun emr-c:headers-in-project ()
130 | "Return a list of available C header files.
131 |
132 | Find header files in the current project. If this is not a valid
133 | project, return all header files in the current directory."
134 | (->> (-if-let (proj (projectile-project-p))
135 | (--map (concat proj it) (projectile-dir-files proj))
136 | (-> (buffer-file-name) (file-name-directory) (directory-files t)))
137 | (--filter (-contains? '("h" "hpp") (file-name-extension it)))
138 | (-map 'file-relative-name)))
139 |
140 | ;;;###autoload
141 | (defun emr-c-insert-include (header)
142 | "Insert an include for HEADER and tidy the includes in the buffer."
143 | (interactive
144 | (list
145 | (if (yes-or-no-p "Library header?")
146 | (format "<%s>" (completing-read "Header: " emr-c:standard-headers))
147 | (format "\"%s\"" (completing-read "Header: " (emr-c:headers-in-project))))))
148 |
149 | (let ((str (concat "#include " header)))
150 | (when (s-contains? str (buffer-string))
151 | (user-error "%s is already included" header))
152 | (save-excursion
153 | (atomic-change-group
154 | (emr-reporting-buffer-changes "Inserted header"
155 | ;; Insert header.
156 | (emr-c:goto-includes-or-buf-start)
157 | (insert str)
158 | (newline)
159 | (emr-c-tidy-includes))))))
160 |
161 |
162 |
163 | ;;; EMR Declarations
164 |
165 | (autoload 'clang-format-region "clang-format" "" t)
166 | (autoload 'clang-format-buffer "clang-format" "" t)
167 |
168 | (defun emr-clang-available? ()
169 | "Return whether clang-format is available."
170 | (and (featurep 'clang-format)
171 | (executable-find "clang-format")))
172 |
173 | (defun emr-cc-get-style ()
174 | "Return style as a string."
175 | (cond
176 | ((stringp emr-clang-format-style) emr-clang-format-style)
177 | ((listp emr-clang-format-style)
178 | (concat "{"(mapconcat (lambda (x)
179 | (format "%s: %s" (car x) (cdr x)))
180 | emr-clang-format-style ", ") "}"))
181 | ((symbolp emr-clang-format-style) (symbol-name emr-clang-format-style))
182 | (t nil)))
183 |
184 | (defun emr-cc-format-region (start end)
185 | "Format region (START/END).
186 | Uses either clang-format, if available, or `emr-c-format-fallback-func'."
187 | (interactive "rp")
188 | (if (emr-clang-available?)
189 | (clang-format-region start end (emr-cc-get-style))
190 | (funcall emr-c-format-fallback-func start end)))
191 |
192 | (defun emr-cc-format-buffer ()
193 | "Format region (START/END).
194 | Uses either clang-format, if available, or `emr-c-format-fallback-func.'"
195 | (interactive)
196 | (if (emr-clang-available?)
197 | (clang-format-buffer (emr-cc-get-style))
198 | (funcall emr-c-format-fallback-func (point-min) (point-max))))
199 |
200 | (defalias 'emr-cc-tidy-includes 'emr-c-tidy-includes)
201 |
202 | (defvar emr-cc-surround-var-hist nil
203 | "A collection of variables used by if-defs..")
204 |
205 | (defun emr-cc-surround-if-end (start end)
206 | "Surround region between START & END with if-def."
207 | (interactive "rp")
208 | (let ((content (buffer-substring-no-properties start end))
209 | (var (completing-read "Variable Name: " emr-cc-surround-var-hist
210 | nil nil nil 'emr-cc-surround-var-hist)))
211 | (save-excursion
212 | (delete-region start end)
213 | (insert (format "#ifdef %s\n" var))
214 | (insert content)
215 | (insert (format "\n#endif /*%s*/" var))
216 | (emr-cc-format-region start (point)))))
217 |
218 | (defun emr-cpp-try-catch (start end)
219 | "Surround region between START & END with try-catch."
220 | (interactive "rp")
221 | (let ((content (buffer-substring-no-properties start end)))
222 | (save-excursion
223 | (delete-region start end)
224 | (insert "try {\n")
225 | (insert content)
226 | (insert
227 | "}\ncatch (exception& e) {\n")
228 | (insert "throw ;\n}\n")
229 | (emr-cc-format-region start (point)))))
230 |
231 | (defun emr-region-active? ()
232 | "Return t if a valid region is active."
233 | (and mark-active (not (equal (mark) (point)))))
234 | (defun emr-region-inactive? ()
235 | "Return nil if a valid region is active."
236 | (not (emr-region-active?)))
237 |
238 | ; ------------------
239 |
240 | ;;; EMR Declarations
241 |
242 | (emr-declare-command 'emr-cc-tidy-includes
243 | :title "tidy"
244 | :description "includes"
245 | :modes '(c++-mode c-mode)
246 | :predicate (lambda ()
247 | (emr-c:looking-at-include?)))
248 |
249 | (emr-declare-command 'emr-cc-format-region
250 | :title "format region"
251 | :description (if (emr-clang-available?)
252 | "with clang"
253 | "with the value of emr-c-format-fallback-func")
254 | :modes '(c-mode c++-mode)
255 | :predicate 'emr-region-active?)
256 |
257 | (emr-declare-command 'emr-cc-format-buffer
258 | :title "format buffer"
259 | :description (if (emr-clang-available?)
260 | "with clang"
261 | "with the value of emr-c-format-fallback-func")
262 | :modes '(c-mode c++-mode)
263 | :predicate 'emr-region-inactive?)
264 |
265 | (emr-declare-command 'emr-cc-surround-if-end
266 | :title "surround"
267 | :description "with if-endif"
268 | :modes '(c++-mode c-mode)
269 | :predicate 'emr-region-active?)
270 |
271 | (emr-declare-command 'emr-cpp-try-catch
272 | :title "surround"
273 | :description "with try-catch"
274 | :modes '(c++-mode)
275 | :predicate 'emr-region-active?)
276 |
277 | (emr-declare-command 'emr-c-insert-include
278 | :title "insert header"
279 | :description "#include"
280 | :modes '(c-mode)
281 | :predicate (lambda () t))
282 |
283 | ;; ------------------
284 |
285 | (defun emr-c:show-menu ()
286 | (when (boundp 'c-mode-map)
287 | (easy-menu-add-item
288 | nil
289 | '("EMR")
290 | ["Insert #include" emr-c-insert-include])))
291 |
292 | ;;;###autoload
293 | (defun emr-c-initialize ()
294 | "Initialize EMR in C buffers and enable the EMR menu."
295 | (add-hook 'c-mode-hook 'emr-c:show-menu)
296 | (--each (buffer-list)
297 | (with-current-buffer it
298 | (when (derived-mode-p 'c-mode)
299 | (emr-c:show-menu)))))
300 |
301 | (provide 'emr-c)
302 |
303 | ;;; emr-c.el ends here
304 |
--------------------------------------------------------------------------------
/emr-css.el:
--------------------------------------------------------------------------------
1 | ;;; emr-css.el --- Refactoring commands for CSS -*- lexical-binding: t; -*-
2 |
3 | ;; Copyright (C) 2016 Wilfred Hughes
4 |
5 | ;; Author: Wilfred Hughes .
21 |
22 | ;;; Commentary:
23 |
24 | ;; Refactoring commands for CSS.
25 |
26 | ;;; Code:
27 |
28 | (require 'emr)
29 | (require 's)
30 | (require 'dash)
31 | (require 'thingatpt)
32 |
33 | ;;;###autoload
34 | (defun emr-css-toggle-important ()
35 | "Add or remove !important on the property at point."
36 | (interactive "*")
37 | (save-excursion
38 | (end-of-line)
39 | (backward-char 1)
40 | (if (looking-back "!important")
41 | (delete-char (- (length " !important")))
42 | (insert " !important"))))
43 |
44 | (provide 'emr-css)
45 |
46 | ;;; emr-css.el ends here
47 |
--------------------------------------------------------------------------------
/emr-elisp.el:
--------------------------------------------------------------------------------
1 | ;;; emr-elisp.el --- Refactoring commands for Emacs Lisp -*- lexical-binding: t; -*-
2 |
3 | ;; Copyright (C) 2013 Chris Barrett
4 | ;; Copyright (C) 2018 Wilfred Hughes
5 |
6 | ;; Author: Chris Barrett
7 |
8 | ;; This file is not part of GNU Emacs.
9 |
10 | ;; This program is free software: you can redistribute it and/or modify
11 | ;; it under the terms of the GNU General Public License as published by
12 | ;; the Free Software Foundation, either version 3 of the License, or
13 | ;; (at your option) any later version.
14 |
15 | ;; This program is distributed in the hope that it will be useful,
16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | ;; GNU General Public License for more details.
19 |
20 | ;; You should have received a copy of the GNU General Public License
21 | ;; along with this program. If not, see .
22 |
23 | ;;; Commentary:
24 |
25 | ;; Refactoring commands for Emacs Lisp
26 |
27 | ;;; Code:
28 |
29 | (require 's)
30 | (require 'list-utils)
31 | (require 'dash)
32 | (require 'thingatpt)
33 | (require 'compile)
34 | (require 'emr)
35 | (require 'emr-lisp)
36 | (autoload 'paredit-splice-sexp-killing-backward "paredit")
37 |
38 | (defcustom emr-el-definition-macro-names
39 | '(defun defun* cl-defun defmacro defmacro* cl-defmacro defcustom
40 | defvar defvar-local defconst defsubst defsubst* cl-defsubst)
41 | "Lists the function, macro and variable definition forms in Elisp.
42 | Used when searching for usages across the whole buffer."
43 | :group 'emr)
44 |
45 | (defun emr-el:safe-read (sexp)
46 | "A wrapper around `read' that return nil immediately if SEXP is null.
47 |
48 | If sexp is nil, `read' would prompt the user for input from stdin.
49 | Bad."
50 | (and sexp (read sexp)))
51 |
52 | (defun emr-el:print (form)
53 | "Print FORM as a Lisp expression."
54 | (let (
55 | ;; Print forms to any depth.
56 | (print-quoted t)
57 | (print-level nil)
58 | (print-length nil)
59 | (print-escape-newlines t)
60 | )
61 | (prin1-to-string form)))
62 |
63 | ;;;; Navigation commands
64 |
65 | (defun emr-el:goto-first-match (regex)
66 | "Move point to the first match in the buffer for REGEX."
67 | (save-match-data
68 | (when (string-match regex (buffer-string) 0)
69 | (goto-char (match-beginning 0)))))
70 |
71 | (defun emr-el:looking-at-decl? ()
72 | (-contains? '(interactive declare) (car-safe (list-at-point))))
73 |
74 | ;;;; Formatting commands
75 |
76 | (defun emr-el:symbol-file-name (fn)
77 | "Find the name of the file that declares function FN."
78 | (-when-let (file (find-lisp-object-file-name fn (symbol-function fn)))
79 | (and (stringp file)
80 | (file-name-nondirectory (file-name-sans-extension file)))))
81 |
82 | (defvar emr-el:special-symbols
83 | '(--cl-rest-- &rest &optional &key &allow-other-keys \,\@ \,)
84 | "A list of symbols that should be ignored by variable searches.")
85 |
86 | (cl-defun emr-el:bindings-in-lambda ((_lam arglist &rest body))
87 | "Return all bound variables within a lambda form."
88 | (let ((bs (-difference arglist emr-el:special-symbols)))
89 | (-concat bs (emr-el:bound-variables body))))
90 |
91 | (cl-defun emr-el:bindings-in-let ((_let bindings &rest body))
92 | "Return the list of bound values in the given `let' or `let*' expression."
93 | (-concat (emr-el:let-binding-list-symbols bindings)
94 | (emr-el:bound-variables body)))
95 |
96 | (cl-defun emr-el:bindings-in-defalias ((_def (_quote sym) func))
97 | "Return the bindings in a defalias form, including the named alias."
98 | (cons sym (emr-el:bound-variables func)))
99 |
100 | (defun emr-el:bound-variables (form)
101 | "Find the list of let- or lambda-bound variables in FORM."
102 | ;; Form traversal can recur infinitely in some quotation scenarios. In
103 | ;; such cases it is not a problem to bail and unwind the stack.
104 | (ignore-errors
105 | (-uniq
106 | (let* (
107 | ;; Handle errors in expansion. Expansion errors are common with syntax
108 | ;; quotes, for example.
109 | (form (or (ignore-errors (macroexpand-all form))
110 | (ignore-errors (macroexpand form))
111 | form))
112 |
113 | (hd (car-safe form)))
114 | (cond
115 | ((equal 'lambda hd) (emr-el:bindings-in-lambda form))
116 | ((equal 'let hd) (emr-el:bindings-in-let form))
117 | ((equal 'let* hd) (emr-el:bindings-in-let form))
118 | ((equal 'defalias hd) (emr-el:bindings-in-defalias form))
119 | ;; `function' is the quotation form for function objects.
120 | ;; Do not bail if the next item is not a lambda.
121 | ((equal 'function hd) (condition-case _err
122 | (-mapcat 'emr-el:bindings-in-lambda (cdr form))
123 | (error
124 | (-mapcat 'emr-el:bound-variables (cdr form)))))
125 | ;; FORM is probably a value if we're not looking at a list, and can be
126 | ;; ignored.
127 | ((listp form)
128 | (->> form
129 | ;; Handle improper lists.
130 | (list-utils-make-proper-copy)
131 | (-mapcat 'emr-el:bound-variables))))))))
132 |
133 | (defun emr-el:unquoted-symbols (form)
134 | "Return a list of every unquoted symbol in FORM."
135 | (let (syms
136 | (forms-remaining (list form)))
137 | (while (not (null forms-remaining))
138 | (let ((subform (pop forms-remaining)))
139 | (cond
140 | ;; Skip quoted symbols.
141 | ((and (consp subform) (eq (car subform) 'quote)))
142 | ;; Iterate on the subforms for lists.
143 | ((consp subform)
144 | (push (cdr subform) forms-remaining)
145 | (push (car subform) forms-remaining))
146 | ;; If this node is a symbol, add it to our list.
147 | ((and subform (symbolp subform))
148 | (push subform syms)))))
149 | syms))
150 |
151 | (defun emr-el:free-variables (form &optional context)
152 | "Try to find the symbols in FORM that do not have variable bindings.
153 | CONTEXT is the top level form that encloses FORM."
154 |
155 | ;; Macro-expand FORM and find the list of bound symbols. Diff this with the
156 | ;; other symbols in FORM. Figure out which ones are not functions, keywords,
157 | ;; special vars, etc. This should give a pretty good idea of which symbols are
158 | ;; 'free'.
159 |
160 | (let ((bound-vars (emr-el:bound-variables form))
161 | (ctx-bound (emr-el:bound-variables context))
162 | (form-syms (emr-el:unquoted-symbols form)))
163 | (->> (or (ignore-errors (macroexpand-all form)) form)
164 | ;; Get all symbols from FORM's macro-expansion.
165 | (list)
166 | (list-utils-make-proper-inplace)
167 | (-flatten)
168 | (-filter 'symbolp)
169 | (-distinct)
170 | ;; Only use symbols present in the original form. This prevents free vars
171 | ;; from the macro-expansion leaking in.
172 | (--filter (-contains? form-syms it))
173 | ;; Finally, reduce the candidates.
174 | (--remove (or (-contains? bound-vars it)
175 | (-contains? emr-el:special-symbols it)
176 | (booleanp it)
177 | (keywordp it)
178 | ;; Remove special vars and function names, unless they've
179 | ;; been bound in the enclosing form.
180 | (unless (-contains? ctx-bound it)
181 | (or
182 | (special-variable-p it)
183 | (ignore-errors
184 | (symbol-function it)))))))))
185 |
186 | ;;;; Definition site tests
187 |
188 | (defun emr-el:macro-definition? (form)
189 | "Return t if FORM expands to a macro definition."
190 | (ignore-errors
191 | (let* ((exp (macroexpand-all form))
192 | ;; Skip surrounding `prog1'. This will exist if the macro has
193 | ;; `declare' specs.
194 | (exp (if (equal 'prog1 (car exp))
195 | (cdr exp)
196 | exp)))
197 | ;; A macro expands to a defalias.
198 | (cl-destructuring-bind (&optional def _sym binding &rest rest) exp
199 | ;; The binding is a call to `cons'. The first arg is the quoted
200 | ;; symbol `macro'.
201 | (cl-destructuring-bind (&optional _cons mac &rest lambda) binding
202 | (and (equal 'defalias def)
203 | ;; NB quoted symbol 'macro must be quoted twice for comparison.
204 | (equal ''macro mac)))))))
205 |
206 | (defun emr-el:function-definition? (form)
207 | "Return t if FORM expands to a function definition."
208 | (ignore-errors
209 | (let ((exp (macroexpand-all form)))
210 | (and (equal 'defalias (car exp))
211 | (equal 'function (caaddr exp))))))
212 |
213 | (defun emr-el:variable-definition? (form)
214 | (ignore-errors
215 | (-contains? '(defconst defvar defcustom)
216 | (car (macroexpand-all form)))))
217 |
218 | (defun emr-el:definition? (form)
219 | "Return non-nil if FORM is a definition."
220 | (or (emr-el:variable-definition? form)
221 | (emr-el:macro-definition? form)
222 | (emr-el:function-definition? form)))
223 |
224 | (defun emr-el:looking-at-definition? ()
225 | "Non-nil if point is at a definition form."
226 | (ignore-errors (emr-el:definition? (list-at-point))))
227 |
228 | ;;;; Refactoring commands
229 |
230 | (defun emr-el:extract-var-values (sexp)
231 | "Return the name and initializing value of SEXP if it is a variable definition."
232 | (let ((exp (macroexpand-all sexp)))
233 | (when (emr-el:variable-definition? exp)
234 | (cl-destructuring-bind (_def sym &rest forms) exp
235 | (cons sym (car forms))))))
236 |
237 | (cl-defun emr-el:replace-usages ((sym . value))
238 | "Replace all instances of SYM with VALUE in the current buffer.
239 | Returns a list of lines where changes were made."
240 | (save-excursion
241 | (goto-char (point-min))
242 | (save-match-data
243 | (let ((match-sym
244 | (rx-to-string
245 | `(seq (not (any "(")) (* space)
246 | (group symbol-start ,(symbol-name sym)
247 | symbol-end))))
248 | (lines))
249 | ;; Check for "(" since we don't want to replace function calls.
250 | (while (and (search-forward-regexp match-sym nil t)
251 | (match-data))
252 | (setq lines (cons (line-number-at-pos) lines))
253 | ;; Perform replacement.
254 | (replace-match (emr-el:print value) t nil nil 1)
255 | (emr-lisp-reindent-defun))
256 | (nreverse lines)))))
257 |
258 | ;;;###autoload
259 | (defun emr-el-inline-variable ()
260 | "Inline the variable defined at point.
261 |
262 | Uses of the variable in the current buffer are replaced with the
263 | initvalue in the variable definition.
264 |
265 | EXAMPLE:
266 |
267 | (emr-el-inline-variable)
268 |
269 | BEFORE:
270 |
271 | (defvar x| value)
272 |
273 | (usage x)
274 |
275 | AFTER:
276 |
277 | (usage value)"
278 | (interactive "*")
279 | (save-excursion
280 | (emr-lisp-back-to-open-round)
281 | (-if-let (def (emr-el:extract-var-values (list-at-point)))
282 | (if (or (consp def) (> (length def) 1))
283 | (emr-lisp-extraction-refactor () "Inlining applied at"
284 |
285 | ;; Clean up line spacing.
286 | (while (emr-blank-line?)
287 | (kill-line))
288 |
289 | ;; Perform inlining.
290 | ;;
291 | ;; emr-lisp-extraction-refactor will report the first insertion. If
292 | ;; there are none or more than one insertion, override this report.
293 | (-if-let (lines (-map 'int-to-string (emr-el:replace-usages def)))
294 | (when (> (length lines) 1)
295 | (message "Inlining applied at lines %s" (s-join ", " lines)))
296 | (user-error "No usages found")))
297 |
298 | (user-error "No value to inline for %s" (car def)))
299 | (user-error "Not a variable definition"))))
300 |
301 | ; ------------------
302 |
303 | (defun emr-el:eval-and-print-progn (prog)
304 | "Eval and print each form in sexp PROG."
305 | (->> (macroexp-unprogn prog)
306 | (-map 'eval)
307 | (-remove 'null)
308 | (-map 'emr-el:print)))
309 |
310 | ;;;###autoload
311 | (defun emr-el-eval-and-replace ()
312 | "Replace the current region or the form at point with its value.
313 |
314 | EXAMPLE:
315 |
316 | (emr-el-eval-and-replace)
317 |
318 | BEFORE:
319 |
320 | (+ (+ 1 2)| 3)
321 |
322 | AFTER:
323 |
324 | (+ 3 3)"
325 | (interactive "*")
326 | (emr-lisp-extraction-refactor (sexp) "Replacement at"
327 |
328 | (-if-let (form (emr-el:safe-read sexp))
329 | (progn
330 | (insert (->> form (emr-el:eval-and-print-progn) (s-join "\n")))
331 | (indent-for-tab-command)
332 | (emr-lisp-reindent-defun))
333 | (user-error "Unable to read the given form"))))
334 |
335 | ; ------------------
336 |
337 | (defun emr-el:read-with-default (prompt value)
338 | "Prompt for user input, showing PROMPT with an inline default VALUE."
339 | (let ((val (s-trim (format "%s" value))))
340 | (s-trim
341 | (if (s-blank? val)
342 | (read-string (format "%s: " prompt))
343 | (read-string (format "%s (default: %s): " prompt val) nil nil val)))))
344 |
345 | (defun emr-el:format-submitted-arglist (arglist)
346 | "Format a user-submitted ARGLIST, raising an error if it is malformed."
347 | (unless (or (s-blank? arglist)
348 | (s-matches? (rx (or "()" "nil")) arglist))
349 | (condition-case _err
350 | (emr-el:safe-read (format "(%s)" arglist))
351 | (error
352 | ;; Rethrow reader errors as something more informative.
353 | (user-error "Malformed arglist")))))
354 |
355 | (defun emr-el:read-args (form context)
356 | "Read an arglist from the user, using FORM to generate a suggestion.
357 | CONTEXT is the top level form that encloses FORM."
358 | (let ((input
359 | ;; Generate suggested arglist for prompt.
360 | (->> (emr-el:free-variables form context)
361 | (-map 'symbol-name)
362 | (s-join " ")
363 | (s-trim)
364 | ;; Read user input, supplying default arglist.
365 | (emr-el:read-with-default "Arglist" )))
366 | )
367 | (emr-el:format-submitted-arglist input)))
368 |
369 | (defun emr-el:format-defun (defun-str)
370 | "Format DEFUN-STR to a prettier defun representation."
371 | (replace-regexp-in-string
372 | (rx bol "(" (* nonl) "def" (* nonl) (group "nil" (* space)) eol)
373 | "()"
374 | defun-str t nil 1))
375 |
376 | (defun emr-el:form-extent-for-extraction ()
377 | "Return either the current region or the list at point."
378 | (if (region-active-p)
379 | (emr-line-str)
380 | (list-at-point)))
381 |
382 | (defun emr-el:unprogn (str)
383 | (with-temp-buffer
384 | (save-excursion (insert str))
385 | (forward-word)
386 | (when (thing-at-point-looking-at "progn")
387 | (ignore-errors
388 | (paredit-splice-sexp-killing-backward)))
389 | (buffer-string)))
390 |
391 | ;;;###autoload
392 | (defun emr-el-extract-function (name arglist)
393 | "Extract a function, using the current region or form at point as the body.
394 | NAME is the name of the new function.
395 | ARGLIST is its argument list.
396 |
397 | EXAMPLE:
398 |
399 | (emr-el-extract-function \"extracted\" '(x))
400 |
401 | BEFORE:
402 |
403 | (defun orig (x)
404 | (application| x))
405 |
406 | AFTER:
407 |
408 | (defun extracted (x)
409 | (application x))
410 |
411 | (defun orig (x)
412 | (extracted x))"
413 | (interactive
414 | (list
415 | ;; Function name.
416 | (read-string "Name: ")
417 |
418 | ;; Prompt user with default arglist.
419 | (emr-el:read-args (emr-el:form-extent-for-extraction)
420 | (thing-at-point 'defun))))
421 |
422 | (when (s-blank? name)
423 | (user-error "Name must not be blank"))
424 |
425 | (emr-lisp-extraction-refactor (sexp) "Extracted to"
426 | (let ((name (intern name))
427 | ;; Extract to a `cl-defun' if given a Common Lisp-style arglist.
428 | (defun-form (if (-any? 'listp arglist) 'cl-defun 'defun))
429 | (body (emr-el:unprogn sexp)))
430 | ;; Insert usage at point.
431 | (insert (emr-el:print (cl-list* name arglist)))
432 | ;; Insert defun.
433 | (->> (format "(%s %s %s\n %s)" defun-form name arglist body)
434 | (emr-el:format-defun)
435 | (emr-lisp-insert-above-defun)))))
436 |
437 | ; ------------------
438 |
439 | (defun emr-el:infer-arglist-for-usage (form)
440 | "Suggest a suitable arglist for the given function application FORM."
441 | (->> form
442 | ;; Anything that isn't a symbol becomes 'argn'.
443 | (--map-indexed (if (symbolp it) it (intern (format "arg%s" it-index))))
444 | ;; Drop function name.
445 | (-drop 1)))
446 |
447 | ;;;###autoload
448 | (defun emr-el-implement-function (name arglist)
449 | "Create a function definition for the symbol at point.
450 | The function will be called NAME and have the given ARGLIST.
451 |
452 | EXAMPLE:
453 |
454 | (emr-el-implement-function \"hello\" '(x y))
455 |
456 | BEFORE:
457 |
458 | |(hello x y)
459 |
460 | AFTER:
461 |
462 | (defun hello (x y)
463 | )
464 |
465 | (hello x y)"
466 | (interactive (list
467 | (emr-el:safe-read
468 | (emr-el:read-with-default "Name" (symbol-at-point)))
469 | ;; Infer arglist from usage.
470 | (->> (list-at-point)
471 | (emr-el:infer-arglist-for-usage)
472 | (-map 'symbol-name)
473 | (s-join " ")
474 | (s-trim)
475 | (emr-el:read-with-default "Arglist")
476 | (emr-el:format-submitted-arglist))))
477 | ;; Determine which defun form to use.
478 | (let ((defun-form (if (-any? 'listp arglist) 'cl-defun 'defun))
479 | pos)
480 |
481 | ;; Insert usage and defun, then move to the point to the body of the defun.
482 |
483 | (save-excursion
484 | ;; Mark whole list at point.
485 | (beginning-of-thing 'sexp)
486 | (mark-sexp)
487 |
488 | (emr-lisp-extraction-refactor () "Defined function"
489 |
490 | ;; Insert reference. Quote the symbol if it's not in the funcall
491 | ;; position.
492 | (if (thing-at-point-looking-at "(")
493 | (insert (format "%s" name))
494 | (insert (format "#'%s" name)))
495 |
496 | ;; Insert definition.
497 | (setq pos (->> (format "(%s %s %s\n )" defun-form name arglist)
498 | (emr-el:format-defun)
499 | (emr-lisp-insert-above-defun)))))
500 |
501 | ;; Move to end of inserted form.
502 | (goto-char pos)
503 | ;; Move to body position.
504 | (beginning-of-defun)
505 | (forward-line 1)
506 | (indent-for-tab-command)))
507 |
508 | ; ------------------
509 |
510 | ;;;###autoload
511 | (defun emr-el-extract-variable (name)
512 | "Extract the current region or form at point to a special variable.
513 | The variable will be called NAME.
514 |
515 | EXAMPLE:
516 |
517 | (emr-el-extract-variable \"x\")
518 |
519 | BEFORE:
520 |
521 | (usage (+ 1 2)|)
522 |
523 | AFTER:
524 |
525 | (defvar x (+ 1 2))
526 |
527 | (usage x)"
528 | (interactive "*sName: ")
529 | (when (s-blank? name)
530 | (user-error "Name must not be blank"))
531 | (emr-lisp-extraction-refactor (sexp) "Extracted to"
532 | ;; Insert usage.
533 | (insert (s-trim name))
534 | ;; Insert definition.
535 | (emr-lisp-insert-above-defun (format "(defvar %s %s)" name sexp))))
536 |
537 | ;;;###autoload
538 | (defun emr-el-extract-constant (name)
539 | "Extract the current region or form at point to a constant special variable.
540 | The variable will be called NAME.
541 |
542 | EXAMPLE:
543 |
544 | (emr-el-extract-constant \"x\")
545 |
546 | BEFORE:
547 |
548 | (usage (+ 1 2)|)
549 |
550 | AFTER:
551 |
552 | (defconst x (+ 1 2))
553 |
554 | (usage x)"
555 | (interactive "*sName: ")
556 | (when (s-blank? name)
557 | (user-error "Name must not be blank"))
558 | (emr-lisp-extraction-refactor (sexp) "Extracted to"
559 | ;; Insert usage
560 | (insert (s-trim name))
561 | ;; Insert definition.
562 | (emr-lisp-insert-above-defun (format "(defconst %s %s)" name sexp))))
563 |
564 | ; ------------------
565 |
566 | (defun emr-el:autoload-exists? (function str)
567 | "Return non-nil if an autoload for FUNCTION exists in string STR."
568 | (s-contains? (format "(autoload '%s " function) str))
569 |
570 | (defun emr-el:beginning-of-defun ()
571 | "A safe version of `beginning-of-defun'.
572 | Attempts to find an enclosing defun form first, rather than
573 | relying on indentation."
574 | (or
575 | ;; Search for known defun form enclosing point.
576 | (cl-loop
577 | while (ignore-errors (backward-up-list) t)
578 | do (when (thing-at-point-looking-at
579 | (rx-to-string `(seq "(" (or ,@(-map 'symbol-name emr-el-definition-macro-names)))))
580 | (cl-return (point))))
581 | ;; Fall back to using indentation.
582 | (ignore-errors
583 | (beginning-of-thing 'defun))))
584 |
585 | (defun emr-el:autoload-directive-exsts-above-defun? ()
586 | "Non-nil if the current defun is preceeded by an autoload directive."
587 | (save-excursion
588 | (emr-el:beginning-of-defun)
589 | (forward-line -1)
590 | (emr-line-matches? (rx bol (* space) ";;;###autoload" (* space) eol))))
591 |
592 | ;;;###autoload
593 | (defun emr-el-insert-autoload-directive ()
594 | "Insert an autoload directive above the current defun, macro or keymap.
595 |
596 | EXAMPLE:
597 |
598 | (emr-el-insert-autoload-directive)
599 |
600 | BEFORE:
601 |
602 | (defun hello| ())
603 |
604 | AFTER:
605 |
606 | ;;;###autoload
607 | (defun hello ())"
608 | (interactive "*")
609 | (unless (emr-el:autoload-directive-exsts-above-defun?)
610 | (emr-reporting-buffer-changes "Inserted autoload"
611 | (save-excursion
612 | (beginning-of-thing 'defun)
613 | (open-line 1)
614 | (insert ";;;###autoload")))))
615 |
616 | (defun emr-el:sort-autoloads (autoloads)
617 | (let ((file-grouping (->> autoloads (--group-by (nth 1 it)))))
618 | ;; Order by file name...
619 | (->> (sort file-grouping (lambda (L R) (string< (car L) (car R))))
620 | ;; ...then by function name.
621 | (-mapcat (lambda (assoc)
622 | (sort (cdr assoc) (lambda (L R) (string< (car L) (car R))))))
623 | ;; Trim all components.
624 | (-map (lambda (xs) (--map (s-trim it) xs)))
625 | ;; ;; Recombine and insert into buffer.
626 | (--map (cl-destructuring-bind (fname file &optional rest) it
627 | (if (not (s-blank? rest))
628 | (format "(autoload %s %s %s)" fname file rest)
629 | (format "(autoload %s %s)" fname file)))))))
630 |
631 | ;;;###autoload
632 | (defun emr-el-tidy-autoloads ()
633 | "Consolidate and reorder autoloads in the current buffer.
634 | Order autoloads alphabetically by their file, then by their function name."
635 | (interactive "*")
636 | (let (autoloads
637 | (rx-autoload (rx bol (* space) "(autoload" (+ space)
638 | (group-n 1 (+ (not space)))
639 | (+ space)
640 | (group-n 2 (+ (not space)))
641 | (* space)
642 | (group-n 3 (*? nonl))
643 | ")")))
644 | (save-excursion
645 | (when (emr-el:goto-first-match rx-autoload)
646 | (beginning-of-line)
647 | (forward-line)
648 |
649 | ;; Collect autoloads in buffer.
650 | (save-excursion
651 | (goto-char (point-min))
652 | (while (search-forward-regexp rx-autoload nil t)
653 | (push (list (match-string 1) (match-string 2) (match-string 3))
654 | autoloads)
655 | (replace-match "")
656 | (when (emr-blank-line?)
657 | (ignore-errors
658 | (kill-line)))))
659 |
660 | (->> (emr-el:sort-autoloads autoloads)
661 | (-distinct)
662 | (s-join "\n")
663 | (s-append "\n")
664 | (insert))))))
665 |
666 | ;;;###autoload
667 | (defun emr-el-extract-autoload (function file)
668 | "Create an autoload for FUNCTION and insert it into the buffer.
669 | FILE is the file that declares FUNCTION. See `autoload' for
670 | details.
671 |
672 | * If there are no autoloads in the buffer, the new autoload will
673 | be inserted above the current toplevel form.
674 |
675 | * If other autoloads exist in the buffer, the new autoload will
676 | be inserted near them."
677 | (interactive
678 | (let* ((sym (intern (or (thing-at-point 'symbol) (read-string "Function: "))))
679 | (file (or (emr-el:symbol-file-name sym)
680 | (read-string "File: "))))
681 | (list sym file)))
682 |
683 | (let ((form `(autoload ',function ,file)))
684 | (save-excursion
685 | (emr-reporting-buffer-changes "Extracted to"
686 | ;; Put the extraction next to existing autoloads if any, otherwise
687 | ;; insert above top-level form.
688 | (if (emr-el:goto-first-match "^(autoload ")
689 | (progn (forward-line 1) (end-of-line) (newline)
690 | (insert (emr-el:print form)))
691 | (emr-lisp-insert-above-defun
692 | (emr-el:print form)))))
693 |
694 | (emr-el-tidy-autoloads)))
695 |
696 | ; ------------------
697 |
698 | (defun emr-el:first-atom (form)
699 | "Return the first atom the car of FORM, at any level of nesting."
700 | (if (listp form)
701 | (car-safe (-flatten form))
702 | form))
703 |
704 | (defun emr-el:let-binding-list-symbols (binding-forms)
705 | "Return the symbols defined in the given let BINDING-FORMS."
706 | (->> binding-forms
707 | (--map (or (car-safe it) it))
708 | (-remove 'null)))
709 |
710 | (defconst emr-el:scope-boundary-forms
711 | '(lambda defun cl-defun defun* defmacro cl-defmacro defmacro*
712 | let let* save-excursion unwind-protect
713 | flet cl-flet cl-flet* cl-labels labels
714 | ert-deftest)
715 | "A list of forms that define some kind of scope or context.
716 | They will bound upward searches when looking for places to insert let forms.")
717 |
718 | (defun emr-lisp-peek-back-upwards ()
719 | "Return the car of the enclosing form."
720 | (save-excursion
721 | (when (ignore-errors (backward-up-list) t)
722 | (forward-char 1)
723 | (sexp-at-point))))
724 |
725 | (defun emr-el:simplify-let-form-at-point ()
726 | "Tidy the let form at point.
727 | If it has no bindings, splice its contents into the surrounding
728 | form or replace with `progn'."
729 | (save-excursion
730 | (emr-el:goto-start-of-let-binding)
731 | ;; Move into list.
732 | (forward-char 1)
733 | (-let (((let-keyword bindings . body) (list-at-point)))
734 | ;; Move to position after bindings list.
735 | (forward-list 1)
736 | (cond
737 | ;; If there are no bindings, splice the body forms into the
738 | ;; surrounding context if any of the following are true:
739 | ;;
740 | ;; 1. the let body has only a single form
741 | ;;
742 | ;; 2. the let expression is an &body or &rest argument to the
743 | ;; enclosing form.
744 | ;;
745 | ((and (null bindings)
746 | (or (>= 1 (length body))
747 | (-contains? (cons 'progn emr-el:scope-boundary-forms)
748 | (emr-lisp-peek-back-upwards))))
749 | (paredit-splice-sexp-killing-backward))
750 |
751 | ;; Replace `let*' with `let' if there's only a single binding form.
752 | ((equal 1 (length bindings))
753 | (when (eq let-keyword 'let*)
754 | (emr-el-toggle-let*)))
755 |
756 | ;; Otherwise replace `let' with `progn'.
757 | ((null bindings)
758 | (backward-kill-sexp 2)
759 | (insert "progn"))))
760 |
761 | (emr-lisp-reindent-defun)))
762 |
763 | (defun emr-el:join-line-after-let-binding-kill ()
764 | "Tidy up newlines after modifiying a let-form binding list."
765 | (when (or (emr-blank-line?)
766 | (emr-line-matches? (rx "(" (* space) eol))
767 | (emr-line-matches? (rx bol (* space) ")" (* space) eol)))
768 | (forward-char 1)
769 | (join-line)))
770 |
771 | ;;;###autoload
772 | (defun emr-el-delete-let-binding-form ()
773 | "Delete the let binding around point."
774 | (interactive "*")
775 | (cl-assert (emr-el:looking-at-let-binding-symbol?))
776 | (let ((kr kill-ring))
777 | (unwind-protect
778 | (save-excursion
779 | ;; Delete binding.
780 | (emr-lisp-back-to-open-round)
781 | (kill-sexp)
782 |
783 | ;; Reformat after kill.
784 | (emr-el:join-line-after-let-binding-kill)
785 |
786 | ;; Ensure whole form is correctly reindented.
787 | (mark-defun)
788 | (indent-region (region-beginning) (region-end)))
789 |
790 | ;; Restore kill-ring.
791 | (setq kill-ring kr)
792 | (emr-el:simplify-let-form-at-point))))
793 |
794 | ; ------------------
795 |
796 | (defun emr-el:goto-containing-body-form ()
797 | "Search upwards for the first function or macro declaration enclosing point.
798 | Move to that body form that encloses point."
799 | ;; Ensure we're at the start of the current symbol.
800 | (when (looking-at (rx symbol-end))
801 | (backward-sexp))
802 | (catch 'found
803 | (while t
804 | (when (-contains-p emr-el:scope-boundary-forms (emr-lisp-peek-back-upwards))
805 | (throw 'found (point)))
806 | (condition-case nil
807 | (backward-up-list)
808 | (error
809 | ;; Outer sexp, can't go up any further. We didn't find any
810 | ;; body form.
811 | (throw 'found nil))))))
812 |
813 | (defun emr-el:let-start-pos ()
814 | "Search upward form point to find the start position of the innermost let."
815 | (let ((positions
816 | (list
817 | (emr-lisp-find-upwards 'let)
818 | (emr-lisp-find-upwards 'let*)
819 | (emr-lisp-find-upwards '-let)
820 | (emr-lisp-find-upwards '-let*))))
821 | (setq positions (-non-nil positions))
822 | (when positions
823 | (-max positions))))
824 |
825 | ;; TODO: the example is redundant here.
826 | (defun emr-el-toggle-let* ()
827 | "Toggle between let and let* in the enclosing let form.
828 |
829 | EXAMPLE:
830 |
831 | (emr-el-toggle-let*)
832 |
833 | BEFORE:
834 |
835 | (let* ((x 1))
836 | (+| x 1))
837 |
838 | AFTER:
839 |
840 | (let ((x 1))
841 | (+ x 1))"
842 | (interactive)
843 | (save-excursion
844 | (goto-char (emr-el:let-start-pos))
845 | (forward-char 1)
846 | (forward-sexp)
847 | ;; TODO: reindent afterwards
848 | (if (eq (char-before (point)) ?*)
849 | (delete-char -1)
850 | (insert "*"))))
851 |
852 | (defun emr-el:wrap-body-form-at-point-with-let ()
853 | "Wrap the form with a let statement at a sensible place."
854 | (emr-el:goto-containing-body-form)
855 | (let ((start-pos (point)))
856 | (forward-sexp)
857 | (insert ")")
858 | (goto-char start-pos))
859 | (insert "(let ()\n ")
860 | (emr-lisp-reindent-defun))
861 |
862 | ;; https://github.com/Wilfred/emacs-refactor/issues/35
863 | (defun emr-el:add-let-binding (var val)
864 | "Add a binding for symbol VAR assigned to VAL to the innermost let form.
865 |
866 | Ensures that VAR is inserted before point.
867 |
868 | VAL should be a string of elisp source code."
869 | (-let* ((let-form-start (emr-el:let-start-pos))
870 | ;; Read the whole let form.
871 | ((let-keyword let-vars . _)
872 | (save-excursion
873 | (goto-char let-form-start)
874 | (read (current-buffer))))
875 | (let-paren-depth
876 | (save-excursion
877 | (nth 0 (syntax-ppss let-form-start))))
878 | (vars-end
879 | (save-excursion
880 | (goto-char let-form-start)
881 | ;; Step over opening paren.
882 | (forward-char)
883 | ;; Step over let keyword.
884 | (forward-sexp)
885 | ;; Step over the vars.
886 | (forward-sexp)
887 | (point))))
888 | (save-excursion
889 | (let ((var-start-depth (+ let-paren-depth 2))
890 | vars-after-p)
891 | (if (> (point) vars-end)
892 | ;; We're extracting a let binding from the body, so we'll insert
893 | ;; this new var after all the existing vars.
894 | (progn
895 | (goto-char vars-end)
896 | (backward-char)
897 | (when let-vars
898 | (newline-and-indent)))
899 | ;; Move up s-expressions until we're at the beginning of the
900 | ;; variable declaration.
901 | ;; (let (|(x foo)) ...)
902 | (setq vars-after-p t)
903 | (while (not (eq (nth 0 (syntax-ppss)) var-start-depth))
904 | (goto-char (nth 1 (syntax-ppss))))
905 | ;; Move to the end of the previous sexp, if present.
906 | (if (> (length let-vars) 1)
907 | (progn
908 | (backward-sexp)
909 | (forward-sexp)))
910 | ;; Convert let to let* if we only had a single binding.
911 | (when (and (= (length let-vars) 1)
912 | (eq let-keyword 'let))
913 | (emr-el-toggle-let*)))
914 |
915 | (insert (format "(%s %s)" var val))
916 | (when vars-after-p
917 | (newline-and-indent))))))
918 |
919 | ;;;###autoload
920 | (defun emr-el-extract-to-let (symbol)
921 | "Extract the region or expression at point to a let-binding named SYMBOL.
922 |
923 | * extracts the list at or around point
924 |
925 | * if there is no enclosing let-form, inserts one at the top of
926 | the current context (e.g. the enclosing `defun' or `lambda' form)."
927 | (interactive "*SVariable name: ")
928 | (atomic-change-group
929 | (let (val-start-pos
930 | val-end-pos
931 | val)
932 |
933 | ;; Get the position of the expression that will be the value
934 | ;; for our extrcted var.
935 | (if (use-region-p)
936 | (progn
937 | (setq val-start-pos (region-beginning))
938 | (setq val-end-pos (region-end))
939 | (deactivate-mark))
940 | (save-excursion
941 | (emr-lisp-back-to-open-round)
942 | (setq val-start-pos (point))
943 | (setq val-end-pos (progn (forward-sexp) (point)))))
944 |
945 | (setq val
946 | (buffer-substring-no-properties val-start-pos val-end-pos))
947 |
948 | ;; Replace the expression with our new variable.
949 | (goto-char val-start-pos)
950 | (delete-region val-start-pos val-end-pos)
951 | (insert (symbol-name symbol))
952 |
953 | ;; If we're not inside a let, add one.
954 | (save-excursion
955 | (unless (emr-el:let-start-pos)
956 | (emr-el:wrap-body-form-at-point-with-let)))
957 |
958 | ;; Insert the new var in the let form.
959 | (emr-el:add-let-binding symbol val))))
960 |
961 | ; ------------------
962 |
963 | (defun emr-el:goto-start-of-let-binding ()
964 | "Move to the opening paren of the let-expression at point.
965 | Otherwise move to the previous one in the current top level form."
966 | (-when-let (pos (emr-el:let-start-pos))
967 | (when (< 0 pos)
968 | (goto-char pos)
969 | (point))))
970 |
971 | (defun emr-el:find-in-tree (elt tree)
972 | "Return non-nil if ELT is in TREE."
973 | (cond ((equal elt tree) elt)
974 | ((listp tree)
975 | (--reduce-from (or acc (emr-el:find-in-tree elt it))
976 | nil tree))))
977 |
978 | (defun emr-el:point-sexp-index ()
979 | "Return the position of point in the current sexp.
980 |
981 | For example:
982 | (foo| foo) => 0
983 | (foo fo|o) => 1"
984 | (let ((init-pos (point))
985 | (result 0)
986 | sexp-start sexp-end)
987 | (save-excursion
988 | ;; Find the boundaries of the containing sexp.
989 | (backward-up-list)
990 | (setq sexp-start (point))
991 |
992 | (forward-sexp)
993 | (setq sexp-end (point))
994 |
995 | ;; Move over the opening paren of the containing sexp.
996 | (goto-char (1+ sexp-start))
997 |
998 | ;; Move forward, counting subitems until we pass the original
999 | ;; point position.
1000 | (while (and
1001 | (< (point) init-pos)
1002 | (< (point) sexp-end))
1003 | (forward-sexp)
1004 | (setq result (1+ result))))
1005 |
1006 | ;; When we stop, we've gone past the original position, so we've
1007 | ;; overcounted by 1.
1008 | (max (1- result)
1009 | 0)))
1010 |
1011 | (defun emr-el:looking-at-let-binding-symbol? ()
1012 | "Non-nil if point is on a binding symbol in a let-binding form."
1013 | (when (symbol-at-point)
1014 | (let* ((let-form
1015 | (save-excursion
1016 | (emr-el:goto-start-of-let-binding)
1017 | (read (current-buffer))))
1018 | (vars-sexp
1019 | (-second-item let-form))
1020 | (enclosing-form
1021 | (save-excursion
1022 | (ignore-errors
1023 | (backward-up-list 1)
1024 | (read (current-buffer)))))
1025 | (enclosing-form-2
1026 | (save-excursion
1027 | (ignore-errors
1028 | (backward-up-list 2)
1029 | (read (current-buffer))))))
1030 | (and let-form
1031 | (or
1032 | ;; If the immediately enclosing form is the vars form,
1033 | ;; then we were in a form (let (x y| z) ...).
1034 | (equal vars-sexp enclosing-form)
1035 | ;; If the next enclosing form is the vars form, we are in
1036 | ;; a form (let ((x| y)) ...). Ensure we're also at the
1037 | ;; var, not its value.
1038 | (and (equal vars-sexp enclosing-form-2)
1039 | (zerop (emr-el:point-sexp-index))))))))
1040 |
1041 | (defun emr-el:let-bindings-recursively-depend? (elt bindings)
1042 | "Non-nil if the given let BINDINGS list has recursive dependency on ELT."
1043 | (-when-let* ((b (--first (equal elt (emr-el:first-atom it)) bindings))
1044 | (pos (cl-position b bindings :test 'equal)))
1045 | (-> (-split-at (1+ pos) bindings)
1046 | (cl-second)
1047 | (-flatten)
1048 | (-contains? elt))))
1049 |
1050 | (cl-defun emr-el:let-binding-is-used? (symbol (_let &optional bindings &rest body))
1051 | "Non-nil if SYMBOL is used in the body or other bindings of the given let expression."
1052 | (or
1053 | ;; Subsequent references in bindings list?
1054 | (emr-el:let-bindings-recursively-depend? symbol bindings)
1055 | ;; Body contains usage?
1056 | (-contains? (-flatten body) symbol)))
1057 |
1058 | (defun emr-el:let-bound-var-at-point-has-usages? ()
1059 | "Non-nil if the let-bound symbol at point is referred to in the
1060 | bindings or body of the enclosing let expression."
1061 | (and (emr-el:looking-at-let-binding-symbol?)
1062 | (save-excursion
1063 | (let ((sym (or (car (list-at-point))
1064 | (symbol-at-point))))
1065 | (emr-el:goto-start-of-let-binding)
1066 | (forward-symbol 1)
1067 | (emr-el:let-binding-is-used? sym (list-at-point))))))
1068 |
1069 | (defun emr-el:split-binding-string (binding-form)
1070 | (let* ((binding-form (s-trim binding-form))
1071 | (str (->> binding-form (s-chop-prefix "(") (s-chop-suffix ")")))
1072 | (idx (string-match (rx (+ (or space "\n"))) binding-form)))
1073 | (list (s-trim (substring str 0 idx))
1074 | (s-trim (substring str idx)))))
1075 |
1076 | ;;;###autoload
1077 | (defun emr-el-inline-let-variable ()
1078 | "Inline the let-bound variable at point.
1079 |
1080 | EXAMPLE:
1081 |
1082 | (emr-el-inline-let-variable)
1083 |
1084 | BEFORE:
1085 |
1086 | (let ((x 1)
1087 | (y| 2))
1088 | (+ x y))
1089 |
1090 | AFTER:
1091 |
1092 | (let ((x 1))
1093 | (+ x 2))"
1094 | (interactive "*")
1095 | (cl-assert (emr-el:looking-at-let-binding-symbol?))
1096 | (save-excursion
1097 | ;; Extract binding list.
1098 | ;;
1099 | ;; This will remove it from the let bindings list. We then replace all
1100 | ;; occurences of SYM with VALUE in the scope of the current let form.
1101 | (emr-lisp-extraction-refactor (form) "Inlined let-bound symbol"
1102 | (cl-destructuring-bind (sym value) (emr-el:split-binding-string form)
1103 | (save-restriction
1104 | ;; Narrow region to the scope of the current let form.
1105 | ;; The start is the position of the extracted binding list. This
1106 | ;; prevents preceding bindings from being altered.
1107 | (narrow-to-region (point)
1108 | (save-excursion
1109 | (emr-el:goto-start-of-let-binding)
1110 | (forward-sexp)
1111 | (point)))
1112 | (goto-char (point-min))
1113 | ;; Replace occurences of SYM with VALUE.
1114 | (while (search-forward-regexp
1115 | (rx-to-string `(seq symbol-start (group-n 1 ,sym) symbol-end))
1116 | nil t)
1117 | (unless (or (emr-looking-at-string?)
1118 | (emr-looking-at-comment?))
1119 | (replace-match value 'fixcase t nil 1)))))))
1120 | ;; Clean up.
1121 | ;;
1122 | ;; Ensure the binding list is well-formatted after removing the
1123 | ;; binding. Perform general tidyups.
1124 | (save-excursion
1125 | (emr-el:join-line-after-let-binding-kill)
1126 | (emr-el:simplify-let-form-at-point)
1127 | (emr-lisp-reindent-defun)))
1128 |
1129 | ; ------------------
1130 |
1131 | (defun emr-el:extract-arguments-in-usage-form (usage)
1132 | "Given a function USAGE form, extract the arguments applied to the function."
1133 | (with-temp-buffer
1134 | (save-excursion (insert usage))
1135 | ;; Move to funcall parameters.
1136 | (forward-char)
1137 | (if (thing-at-point-looking-at (rx (or "funcall" "apply") symbol-end))
1138 | (forward-symbol 2)
1139 | (forward-symbol 1))
1140 | ;; Collect all arguments in usage.
1141 | (let ((acc)
1142 | (kr kill-ring))
1143 | (unwind-protect
1144 | (while (ignore-errors (kill-sexp) t)
1145 | (push (s-trim (car kill-ring)) acc))
1146 | ;; Restore kill-ring to previous state.
1147 | (setq kill-ring kr))
1148 | (nreverse acc))))
1149 |
1150 | (cl-defun emr-el:defun-arglist-symbols ((_def _sym arglist &rest body))
1151 | arglist)
1152 |
1153 | (cl-defun emr-el:peek-forward-sexp (&optional (n 1))
1154 | "Return the sexp N positions forward of point."
1155 | (ignore-errors
1156 | (let ((start (save-excursion (forward-sexp (1- n)) (point)))
1157 | (end (save-excursion (forward-sexp n) (point))))
1158 | (emr-el:safe-read (buffer-substring start end)))))
1159 |
1160 | (defun emr-el:defun-body-str (defun-str)
1161 | "Extract the body forms from DEFUN-STR."
1162 | (with-temp-buffer
1163 | (lisp-mode)
1164 | (save-excursion (insert defun-str))
1165 | ;; Move past arglist.
1166 | (forward-char)
1167 | (forward-sexp 3)
1168 |
1169 | ;; Move past docstring.
1170 | (when (and (stringp (emr-el:peek-forward-sexp))
1171 | (emr-el:peek-forward-sexp 2))
1172 | (forward-sexp))
1173 |
1174 | ;; Move past `declare' and `interactive' forms.
1175 | (while (-contains? '(declare interactive) (car-safe (emr-el:peek-forward-sexp)))
1176 | (forward-sexp))
1177 |
1178 | ;; Everything from here is the body. Delete everything prior to this point.
1179 | (paredit-splice-sexp-killing-backward)
1180 | (buffer-string)))
1181 |
1182 | (defun emr-el:transform-function-usage (def usage)
1183 | "Replace the USAGE of a function with the body from its DEF.
1184 | Its variables will be let-bound."
1185 | (let* ((params (->> (emr-el:safe-read def)
1186 | (emr-el:defun-arglist-symbols)
1187 | (--remove (-contains? emr-el:special-symbols it))))
1188 | (args (emr-el:extract-arguments-in-usage-form usage))
1189 | ;; Join the function arglist and funcall arguments to create a
1190 | ;; let-bindings list.
1191 | (bindings (or (->> (-zip params args) (-map 'list-utils-make-proper))
1192 | "()"))
1193 | (body (emr-el:defun-body-str def)))
1194 | (with-temp-buffer
1195 | (lisp-mode)
1196 | (save-excursion
1197 | (insert (format "(let %s\n %s)" bindings body)))
1198 | (emr-el:simplify-let-form-at-point)
1199 | (emr-lisp-reindent-defun)
1200 | (buffer-string))))
1201 |
1202 | (defun emr-el:defun-at-point-has-body ()
1203 | (not (s-blank-str? (emr-el:defun-body-str (thing-at-point 'defun)))))
1204 |
1205 | ;;;###autoload
1206 | (defun emr-el-inline-function ()
1207 | "Replace usages of a function with its body forms.
1208 | Replaces all usages in the current buffer."
1209 | (interactive "*")
1210 | ;; Warn the user if the defun at point has an empty body. Prompt before
1211 | ;; continuing.
1212 | (when (or (emr-el:defun-at-point-has-body)
1213 | (y-or-n-p "Warning: This function has no body. Continue? "))
1214 | (atomic-change-group
1215 | (save-excursion
1216 |
1217 | ;; Extract the whole defun at point.
1218 | (beginning-of-defun)
1219 | (emr-lisp-extraction-refactor (def) "Inlined function"
1220 |
1221 | ;; There will now be a blank line where the defun used to be. Join
1222 | ;; lines to fix this.
1223 | (emr-collapse-vertical-whitespace)
1224 |
1225 | (let ((fname (nth 1 (s-split (rx space) def)))
1226 | ;; Tracks the line numbers where inlinings are performed.
1227 | (modified-lines))
1228 |
1229 | (goto-char (point-min))
1230 |
1231 | ;; Search the buffer for function usages.
1232 | ;;
1233 | ;; Functions can be called directly with the function name in
1234 | ;; the car of a list, or indirectly using funcall/apply. Handle
1235 | ;; all these cases.
1236 | (while (search-forward-regexp
1237 | (rx-to-string `(seq "("
1238 | ;; Optional use of apply/funcall.
1239 | (? (or "apply" "funcall")
1240 | (+ (any space "\n" "\t"))
1241 | "'")
1242 | ;; Usage of name.
1243 | ,fname symbol-end))
1244 | nil t)
1245 | ;; Move to start of the usage form.
1246 | (search-backward "(")
1247 |
1248 | ;; Inline the function at and update the `modified-lines' list.
1249 | (emr-lisp-extraction-refactor (usage) "Replace usage"
1250 | (push (line-number-at-pos) modified-lines)
1251 | (insert (emr-el:transform-function-usage def usage))
1252 | (emr-lisp-reindent-defun)))
1253 |
1254 | ;; Report inlining count to the user.
1255 | (if modified-lines
1256 | (let* ((n (length modified-lines))
1257 | (s (if (equal 1 n) "" "s")))
1258 | (message "%s replacement%s performed at line%s: %s" n s s
1259 | (->> modified-lines
1260 | (reverse)
1261 | (-map 'number-to-string)
1262 | (s-join ", "))))
1263 | ;; Abort if no changes were made to the buffer. This will revert
1264 | ;; the buffer text to its state before the extraction.
1265 | (error "No usages found"))))))))
1266 |
1267 | ; ------------------
1268 |
1269 | (defun emr-el:def-name (definition-form)
1270 | "Given a DEFINITION-FORM such as defvar/defun/..., return its name."
1271 | (let* ((form-name (nth 1 definition-form)))
1272 | (when (symbolp form-name)
1273 | form-name)))
1274 |
1275 | (defun emr-el:def-find-usages (definition-form)
1276 | "Find the usages for a given DEFINITION-FORM symbol.
1277 |
1278 | Returns a list of conses, where the car is the line number and
1279 | the cdr is the usage form."
1280 | (-when-let (sym (emr-el:def-name definition-form))
1281 | ;; Search the buffer for usages of `sym'. Remove the definition form
1282 | ;; from the results.
1283 | (let (acc)
1284 | (save-excursion
1285 | (goto-char (point-min))
1286 | (while (search-forward-regexp
1287 | (rx-to-string `(seq symbol-start ,(symbol-name sym) symbol-end))
1288 | nil t)
1289 | (-when-let (form (list-at-point))
1290 | (unless (equal definition-form form)
1291 | ;; Add this usage to `acc', unless it is the original definition.
1292 | (push (cons (line-number-at-pos) form) acc)))))
1293 | (nreverse acc))))
1294 |
1295 | ;;;###autoload
1296 | (defun emr-el-delete-unused-definition ()
1297 | "Delete the definition form at point if it does not have usages."
1298 | (interactive "*")
1299 | (unless (emr-el:def-find-usages (list-at-point))
1300 | (save-excursion
1301 | (beginning-of-thing 'defun)
1302 | (kill-sexp)
1303 |
1304 | (emr-collapse-vertical-whitespace))))
1305 |
1306 | ;;; `emr-el-ref': A reference to a function or variable within a file.
1307 | (cl-defstruct emr-el-ref file line col identifier type form)
1308 |
1309 | (defun emr-el:interactive-form-p (form)
1310 | "Does FORM contain an (interactive) expression?"
1311 | ;; (defun foo () x y ...) -> (x y ...)
1312 | (let ((body (-drop 3 form)))
1313 | ;; Ignore docstring, if present.
1314 | (when (stringp (car body))
1315 | (setq body (-drop 1 body)))
1316 |
1317 | (eq (car-safe (car body))
1318 | 'interactive)))
1319 |
1320 | (defun emr-el:find-unused-defs ()
1321 | "Return a list of all unused definitions in the buffer.
1322 | The result is a list of `emr-el-ref'."
1323 | (save-excursion
1324 | (let (acc)
1325 | (goto-char (point-min))
1326 |
1327 | ;; Find definitions in this buffer.
1328 | ;;
1329 | ;; This will search the buffer for known defun forms. As a special
1330 | ;; cases, forms with a preceding autoload directive are ignored.
1331 | (while (search-forward-regexp
1332 | (rx-to-string `(seq "(" (or ,@(-map 'symbol-name emr-el-definition-macro-names))
1333 | symbol-end))
1334 | nil t)
1335 | (unless (or (emr-looking-at-string?)
1336 | (emr-looking-at-comment?)
1337 | (emr-el:autoload-directive-exsts-above-defun?))
1338 | ;; Collect definitions that do not have usages.
1339 | (-when-let* ((form (list-at-point))
1340 | (col (save-excursion
1341 | (emr-el:beginning-of-defun)
1342 | (current-column))))
1343 | (unless (or
1344 | ;; Consider interactive forms to be used.
1345 | (emr-el:interactive-form-p form)
1346 | (emr-el:def-find-usages form))
1347 | (push
1348 | (make-emr-el-ref :file (buffer-file-name)
1349 | :line (line-number-at-pos)
1350 | :col col
1351 | :type (car form)
1352 | :identifier (nth 1 form)
1353 | :form form)
1354 | acc)))))
1355 | (nreverse acc))))
1356 |
1357 | (define-compilation-mode emr-buffer-report-mode "EMR Report"
1358 | "EMR buffer report compilation mode."
1359 | (set (make-local-variable 'truncate-lines) t)
1360 | (set (make-local-variable 'compilation-disable-input) t)
1361 | (set (make-local-variable 'compilation-error-face) compilation-info-face))
1362 |
1363 | ;; TODO: This is fooled by recursive functions.
1364 | ;;;###autoload
1365 | (defun emr-el-find-unused-definitions ()
1366 | "Search the buffer for functions and variables that have no usages.
1367 | Definitions with export directives are ignored. If any unused
1368 | definitions are found, they will be collated and displayed in a
1369 | popup window."
1370 | (interactive)
1371 |
1372 | (let ((defs-buf (get-buffer-create "*Unused Definitions*")))
1373 | ;; Find unused refs. If there are none, delete any windows showing `defs-buf'.
1374 | (-if-let (defs (emr-el:find-unused-defs))
1375 |
1376 | ;; Show results window.
1377 | ;;
1378 | ;; The results buffer uses a custom compilation mode so the user can
1379 | ;; navigate to unused declarations.
1380 | (with-help-window defs-buf
1381 | (let ((header (format "Unused definitions in %s:\n\n" (buffer-file-name))))
1382 | (with-current-buffer defs-buf
1383 | (atomic-change-group
1384 | (emr-buffer-report-mode)
1385 | ;; Prepare buffer.
1386 | (read-only-mode -1)
1387 | (delete-region (point-min) (point-max))
1388 | (insert header)
1389 | ;; Insert usages.
1390 | (->> defs
1391 | (--map (format
1392 | "%s:%s:%s:%s: %s"
1393 | (file-name-nondirectory (emr-el-ref-file it))
1394 | (emr-el-ref-line it)
1395 | (emr-el-ref-col it)
1396 | (emr-el-ref-type it)
1397 | (format "%s" (emr-el-ref-identifier it))))
1398 | (s-join "\n\n")
1399 | (insert))
1400 |
1401 | ;; Insert summary.
1402 | (newline 2)
1403 | (insert (format
1404 | "Finished. %s item%s found."
1405 | (length defs)
1406 | (if (equal 1 (length defs)) "" "s")))
1407 | (read-only-mode +1)))))
1408 |
1409 | ;; No results to show. Clean results window.
1410 | (progn
1411 | (message "No unused definitions found")
1412 | (-when-let (win (get-window-with-predicate
1413 | (lambda (w) (equal defs-buf (window-buffer w)))))
1414 | (delete-window win)
1415 | (kill-buffer defs-buf))))))
1416 |
1417 | ; ------------------
1418 |
1419 | ;;;; EMR declarations
1420 |
1421 | (emr-declare-command 'emr-el-toggle-let*
1422 | :title "toggle let/let*"
1423 | :modes 'emacs-lisp-mode
1424 | :predicate #'emr-el:let-start-pos)
1425 |
1426 | (emr-declare-command 'emr-el-implement-function
1427 | :title "implement function"
1428 | :modes 'emacs-lisp-mode
1429 | :predicate (lambda ()
1430 | (and (symbol-at-point)
1431 | (not (emr-looking-at-string?))
1432 | (not (emr-looking-at-comment?))
1433 | (not (thing-at-point 'number))
1434 | (not (emr-el:looking-at-definition?))
1435 | (not (emr-el:looking-at-let-binding-symbol?))
1436 | (not (boundp (symbol-at-point)))
1437 | (not (fboundp (symbol-at-point))))))
1438 |
1439 | (emr-declare-command 'emr-el-inline-variable
1440 | :title "inline"
1441 | :modes 'emacs-lisp-mode
1442 | :predicate (lambda ()
1443 | (and (emr-el:variable-definition? (list-at-point))
1444 | (emr-el:def-find-usages (list-at-point)))))
1445 |
1446 | (emr-declare-command 'emr-el-inline-function
1447 | :title "inline"
1448 | :modes 'emacs-lisp-mode
1449 | :predicate (lambda ()
1450 | (and (emr-el:function-definition? (list-at-point))
1451 | (emr-el:def-find-usages (list-at-point)))))
1452 |
1453 | (emr-declare-command 'emr-el-extract-function
1454 | :title "extract function"
1455 | :description "defun"
1456 | :modes 'emacs-lisp-mode
1457 | :predicate (lambda ()
1458 | (not (or (emr-el:looking-at-definition?)
1459 | (emr-el:looking-at-let-binding-symbol?)))))
1460 |
1461 | (emr-declare-command 'emr-el-extract-variable
1462 | :title "extract variable"
1463 | :description "defvar"
1464 | :modes 'emacs-lisp-mode
1465 | :predicate (lambda ()
1466 | (and (not (emr-el:looking-at-definition?))
1467 | (not (emr-el:looking-at-let-binding-symbol?))
1468 | (thing-at-point 'defun))))
1469 |
1470 | (emr-declare-command 'emr-el-extract-constant
1471 | :title "extract constant"
1472 | :description "defconst"
1473 | :modes 'emacs-lisp-mode
1474 | :predicate (lambda ()
1475 | (not (or (emr-el:looking-at-definition?)
1476 | (emr-el:looking-at-let-binding-symbol?)))))
1477 |
1478 | (emr-declare-command 'emr-el-extract-to-let
1479 | :title "extract to let"
1480 | :description "let"
1481 | :modes 'emacs-lisp-mode
1482 | :predicate (lambda ()
1483 | (not (or (emr-el:looking-at-definition?)
1484 | (emr-el:looking-at-decl?)
1485 | (emr-el:looking-at-let-binding-symbol?)))))
1486 |
1487 | (emr-declare-command 'emr-el-delete-let-binding-form
1488 | :title "delete binding"
1489 | :description "unused"
1490 | :modes 'emacs-lisp-mode
1491 | :predicate (lambda ()
1492 | (and (emr-el:looking-at-let-binding-symbol?)
1493 | (not (emr-el:let-bound-var-at-point-has-usages?)))))
1494 |
1495 | (emr-declare-command 'emr-el-inline-let-variable
1496 | :title "inline binding"
1497 | :modes 'emacs-lisp-mode
1498 | :predicate (lambda ()
1499 | (and (emr-el:looking-at-let-binding-symbol?)
1500 | (emr-el:let-bound-var-at-point-has-usages?))))
1501 |
1502 | (emr-declare-command 'emr-el-extract-autoload
1503 | :title "add autoload"
1504 | :description "autoload"
1505 | :modes 'emacs-lisp-mode
1506 | :predicate (lambda ()
1507 | (let ((sym (symbol-at-point)))
1508 | (and (not (emr-el:autoload-exists? sym (buffer-string)))
1509 | (not (emr-el:looking-at-definition?))
1510 | (not (emr-el:variable-definition? (list-at-point)))
1511 | (or (functionp sym)
1512 | (macrop sym))
1513 | ;; Don't offer autoload if this function is
1514 | ;; defined in the current file.
1515 | (not (equal
1516 | (cdr-safe (find-function-library sym))
1517 | (buffer-file-name)))))))
1518 |
1519 | (emr-declare-command 'emr-el-insert-autoload-directive
1520 | :title "autoload cookie"
1521 | :description "directive"
1522 | :modes 'emacs-lisp-mode
1523 | :predicate (lambda ()
1524 | (and (emr-el:looking-at-definition?)
1525 | (not (emr-el:autoload-directive-exsts-above-defun?)))))
1526 |
1527 | (emr-declare-command 'emr-el-eval-and-replace
1528 | :title "eval and replace"
1529 | :description "value"
1530 | :modes 'emacs-lisp-mode
1531 | :predicate (lambda ()
1532 | (not (or (emr-el:looking-at-definition?)
1533 | (emr-el:looking-at-let-binding-symbol?)))))
1534 |
1535 | (emr-declare-command 'emr-el-tidy-autoloads
1536 | :title "tidy"
1537 | :description "autoloads"
1538 | :modes 'emacs-lisp-mode
1539 | :predicate (lambda ()
1540 | (thing-at-point-looking-at
1541 | (rx bol (* space) "(autoload " (* nonl)))))
1542 |
1543 | (emr-declare-command 'emr-el-delete-unused-definition
1544 | :title "delete"
1545 | :description "unused"
1546 | :modes 'emacs-lisp-mode
1547 | :predicate (lambda ()
1548 | (and (emr-el:looking-at-definition?)
1549 | (not (emr-el:autoload-directive-exsts-above-defun?))
1550 | (not (emr-el:def-find-usages (list-at-point))))))
1551 |
1552 |
1553 | (defun emr-el:looking-at-symbol-p ()
1554 | "Is point looking at an unquoted symbol?"
1555 | (save-excursion
1556 | (-let (((_ _ _ in-string in-comment . _) (syntax-ppss))
1557 | (sym-bounds (bounds-of-thing-at-point 'symbol))
1558 | (sexp nil))
1559 | (when (and
1560 | (not in-string)
1561 | (not in-comment)
1562 | sym-bounds)
1563 | (goto-char (car sym-bounds))
1564 | (setq sexp (read (current-buffer)))
1565 |
1566 | (and
1567 | (symbolp sexp)
1568 | (not (null sexp))
1569 | (not (keywordp sexp)))))))
1570 |
1571 | (defun emr-el:looking-at-local-var-p ()
1572 | "Is point looking at a symbol for a locally bound variable?"
1573 | (when (emr-el:looking-at-symbol-p)
1574 | (let ((sym (symbol-at-point))
1575 | form)
1576 | (save-excursion
1577 | (emr-el:beginning-of-defun)
1578 | (setq form (read (current-buffer)))
1579 | (memq sym (emr-el:bound-variables form))))))
1580 |
1581 | ;;;; Setup
1582 |
1583 | (defun emr-el:show-menu ()
1584 | (easy-menu-add-item
1585 | nil
1586 | '("EMR")
1587 | ["Find unused definitions" emr-el-find-unused-definitions]))
1588 |
1589 | ;;;###autoload
1590 | (defun emr-el-initialize ()
1591 | "Enable the EMR menu for Elisp buffers."
1592 | (add-hook 'emacs-lisp-mode-hook 'emr-el:show-menu)
1593 | (--each (buffer-list)
1594 | (with-current-buffer it
1595 | (when (derived-mode-p 'emacs-lisp-mode)
1596 | (emr-el:show-menu)))))
1597 |
1598 | (provide 'emr-elisp)
1599 |
1600 | ;;; emr-elisp.el ends here
1601 |
--------------------------------------------------------------------------------
/emr-iedit.el:
--------------------------------------------------------------------------------
1 | ;;; emr-iedit.el -- Use iedit for rename refactorings -*- lexical-binding: t; -*-
2 |
3 | ;; Copyright (C) 2015 EMR Project
4 | ;; Copyright (C) 2018 Wilfred Hughes
5 |
6 | ;; Author: YangYingchao
7 |
8 | ;; This file is not part of GNU Emacs.
9 |
10 | ;; This program is free software: you can redistribute it and/or modify
11 | ;; it under the terms of the GNU General Public License as published by
12 | ;; the Free Software Foundation, either version 3 of the License, or
13 | ;; (at your option) any later version.
14 |
15 | ;; This program is distributed in the hope that it will be useful,
16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | ;; GNU General Public License for more details.
19 |
20 | ;; You should have received a copy of the GNU General Public License
21 | ;; along with this program. If not, see .
22 |
23 | ;;; Commentary:
24 | ;; Rename variables/functions/macros with iedit.
25 |
26 | ;;; Code:
27 |
28 | (require 'emr)
29 | (require 's)
30 | (require 'dash)
31 | (require 'thingatpt)
32 | (require 'which-func)
33 | (require 'emr-elisp)
34 |
35 | (require 'iedit)
36 |
37 | (defconst emr-iedit:rx-iterator
38 | (rx (+ (or alnum "-" "_"))))
39 |
40 | (defun emr-iedit:looking-at-iterator? ()
41 | (thing-at-point-looking-at emr-iedit:rx-iterator))
42 |
43 | (defun emr-iedit-global ()
44 | "Rename a variable appears in current buffer.."
45 | (interactive)
46 | (iedit-mode))
47 |
48 | (defun emr-iedit-in-function ()
49 | "Rename variable appears in current function."
50 | (interactive)
51 | (iedit-mode-toggle-on-function))
52 |
53 |
54 | (emr-declare-command 'emr-iedit-in-function
55 | :title "rename"
56 | :description "in function"
57 | :modes '(prog-mode)
58 | :predicate (lambda ()
59 | (and (not (iedit-region-active))
60 | (if (eq major-mode 'emacs-lisp-mode)
61 | (emr-el:looking-at-local-var-p)
62 | (and
63 | (emr-iedit:looking-at-iterator?)
64 | (which-function))))))
65 |
66 | (emr-declare-command 'emr-iedit-global
67 | :title "rename"
68 | :description "in file"
69 | :modes '(prog-mode)
70 | :predicate (lambda ()
71 | (and (not (iedit-region-active))
72 | (if (eq major-mode 'emacs-lisp-mode)
73 | (and
74 | (emr-el:looking-at-symbol-p)
75 | (not (emr-el:looking-at-local-var-p)))
76 | (emr-iedit:looking-at-iterator?)))))
77 |
78 | (provide 'emr-iedit)
79 |
80 | ;; Local Variables:
81 | ;; coding: utf-8
82 | ;; indent-tabs-mode: nil
83 | ;; End:
84 |
85 | ;;; emr-iedit.el ends here
86 |
--------------------------------------------------------------------------------
/emr-js.el:
--------------------------------------------------------------------------------
1 | ;;; emr-js.el --- Refactoring commands for JavaScript. -*- lexical-binding: t; -*-
2 |
3 | ;; Copyright (C) 2014 Chris Barrett
4 |
5 | ;; Author: Chris Barrett
6 |
7 | ;; This file is not part of GNU Emacs.
8 |
9 | ;; This program is free software: you can redistribute it and/or modify
10 | ;; it under the terms of the GNU General Public License as published by
11 | ;; the Free Software Foundation, either version 3 of the License, or
12 | ;; (at your option) any later version.
13 |
14 | ;; This program is distributed in the hope that it will be useful,
15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | ;; GNU General Public License for more details.
18 |
19 | ;; You should have received a copy of the GNU General Public License
20 | ;; along with this program. If not, see .
21 |
22 | ;;; Commentary:
23 |
24 | ;; Refactoring commands for JavaScript. Requires `js2-refactor`.
25 | ;;
26 | ;; https://github.com/magnars/js2-refactor.el
27 | ;;
28 |
29 | ;;; Code:
30 |
31 | (require 'emr)
32 |
33 | (emr-declare-command 'js2r-extract-function
34 | :title "extract to function"
35 | :description nil
36 | :modes 'js2-mode
37 | :predicate (lambda ()
38 | (and
39 | (require 'js2-refactor nil t)
40 | (use-region-p))))
41 |
42 | (emr-declare-command 'js2r-extract-var
43 | :title "extract local variable"
44 | :description nil
45 | :modes 'js2-mode
46 | :predicate (lambda ()
47 | (and
48 | (require 'js2-refactor nil t)
49 | (use-region-p))))
50 |
51 | (emr-declare-command 'js2r-var-to-this
52 | :title "local variable to instance variable"
53 | :description nil
54 | :modes 'js2-mode
55 | :predicate (lambda ()
56 | (and
57 | (require 'js2-refactor nil t)
58 | (use-region-p))))
59 |
60 | (emr-declare-command 'js2r-log-this
61 | :title "log this"
62 | :description nil
63 | :modes 'js2-mode
64 | :predicate (lambda ()
65 | (and
66 | (require 'js2-refactor nil t)
67 | (or (use-region-p)
68 | (ignore-errors (js2r--name-node-at-point))))))
69 |
70 | (emr-declare-command 'js2r-introduce-parameter
71 | :title "add parameter"
72 | :description nil
73 | :modes 'js2-mode
74 | :predicate (lambda ()
75 | (and
76 | (require 'js2-refactor nil t)
77 | (use-region-p))))
78 |
79 | (emr-declare-command 'js2r-extract-method
80 | :title "extract to method"
81 | :description nil
82 | :modes 'js2-mode
83 | :predicate (lambda ()
84 | (and
85 | (require 'js2-refactor nil t)
86 | (use-region-p))))
87 |
88 | (provide 'emr-js)
89 |
90 | ;;; emr-js.el ends here
91 |
--------------------------------------------------------------------------------
/emr-lisp.el:
--------------------------------------------------------------------------------
1 | ;;; emr-lisp.el --- Refactoring commands common to all Lisps. -*- lexical-binding: t; -*-
2 |
3 | ;; Copyright (C) 2013 Chris Barrett
4 |
5 | ;; Author: Chris Barrett
6 |
7 | ;; This file is not part of GNU Emacs.
8 |
9 | ;; This program is free software: you can redistribute it and/or modify
10 | ;; it under the terms of the GNU General Public License as published by
11 | ;; the Free Software Foundation, either version 3 of the License, or
12 | ;; (at your option) any later version.
13 |
14 | ;; This program is distributed in the hope that it will be useful,
15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | ;; GNU General Public License for more details.
18 |
19 | ;; You should have received a copy of the GNU General Public License
20 | ;; along with this program. If not, see .
21 |
22 | ;;; Commentary:
23 |
24 | ;; Refactoring commands common to all Lisps.
25 |
26 | ;;; Code:
27 |
28 | (require 'emr)
29 | (autoload 'thing-at-point-looking-at "thingatpt")
30 | (autoload 'beginning-of-sexp "thingatpt")
31 |
32 | (defun emr-lisp-back-to-open-round ()
33 | "Move to the opening paren for the Lisp list at point."
34 | (interactive)
35 | (when (or (not (equal "(" (thing-at-point 'char)))
36 | (emr-looking-at-string?))
37 | (beginning-of-sexp)
38 | (unless (equal "(" (thing-at-point 'char))
39 | (search-backward "("))))
40 |
41 | (defun emr-lisp-back-to-open-round-or-quote ()
42 | "Move to the opening paren or quote for the Lisp list at point."
43 | (interactive)
44 | (emr-lisp-back-to-open-round)
45 | (when (or (thing-at-point-looking-at "'")
46 | (thing-at-point-looking-at "`")
47 | (emr-looking-at-string?))
48 | (search-backward-regexp (rx (or "'" "`")))))
49 |
50 | (defun emr-lisp-find-upwards (sym)
51 | "Search upwards from POINT for an enclosing form beginning with SYM."
52 | (save-excursion
53 | (cl-loop
54 | while (ignore-errors (backward-up-list) t)
55 | when (thing-at-point-looking-at
56 | (rx-to-string `(seq "(" ,(symbol-name sym) symbol-end)))
57 | do (cl-return (point)))))
58 |
59 | ; ------------------
60 |
61 | (defun emr-lisp-reindent-defun ()
62 | "Reindent the current top level form."
63 | (save-excursion (end-of-defun) (beginning-of-defun) (indent-sexp)))
64 |
65 | (defun emr-lisp-reindent-string (form-str)
66 | "Reformat FORM-STR, assuming it is a Lisp fragment."
67 | (with-temp-buffer
68 | (lisp-mode-variables)
69 | (insert form-str)
70 | (emr-lisp-reindent-defun)
71 | (buffer-string)))
72 |
73 | (defun emr-lisp-insert-above-defun (form-str)
74 | "Insert and indent FORM-STR above the current top level form.
75 | Return the position of the end of FORM-STR."
76 | (emr-insert-above-defun (emr-lisp-reindent-string form-str)))
77 |
78 | (cl-defmacro emr-lisp-extraction-refactor ((&optional binding) description &rest body)
79 | "Kill the sexp near point then execute forms.
80 | BINDING is the name to bind to the extracted form.
81 | DESCRIPTION is used to report the result of the refactoring.
82 | BODY is a list of forms to execute after extracting the sexp near point."
83 | (declare (indent 2))
84 | `(atomic-change-group
85 | (save-excursion
86 |
87 | ;; Either extract the active region or the sexp near point.
88 | (if (region-active-p)
89 | (kill-region (region-beginning) (region-end))
90 | (emr-lisp-back-to-open-round-or-quote)
91 | (kill-sexp))
92 |
93 | (emr-lisp-reindent-defun)
94 |
95 | (let
96 | ;; Define BINDING if supplied.
97 | ,(when binding `((,binding (s-trim (car kill-ring)))))
98 |
99 | ;; Revert kill-ring pointer.
100 | (setq kill-ring (cdr kill-ring))
101 | (save-excursion
102 | (emr-reporting-buffer-changes ,description
103 | ,@body))))))
104 |
105 | (provide 'emr-lisp)
106 |
107 | ;;; emr-lisp.el ends here
108 |
--------------------------------------------------------------------------------
/emr-prog.el:
--------------------------------------------------------------------------------
1 | ;;; emr-prog.el --- Common refactoring commands for all programming modes. -*- lexical-binding: t; -*-
2 |
3 | ;; Copyright (C) 2013 Chris Barrett
4 |
5 | ;; Author: Chris Barrett
6 |
7 | ;; This file is not part of GNU Emacs.
8 |
9 | ;; This program is free software: you can redistribute it and/or modify
10 | ;; it under the terms of the GNU General Public License as published by
11 | ;; the Free Software Foundation, either version 3 of the License, or
12 | ;; (at your option) any later version.
13 |
14 | ;; This program is distributed in the hope that it will be useful,
15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | ;; GNU General Public License for more details.
18 |
19 | ;; You should have received a copy of the GNU General Public License
20 | ;; along with this program. If not, see .
21 |
22 | ;;; Commentary:
23 |
24 | ;; Common refactoring commands for all programming modes.
25 |
26 | ;;; Code:
27 |
28 | (require 'emr)
29 |
30 | (emr-declare-command 'comment-region
31 | :title "comment"
32 | :description "region"
33 | :modes 'prog-mode
34 | :predicate (lambda ()
35 | (region-active-p)))
36 |
37 | (emr-declare-command 'uncomment-region
38 | :title "uncomment"
39 | :description "region"
40 | :modes 'prog-mode
41 | :predicate (lambda ()
42 | (and (region-active-p)
43 | (s-contains? comment-start
44 | (buffer-substring (region-beginning)
45 | (region-end))))))
46 |
47 | (provide 'emr-prog)
48 |
49 | ;;; emr-prog.el ends here
50 |
--------------------------------------------------------------------------------
/emr-ruby.el:
--------------------------------------------------------------------------------
1 | ;;; emr-ruby.el --- Refactoring support for Ruby. -*- lexical-binding: t; -*-
2 |
3 | ;; Copyright (C) 2014 Chris Barrett
4 |
5 | ;; Author: Chris Barrett
6 |
7 | ;; This file is not part of GNU Emacs.
8 |
9 | ;; This program is free software: you can redistribute it and/or modify
10 | ;; it under the terms of the GNU General Public License as published by
11 | ;; the Free Software Foundation, either version 3 of the License, or
12 | ;; (at your option) any later version.
13 |
14 | ;; This program is distributed in the hope that it will be useful,
15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | ;; GNU General Public License for more details.
18 |
19 | ;; You should have received a copy of the GNU General Public License
20 | ;; along with this program. If not, see .
21 |
22 | ;;; Commentary:
23 |
24 | ;; Refactoring support for Ruby. Requires `Ruby Refactor`.
25 | ;;
26 | ;; https://github.com/ajvargo/ruby-refactor
27 | ;;
28 |
29 | ;;; Code:
30 |
31 | (require 'emr)
32 |
33 | (emr-declare-command 'ruby-refactor-extract-to-method
34 | :title "extract method"
35 | :description nil
36 | :modes '(enh-ruby-mode ruby-mode)
37 | :predicate (lambda ()
38 | (and (require 'ruby-refactor nil t)
39 | (use-region-p))))
40 |
41 | (emr-declare-command 'ruby-refactor-extract-local-variable
42 | :title "extract local variable"
43 | :description nil
44 | :modes '(enh-ruby-mode ruby-mode)
45 | :predicate (lambda ()
46 | (and (require 'ruby-refactor nil t)
47 | (use-region-p))))
48 |
49 | (emr-declare-command 'ruby-refactor-extract-constant
50 | :title "extract constant"
51 | :description nil
52 | :modes '(enh-ruby-mode ruby-mode)
53 | :predicate (lambda ()
54 | (and (require 'ruby-refactor nil t)
55 | (use-region-p))))
56 |
57 | (emr-declare-command 'ruby-refactor-add-parameter
58 | :title "add parameter"
59 | :description nil
60 | :modes '(enh-ruby-mode ruby-mode)
61 | :predicate (lambda ()
62 | (require 'ruby-refactor nil t)))
63 |
64 | (emr-declare-command 'ruby-refactor-convert-post-conditional
65 | :title "convert post conditional"
66 | :description nil
67 | :modes '(enh-ruby-mode ruby-mode)
68 | :predicate (lambda ()
69 | (and (require 'ruby-refactor nil t)
70 | (use-region-p))))
71 |
72 | (emr-declare-command 'ruby-refactor-extract-to-let
73 | :title "extract to let"
74 | :description nil
75 | :modes '(enh-ruby-mode ruby-mode)
76 | :predicate (lambda ()
77 | (and (require 'ruby-refactor nil t)
78 | (use-region-p))))
79 |
80 | (provide 'emr-ruby)
81 |
82 | ;;; emr-ruby.el ends here
83 |
--------------------------------------------------------------------------------
/emr-scheme.el:
--------------------------------------------------------------------------------
1 | ;;; emr-scheme.el --- Refactoring commands for Scheme. -*- lexical-binding: t; -*-
2 |
3 | ;; Copyright (C) 2013 Chris Barrett
4 |
5 | ;; Author: Chris Barrett
6 |
7 | ;; This file is not part of GNU Emacs.
8 |
9 | ;; This program is free software: you can redistribute it and/or modify
10 | ;; it under the terms of the GNU General Public License as published by
11 | ;; the Free Software Foundation, either version 3 of the License, or
12 | ;; (at your option) any later version.
13 |
14 | ;; This program is distributed in the hope that it will be useful,
15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | ;; GNU General Public License for more details.
18 |
19 | ;; You should have received a copy of the GNU General Public License
20 | ;; along with this program. If not, see .
21 |
22 | ;;; Commentary:
23 |
24 | ;; Refactoring commands for Scheme.
25 |
26 | ;;; Code:
27 |
28 | (require 'emr)
29 | (require 'emr-lisp)
30 | (require 'emr-elisp)
31 | (require 'dash)
32 |
33 | (defun emr-scm:looking-at-def? ()
34 | "Non-nil if point is looking at a definition form."
35 | (emr-line-matches? (rx bol (* space) "(define" (+ space))))
36 |
37 | (defun emr-scm:inside-def? ()
38 | "Non-nil if point is inside a definition form."
39 | (and
40 | (emr-lisp-find-upwards 'define)
41 | (not (emr-scm:looking-at-def?))))
42 |
43 | ;;;###autoload
44 | (defun emr-scm-extract-function (name arglist)
45 | "Extract a function, using the current region or form at point as the body.
46 | NAME is the name of the new function.
47 | ARGLIST is its argument list."
48 | (interactive
49 | (list
50 | ;; Read a name for the function, ensuring it is not blank.
51 | (let ((x (read-string "Name: ")))
52 | (if (s-blank? x) (user-error "Name must not be blank") x))
53 |
54 | ;; Prompt user with default arglist.
55 | (read-string "Arglist: ")))
56 |
57 | (cl-assert (not (s-blank? name)) () "Name must not be blank")
58 |
59 | (emr-lisp-extraction-refactor (body) "Extracted to"
60 | (let ((hdr (cons (intern name)
61 | (emr-el:safe-read (format "(%s)" arglist)))))
62 |
63 | ;; Insert usage at point.
64 | (insert (emr-el:print hdr))
65 |
66 | ;; Insert definition.
67 | (->> (format "(define %s\n %s)" hdr body)
68 | (emr-el:format-defun)
69 | (emr-lisp-insert-above-defun)))))
70 |
71 | ;;;###autoload
72 | (defun emr-scm-extract-variable (name)
73 | "Extract the current region or form at point to a special variable.
74 | The variable will be called NAME."
75 | (interactive "*sName: ")
76 | (cl-assert (not (s-blank? name)) () "Name must not be blank")
77 | (emr-lisp-extraction-refactor (sexp) "Extracted to"
78 | ;; Insert usage.
79 | (insert (s-trim name))
80 | ;; Insert definition.
81 | (emr-lisp-insert-above-defun (format "(define %s %s)" name sexp))))
82 |
83 | ; ------------------
84 |
85 | (emr-declare-command 'emr-scm-extract-function
86 | :title "function"
87 | :description "define"
88 | :modes 'scheme-mode
89 | :predicate (lambda ()
90 | (not (or (emr-scm:looking-at-def?)
91 | (emr-el:looking-at-let-binding-symbol?)))))
92 |
93 | (emr-declare-command 'emr-scm-extract-variable
94 | :title "variable"
95 | :description "define"
96 | :modes 'scheme-mode
97 | :predicate (lambda ()
98 | (not (or (emr-scm:looking-at-def?)
99 | (emr-el:looking-at-let-binding-symbol?)))))
100 |
101 | (provide 'emr-scheme)
102 |
103 | ;;; emr-scheme.el ends here
104 |
--------------------------------------------------------------------------------
/emr.el:
--------------------------------------------------------------------------------
1 | ;;; emr.el --- Emacs refactoring system. -*- lexical-binding: t; -*-
2 |
3 | ;; Copyright (C) 2013 Chris Barrett
4 | ;; Copyright (C) 2016-2018 Wilfred Hughes
5 |
6 | ;; Author: Chris Barrett
7 | ;; Keywords: tools convenience refactoring
8 | ;; Version: 0.4.1
9 | ;; URL: https://github.com/Wilfred/emacs-refactor
10 | ;; Package-Requires: ((s "1.3.1") (dash "1.2.0") (cl-lib "0.2") (popup "0.5.0") (emacs "24.1") (list-utils "0.3.0") (paredit "24.0.0") (projectile "0.9.1") (clang-format "0.0.1") (iedit "0.97"))
11 |
12 | ;; This file is not part of GNU Emacs.
13 |
14 | ;; This program is free software: you can redistribute it and/or modify
15 | ;; it under the terms of the GNU General Public License as published by
16 | ;; the Free Software Foundation, either version 3 of the License, or
17 | ;; (at your option) any later version.
18 |
19 | ;; This program is distributed in the hope that it will be useful,
20 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
21 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 | ;; GNU General Public License for more details.
23 |
24 | ;; You should have received a copy of the GNU General Public License
25 | ;; along with this program. If not, see .
26 |
27 | ;;; Commentary:
28 |
29 | ;; Add this package to your load path and add an autoload for `emr-show-refactor-menu`.
30 | ;; Bind the `emr-show-refactor-menu` command to something convenient.
31 | ;;
32 | ;; (autoload 'emr-show-refactor-menu "emr")
33 | ;; (define-key prog-mode-map (kbd "M-RET") 'emr-show-refactor-menu)
34 | ;; (eval-after-load "emr" '(emr-initialize))
35 | ;;
36 | ;; See README.md for more information.
37 |
38 | ;;; Code:
39 |
40 | (require 'dash)
41 | (require 's)
42 | (require 'cl-lib)
43 | (require 'popup)
44 | (autoload 'beginning-of-thing "thingatpt")
45 | (autoload 'emr-c-initialize "emr-c")
46 | (autoload 'emr-el-initialize "emr-elisp")
47 |
48 | (defgroup emacs-refactor nil
49 | "Provides refactoring tools for Emacs."
50 | :group 'tools
51 | :prefix "emr-")
52 |
53 | (defcustom emr-report-actions t
54 | "Non-nil means display an indication when a refactoring results in an insertion."
55 | :type 'checkbox
56 | :group 'emacs-refactor)
57 |
58 | (defcustom emr-lines-between-toplevel-forms 1
59 | "The number of lines to try to preserve between toplevel forms."
60 | :type 'integer
61 | :group 'emr)
62 |
63 | (defcustom emr-popup-help-delay 1
64 | "The time to wait before showing documentation in the refactor menu."
65 | :type 'integer
66 | :group 'emr)
67 |
68 | ;;; Utility functions
69 |
70 | ;;;###autoload
71 | (defun emr-move-above-defun ()
72 | "Move to the start of the current defun.
73 | If the defun is preceded by comments, move above them."
74 | (interactive)
75 | (ignore-errors
76 | (beginning-of-thing 'defun))
77 | ;; If there is a comment attached to this defun, skip over it.
78 | (let* ((prev-line-end-pos
79 | (unless (bobp)
80 | (save-excursion
81 | (forward-line -1)
82 | (point-at-eol))))
83 | (comment-preceding
84 | (and
85 | prev-line-end-pos
86 | (emr-looking-at-comment? prev-line-end-pos))))
87 | (when comment-preceding
88 | (forward-line -1)
89 | (while (and (emr-looking-at-comment? (line-end-position))
90 | (not (bobp)))
91 | (forward-line -1)))))
92 |
93 | (defun emr-looking-at-string? ()
94 | "Return non-nil if point is inside a string."
95 | (save-excursion (nth 3 (syntax-ppss))))
96 |
97 | (defun emr-looking-at-comment? (&optional pos)
98 | "Non-nil if POS is on a comment."
99 | (save-excursion
100 | (nth 4 (syntax-ppss pos))))
101 |
102 | (defun emr-line-str ()
103 | "Return the contents of the current line."
104 | (buffer-substring (line-beginning-position)
105 | (line-end-position)))
106 |
107 | (cl-defun emr-blank-line? (&optional (point (point)))
108 | "Non-nil if POINT is on a blank line."
109 | (save-excursion
110 | (goto-char point)
111 | (s-blank-str? (emr-line-str))))
112 |
113 | (cl-defun emr-line-matches? (regex &optional (point (point)))
114 | "Non-nil if POINT is on a line that matches REGEX."
115 | (save-excursion
116 | (goto-char point)
117 | (s-matches? regex (emr-line-str))))
118 |
119 | (defun emr-insert-above-defun (str)
120 | "Insert and indent STR above the current top level form.
121 | Return the position of the end of STR."
122 | (save-excursion
123 | (let ((mark-ring nil))
124 | ;; Move to position above top-level form.
125 | (beginning-of-line)
126 | (emr-move-above-defun)
127 | (open-line 2)
128 | ;; Perform insertion.
129 | (insert str)
130 | ;; Ensure there is leading blank line.
131 | (save-excursion
132 | (emr-move-above-defun)
133 | (unless (save-excursion
134 | (forward-line -1)
135 | (emr-blank-line?))
136 | (open-line 1)))
137 | (point))))
138 |
139 | (defun emr-collapse-vertical-whitespace ()
140 | "Collapse blank lines around point.
141 | Ensure there are at most `emr-lines-between-toplevel-forms' blanks."
142 | (when (emr-blank-line?)
143 | (save-excursion
144 | ;; Delete blank lines.
145 | (search-backward-regexp (rx (not (any space "\n"))) nil t)
146 | (forward-line 1)
147 | (while (emr-blank-line?)
148 | (forward-line)
149 | (join-line))
150 | ;; Open a user-specified number of blanks.
151 | (open-line emr-lines-between-toplevel-forms))))
152 |
153 | ;;; Reporting
154 |
155 | ;;; These commands may be used to describe the changes made to buffers. See
156 | ;;; example in emr-elisp.
157 |
158 | (defun emr:indexed-lines (str)
159 | "Split string STR into a list of conses.
160 | The index is the car and the line is the cdr."
161 | (--map-indexed (cons it-index it) (s-lines str)))
162 |
163 | (defun emr:diff-lines (str1 str2)
164 | "Get the lines that differ between strings STR1 and STR2."
165 | (--remove (equal (car it) (cdr it))
166 | (-zip (emr:indexed-lines str1) (emr:indexed-lines str2))))
167 |
168 | (defun emr:report-action (description line text)
169 | "Report the action that occured at the point of difference.
170 |
171 | Displays a short summary containing the line number, a
172 | description of the change, and a snippet of text from the
173 | buffer."
174 | (when emr-report-actions
175 |
176 | (->> (if (s-blank? text)
177 | "nil"
178 | (replace-regexp-in-string "[ \n\r\t]+" " " text))
179 |
180 | (format "%s line %s: %s" description line)
181 | (s-truncate (window-width (minibuffer-window)))
182 | (message))))
183 |
184 | (defun emr:line-visible? (line)
185 | "Return non-nil if LINE is within the visible bounds of the current window."
186 | (let* ((min (line-number-at-pos (window-start)))
187 | (max (line-number-at-pos (window-end))))
188 | (and (>= line min) (<= line max))))
189 |
190 | ;;;###autoload
191 | (defmacro emr-reporting-buffer-changes (description &rest body)
192 | "Perform a refactoring action and show a brief diff.
193 | * DESCRIPTION describes the overall action, and is shown to the user.
194 | * BODY forms perform the refactor action."
195 | (declare (indent 1))
196 | `(let ((before-changes (buffer-string)))
197 | ,@body
198 | ;; Report changes.
199 | (-when-let (diff (and emr-report-actions
200 | (car (emr:diff-lines before-changes (buffer-string)))))
201 | (cl-destructuring-bind (_ . (line . text)) diff
202 | (unless (emr:line-visible? line)
203 | (emr:report-action ,description line text))))))
204 |
205 | ;;; Popup menu
206 |
207 | ;;; Items to be displayed in the refactoring popup menu are declared using
208 | ;;; the `emr-declare-command' macro. This macro builds a struct to
209 | ;;; represent the command and adds it to a table for later retrieval.
210 | ;;;
211 | ;;; When the user invokes the popup menu, each struct is transformed into a
212 | ;;; function that will return a popup if that command is available.
213 |
214 | (cl-defstruct emr-refactor-spec
215 | function title description modes predicate)
216 |
217 | (defvar emr:refactor-commands (make-hash-table :test 'equal)
218 | "A table of refactoring specs used to build menu items.")
219 |
220 | (defun emr:documentation (sym)
221 | "Get the docstring for SYM.
222 | Removes the function arglist and Lisp usage example."
223 | (cl-destructuring-bind (before-example &optional after-example)
224 | (->> (documentation sym)
225 | (s-lines)
226 | ;; Remove the function arglist.
227 | (nreverse)
228 | (-drop 1)
229 | (nreverse)
230 | (s-join "\n")
231 | (s-trim)
232 | ;; Find the EXAMPLE section.
233 | (s-split (rx bol "EXAMPLE:")))
234 | ;; Rejoin with the EXAMPLE removed.
235 | (concat before-example
236 | (when after-example
237 | (->> after-example
238 | (s-lines)
239 | (--drop-while (or (s-blank-str? it)
240 | (s-matches? (rx bol (+ space)) it)))
241 | (s-join "\n"))))))
242 |
243 | ;;;###autoload
244 | (cl-defun emr-declare-command
245 | (function &key modes title description predicate)
246 | "Define a refactoring command.
247 |
248 | * FUNCTION is the refactoring command to perform. It should be
249 | either the name of a refactoring command as a symbol or a
250 | lambda-expression.
251 |
252 | * MODES is a symbol or list of symbols. These are the modes in
253 | which this command will be available. This will also enable the
254 | command for derived modes.
255 |
256 | * TITLE is the name of the command that will be displayed in the
257 | popup menu.
258 |
259 | * PREDICATE is a condition that must be satisfied to display this
260 | item. It should be a lambda-expression or function name.
261 |
262 | * DESCRIPTION is shown to the left of the title in the popup
263 | menu."
264 | (declare (indent 1))
265 | (cl-assert title)
266 | (cl-assert modes)
267 | (cl-assert (or (functionp predicate)
268 | (symbolp predicate)))
269 | ;; Add the created function into the global table of refactoring commands.
270 | (puthash function
271 | (make-emr-refactor-spec
272 | :function function
273 | :title title
274 | :modes (if (symbolp modes) (list modes) modes)
275 | :predicate predicate
276 | :description description)
277 | emr:refactor-commands))
278 |
279 | (defun emr:hash-values (ht)
280 | "Return the hash values in hash table HT."
281 | (cl-loop for v being the hash-values in ht collect v))
282 |
283 | (defun emr:make-popup (struct)
284 | "Test whether the refactoring specified by STRUCT is available.
285 | Return a popup item for the refactoring menu if so."
286 | (when (and
287 | ;; 1. Test whether this command is available in the current
288 | ;; buffer's major mode.
289 | (apply 'derived-mode-p (emr-refactor-spec-modes struct))
290 | ;; 2. Run the declared predicate to test whether the refactoring
291 | ;; command is available in the current context.
292 | (ignore-errors
293 | (funcall (emr-refactor-spec-predicate struct))))
294 | ;; If the above tests succeed, create a popup for the
295 | ;; refactor menu.
296 | (popup-make-item (emr-refactor-spec-title struct)
297 | :value (emr-refactor-spec-function struct)
298 | :summary (emr-refactor-spec-description struct)
299 | :document (ignore-errors
300 | (emr:documentation
301 | (emr-refactor-spec-function struct))))))
302 |
303 | ;;;###autoload
304 | (defun emr-show-refactor-menu ()
305 | "Show the refactor menu at point."
306 | (interactive)
307 | (emr-initialize)
308 | ;; Run each factory function and collect the menu items representing
309 | ;; available commands.
310 | (-if-let (actions (->> emr:refactor-commands
311 | (emr:hash-values)
312 | (-keep 'emr:make-popup)))
313 | ;; Display the menu.
314 | (atomic-change-group
315 | (-when-let (action (popup-menu*
316 | actions
317 | :isearch t
318 | :help-delay emr-popup-help-delay))
319 | (call-interactively action)))
320 |
321 | ;; Having no items to show implies that no refactoring commands are
322 | ;; available.
323 | (message "No refactorings available")))
324 |
325 | ;;;###autoload
326 | (defun emr-initialize ()
327 | "Activate language support for EMR."
328 |
329 | (require 'emr-prog)
330 | (require 'emr-iedit)
331 |
332 | ;; Lazily load support for individual languages.
333 |
334 | (eval-after-load 'lisp-mode '(emr-el-initialize))
335 | (eval-after-load 'cc-mode '(emr-c-initialize))
336 | (eval-after-load 'scheme '(require 'emr-scheme))
337 | (eval-after-load 'js2-refactor '(require 'emr-js))
338 | (eval-after-load 'ruby-refactor '(require 'emr-ruby))
339 | (eval-after-load 'css-mode '(require 'emr-css)))
340 |
341 | (provide 'emr)
342 |
343 | ;;; emr.el ends here
344 |
--------------------------------------------------------------------------------
/test/emr-css-test.el:
--------------------------------------------------------------------------------
1 | ;;; emr-elisp-test --- Tests for emr-elisp -*- lexical-binding: t; -*-
2 |
3 | ;; Copyright (C) 2016 Wilfred Hughes
4 |
5 | ;; Author: Wilfred Hughes .
21 |
22 | ;;; Commentary:
23 |
24 | ;; Tests for emr-css
25 |
26 | ;;; Code:
27 |
28 | (require 'emr-css)
29 |
30 | (ert-deftest emr-css-add-important ()
31 | "Add !important in CSS."
32 | (with-temp-buffer
33 | (css-mode)
34 | (insert "display: block;")
35 | (emr-css-toggle-important)
36 | (should
37 | (equal
38 | (buffer-string)
39 | "display: block !important;"))))
40 |
41 | (ert-deftest emr-css-remove-important ()
42 | "Remove !important if present in CSS."
43 | (with-temp-buffer
44 | (css-mode)
45 | (insert "display: block !important;")
46 | (emr-css-toggle-important)
47 | (should
48 | (equal
49 | (buffer-string)
50 | "display: block;"))))
51 |
52 | (provide 'emr-css-test)
53 |
54 | ;;; emr-css-test.el ends here
55 |
--------------------------------------------------------------------------------
/test/emr-elisp-test.el:
--------------------------------------------------------------------------------
1 | ;;; emr-elisp-test --- Tests for emr-elisp -*- lexical-binding: t; -*-
2 |
3 | ;; Copyright (C) 2013 Chris Barrett
4 |
5 | ;; Author: Chris Barrett
6 |
7 | ;; This file is not part of GNU Emacs.
8 |
9 | ;; This program is free software: you can redistribute it and/or modify
10 | ;; it under the terms of the GNU General Public License as published by
11 | ;; the Free Software Foundation, either version 3 of the License, or
12 | ;; (at your option) any later version.
13 |
14 | ;; This program is distributed in the hope that it will be useful,
15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | ;; GNU General Public License for more details.
18 |
19 | ;; You should have received a copy of the GNU General Public License
20 | ;; along with this program. If not, see .
21 |
22 | ;;; Commentary:
23 |
24 | ;; Tests for emr-elisp
25 |
26 | ;;; Code:
27 |
28 | ;;;; Function implementation.
29 |
30 | (require 'emr-elisp)
31 | (require 'dash)
32 | (require 's)
33 |
34 | (ert-deftest emr-elisp-symbol-names ()
35 | "elisp--uses symbol names when inferring arglists from callsites"
36 | (let ((fname (cl-gensym)))
37 | (should
38 | (equal
39 | '(x y)
40 | (emr-el:infer-arglist-for-usage `(,fname x y))))))
41 |
42 | (ert-deftest emr-elisp-argn ()
43 | "elisp--uses argn for non-symbol names when inferring arglists from callsites"
44 | (should
45 | (equal
46 | '(arg1 arg2)
47 | (emr-el:infer-arglist-for-usage '(hello 9 8)))))
48 |
49 | (ert-deftest emr-el:interactive-form-p ()
50 | (should
51 | (not
52 | (emr-el:interactive-form-p
53 | '(defun foo () 1))))
54 | (should
55 | (emr-el:interactive-form-p
56 | '(defun foo () (interactive) 1)))
57 | (should
58 | (emr-el:interactive-form-p
59 | '(defun foo () "docstring" (interactive) 1))))
60 |
61 | (ert-deftest emr-el:looking-at-symbol-p ()
62 | (with-temp-buffer
63 | (emacs-lisp-mode)
64 | (insert "foo")
65 |
66 | (goto-char (point-min))
67 | (should (emr-el:looking-at-symbol-p))
68 |
69 | (goto-char (point-max))
70 | (should (emr-el:looking-at-symbol-p)))
71 |
72 | (dolist (src '("()" "[]" "123" "1.1" "-1" "1e2" "?x" "\"foo\""
73 | ";; foo" ":foo" "'foo"))
74 | (with-temp-buffer
75 | (emacs-lisp-mode)
76 | (insert src)
77 | (goto-char (point-min))
78 |
79 | (should (not (emr-el:looking-at-symbol-p))))))
80 |
81 | ;;;; Bound variables
82 |
83 | (ert-deftest emr-elisp-free-vars-let ()
84 | "elisp--finds free vars in let form"
85 | (should
86 | (equal
87 | '(a b c d)
88 |
89 | (emr-el:free-variables
90 | '(let (x (y a))
91 | b
92 | (let (z w)
93 | c
94 | (list d)))))))
95 |
96 | (ert-deftest emr-elisp-quoted-vars ()
97 | "elisp--quoted vars are not free variables"
98 | (should
99 | (equal
100 | '(x)
101 |
102 | (emr-el:free-variables
103 | '(list x 'y)))))
104 |
105 | (ert-deftest emr-elisp-free-vars-let* ()
106 | "elisp--finds free vars in let* form"
107 | (should
108 | (equal
109 | '(a b c d)
110 |
111 | (emr-el:free-variables
112 | '(let* (x (y a))
113 | b
114 | (let* (z w)
115 | c
116 | (list d)))))))
117 |
118 | (ert-deftest emr-elisp-free-vars-lambda ()
119 | "elisp--finds free vars in lambda form"
120 | (should
121 | (equal
122 | '(a b c)
123 |
124 | (emr-el:free-variables
125 | '(lambda (x &rest y)
126 | a
127 | (list b)
128 | (lambda (z w)
129 | c))))))
130 |
131 | (ert-deftest emr-elisp-free-vars-progn ()
132 | "elisp--finds free vars in progn form"
133 | (should
134 | (equal
135 | '(a b c)
136 |
137 | (emr-el:free-variables
138 | '(progn
139 | a
140 | (lambda (x &rest y) b)
141 | (let (z w) c))))))
142 |
143 | (ert-deftest emr-elisp-free-vars-bind ()
144 | "elisp--finds free vars in cl-destructuring-bind"
145 | (should
146 | (equal
147 | '(a b c d)
148 |
149 | (emr-el:free-variables
150 | '(cl-destructuring-bind (x y) (list 1 2)
151 | a
152 | (list b)
153 | (cl-destructuring-bind (z . w) (list 3 4 5)
154 | (list c d)))))))
155 |
156 | (ert-deftest emr-free-vars-defun ()
157 | "elisp--finds free vars in defun form"
158 | (should
159 | (equal
160 | '(a b)
161 |
162 | (emr-el:free-variables
163 | '(defun hello (x y)
164 | (list a x)
165 | (progn
166 | (list b y)))))))
167 |
168 | (ert-deftest emr-elisp-free-vars-function-sym ()
169 | "elisp--survives function symbol followed by non-lambda term"
170 | (let ((fname (cl-gensym)))
171 | (should
172 | (equal
173 | `(,fname)
174 |
175 | (emr-el:free-variables
176 | `(function ,fname))))))
177 |
178 | (ert-deftest emr-elisp-free-vars-outer-scope ()
179 | "elisp--checks outer scope for bindings that share names with functions"
180 | (should
181 | (equal
182 | '(message y)
183 |
184 | (emr-el:free-variables '(funcall message y)
185 | '(let (message)
186 | (funcall message y))))))
187 |
188 | (ert-deftest emr-elisp-extract-with-autoload ()
189 | "Ensure we don't move autoload cookies when we extract functions."
190 | (with-temp-buffer
191 | (delay-mode-hooks (emacs-lisp-mode))
192 |
193 | (insert ";;;###autoload\n(defun foo ()\n (+ 1 2))")
194 | (search-backward "+")
195 |
196 | (emr-el-extract-function "extracted" '())
197 |
198 | ;; Autoload cookie should still be before foo.
199 | (should
200 | (s-contains-p ";;;###autoload\n(defun foo"
201 | (buffer-string)))))
202 |
203 | ;;;; Inspecting forms at point
204 |
205 | (ert-deftest emr-el:looking-at-let-binding-symbol? ()
206 | ;; Vars with initial values.
207 | (with-temp-buffer
208 | (insert "(let* ((x 1)\n (y 2))\n 1)")
209 | (goto-char (point-min))
210 | (search-forward "y")
211 |
212 | (should (emr-el:looking-at-let-binding-symbol?)))
213 | ;; Vars without initial values.
214 | (with-temp-buffer
215 | (insert "(let* (x y)\n 1)")
216 | (goto-char (point-min))
217 | (search-forward "y")
218 |
219 | (should (emr-el:looking-at-let-binding-symbol?)))
220 | ;; Not looking at a symbol.
221 | (with-temp-buffer
222 | (insert "(let* ((x 1)\n (y 2))\n 1)")
223 | (goto-char (point-min))
224 | (search-forward "2")
225 |
226 | (should (not (emr-el:looking-at-let-binding-symbol?))))
227 | ;; A symbol, but in the let body.
228 | (with-temp-buffer
229 | (insert "(let* ((x 1)\n (y 2))\n y)")
230 | (goto-char (point-max))
231 | (search-backward "y")
232 |
233 | (should (not (emr-el:looking-at-let-binding-symbol?))))
234 | ;; A symbol, but used as an initial value for another symbol.
235 | (with-temp-buffer
236 | (insert "(let* ((x 1)\n (y z))\n y)")
237 | (goto-char (point-min))
238 | (search-forward "z")
239 |
240 | (should (not (emr-el:looking-at-let-binding-symbol?)))))
241 |
242 | (ert-deftest emr-elisp-top-level-let ()
243 | "elisp--a top level let is not a definition"
244 | (with-temp-buffer
245 | (insert "(let ((x 1))\n (message \"foo: %s %s\" x 'bar))")
246 | (goto-char (point-min))
247 | (search-forward "message")
248 | (should (not (emr-el:looking-at-definition?)))))
249 |
250 | (ert-deftest emr-el-extract-to-let--body ()
251 | "Extracting a variable from the body."
252 | (with-temp-buffer
253 | (insert "(let ((x 1))\n (+ 1 2))")
254 | (search-backward "(+")
255 | (emr-el-extract-to-let 'y)
256 |
257 | (let ((result-form (read (buffer-string))))
258 | (should
259 | (equal
260 | result-form
261 | '(let ((x 1)
262 | (y (+ 1 2)))
263 | y))))))
264 |
265 | (ert-deftest emr-el-extract-to-let--first-var ()
266 | "Ensure whitspace is correct for the first variable."
267 | (with-temp-buffer
268 | (insert "(+ 1 (* 2 3))")
269 | (search-backward "(*")
270 | (emr-el-extract-to-let 'x)
271 |
272 | (should
273 | (equal (buffer-string) "(let ((x (* 2 3)))\n (+ 1 x))"))))
274 |
275 | (ert-deftest emr-el-extract-to-let--let-var ()
276 | "Extracting a variable from another let-bound variable."
277 | (with-temp-buffer
278 | (insert "(let ((x (+ 1 2)))\n x)")
279 | (search-backward "(+")
280 | (emr-el-extract-to-let 'z)
281 |
282 | (let ((result-form (read (buffer-string))))
283 | (should
284 | (equal
285 | result-form
286 | '(let* ((z (+ 1 2))
287 | (x z))
288 | x))))))
289 |
290 | (ert-deftest emr-el-extract-to-let--numeric-literal ()
291 | "Extracting a literal value from another let-bound variable."
292 | (with-temp-buffer
293 | (insert "(let ((x 3))\n x)")
294 | (search-backward "3")
295 | (transient-mark-mode t)
296 | (set-mark (1+ (point)))
297 | (emr-el-extract-to-let 'z)
298 |
299 | (let ((result-form (read (buffer-string))))
300 | (should
301 | (equal
302 | result-form
303 | '(let* ((z 3)
304 | (x z))
305 | x))))))
306 |
307 | (ert-deftest emr-el-extract-to-let--no-let ()
308 | "Extracting a let variable when we don't a let form yet."
309 | (with-temp-buffer
310 | (insert "(defun foo ()\n (+ 1 2))")
311 | (search-backward "(+")
312 | (emr-el-extract-to-let 'x)
313 |
314 | (let ((result-form (read (buffer-string))))
315 | (should
316 | (equal
317 | result-form
318 | '(defun foo ()
319 | (let ((x (+ 1 2)))
320 | x)))))))
321 |
322 | ;;;; Commands
323 |
324 | (ert-deftest emr-el:find-unused-defs ()
325 | "Ensure we don't crash on quoted definitions."
326 | (with-temp-buffer
327 | (insert "(defmacro my-defvar (name val)\n `(defvar ,name ,val))")
328 |
329 | (emr-el:find-unused-defs)))
330 |
331 | (defun emr-el-test-example-docstring (str)
332 | "Extract the example invocation, before and after from a docstring."
333 | (let (start-pos example before after)
334 | (with-temp-buffer
335 | (insert str)
336 | (goto-char (point-min))
337 |
338 | (search-forward-regexp (rx bol "EXAMPLE:\n"))
339 | (setq start-pos (point))
340 |
341 | (search-forward-regexp (rx bol "BEFORE:\n"))
342 | (setq example
343 | (buffer-substring start-pos (line-beginning-position -1)))
344 |
345 | (setq start-pos (point))
346 | (search-forward-regexp (rx bol "AFTER:\n"))
347 | (setq before
348 | (buffer-substring start-pos (line-beginning-position -1)))
349 |
350 | (setq after
351 | (buffer-substring (point) (point-max))))
352 | (list
353 | (emr-el-tests:unindent-rigidly example)
354 | (emr-el-tests:unindent-rigidly before)
355 | (emr-el-tests:unindent-rigidly after))))
356 |
357 | (defun emr-el-tests:unindent-rigidly (string)
358 | "Given an indented STRING, unindent rigidly until
359 | at least one line has no indent."
360 | (let* ((lines (s-lines string))
361 | (nonblanks (--remove (s-blank? it) lines))
362 | ;; Get the leading whitespace for each line.
363 | (indents (--map (car (s-match (rx bos (+ whitespace)) it))
364 | nonblanks))
365 | (min-indent (-min (--map (length it) indents)))
366 | (unindented (--map
367 | (if (s-blank? it)
368 | it
369 | (substring it min-indent))
370 | lines)))
371 | (s-trim (s-join "\n" unindented))))
372 |
373 | (defmacro gentest-from-docstring (fname)
374 | "Define an ERT test according to the spec in FNAME's docstring.
375 | FNAME is a refactoring command with a docstring of the following style:
376 |
377 |
378 |
379 | EXAMPLE:
380 |
381 |
382 | BEFORE:
383 |
384 |
386 |
387 | AFTER:
388 |
389 |
390 |
391 | "
392 | `(ert-deftest ,fname ()
393 | (let* ((text-quoting-style 'straight)
394 | (docstring (documentation ',fname)))
395 |
396 | ;; Basic sanity checks before running.
397 | (assert (not (s-blank? docstring)))
398 | (assert (s-contains? "EXAMPLE:" docstring))
399 | (assert (s-contains? "BEFORE:" docstring))
400 | (assert (s-contains? "AFTER:" docstring))
401 | (-let [(form before after) (emr-el-test-example-docstring docstring)]
402 | (with-temp-buffer
403 | ;; Insert the BEFORE into the buffer.
404 | (delay-mode-hooks (lisp-mode))
405 | (insert before)
406 |
407 | ;; Move to position.
408 | (goto-char (point-min))
409 | (search-forward "|")
410 | (delete-char -1)
411 |
412 | ;; Run the command we're testing.
413 | (eval (read form))
414 |
415 | (let ((result
416 | (s-trim (buffer-substring-no-properties (point-min) (point-max)))))
417 | (should (equal result after))))))))
418 |
419 | (gentest-from-docstring emr-el-inline-variable)
420 | (gentest-from-docstring emr-el-inline-let-variable)
421 | (gentest-from-docstring emr-el-toggle-let*)
422 | (gentest-from-docstring emr-el-eval-and-replace)
423 | (gentest-from-docstring emr-el-extract-function)
424 | (gentest-from-docstring emr-el-extract-constant)
425 | (gentest-from-docstring emr-el-extract-variable)
426 | (gentest-from-docstring emr-el-insert-autoload-directive)
427 |
428 | (provide 'emr-elisp-test)
429 |
430 | ;;; emr-elisp-test.el ends here
431 |
--------------------------------------------------------------------------------
/test/test-helper.el:
--------------------------------------------------------------------------------
1 | ;;; test-utils.el --- Common utilities for emr tests -*- lexical-binding: t; -*-
2 |
3 | ;; Copyright (C) 2013 Chris Barrett
4 | ;; Copyright (C) 2018 Wilfred Hughes
5 |
6 | ;; Author: Chris Barrett
7 |
8 | ;; This file is not part of GNU Emacs.
9 |
10 | ;; This program is free software: you can redistribute it and/or modify
11 | ;; it under the terms of the GNU General Public License as published by
12 | ;; the Free Software Foundation, either version 3 of the License, or
13 | ;; (at your option) any later version.
14 |
15 | ;; This program is distributed in the hope that it will be useful,
16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | ;; GNU General Public License for more details.
19 |
20 | ;; You should have received a copy of the GNU General Public License
21 | ;; along with this program. If not, see .
22 |
23 | ;;; Commentary:
24 |
25 | ;; Common utilities for emr tests
26 |
27 | ;;; Code:
28 |
29 | (require 'ert)
30 | (require 'f)
31 |
32 | (let ((emr-dir (f-parent (f-dirname (f-this-file)))))
33 | (add-to-list 'load-path emr-dir))
34 |
35 | (require 'undercover)
36 | (undercover "emr*.el"
37 | (:exclude "*-test.el")
38 | (:report-file "/tmp/undercover-report.json"))
39 |
40 | ;;; test-helper.el ends here
41 |
--------------------------------------------------------------------------------