├── .dir-locals.el
├── .github
└── workflows
│ └── test.yml
├── .gitignore
├── Keg
├── LICENSE
├── README.org
├── codecov.yml
├── mic-adapter.el
├── mic-deffilter.el
├── mic-definputter.el
├── mic-filter.el
├── mic-utils.el
├── mic.el
└── test
├── mic-adapter-test.el
├── mic-deffilter-test.el
├── mic-definputter-test.el
├── mic-filter-test.el
├── mic-test.el
└── mic-utils-test.el
/.dir-locals.el:
--------------------------------------------------------------------------------
1 | ((emacs-lisp-mode (package-lint-main-file . "mic.el")))
2 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: test
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | test:
7 | runs-on: ${{ matrix.os }}
8 | continue-on-error:
9 | ${{ contains(fromJson('["snapshot"]'), matrix.emacs_version) }}
10 | strategy:
11 | fail-fast: false
12 | matrix:
13 | os:
14 | - ubuntu-latest
15 |
16 | emacs_version:
17 | - '26.1'
18 | - '26.2'
19 | - '26.3'
20 | - '27.1'
21 | - '27.2'
22 | - '28.1'
23 | - '28.2'
24 | - '29.1'
25 | - '29.2'
26 | - '29.3'
27 | - '29.4'
28 | - 'snapshot'
29 |
30 | steps:
31 | - uses: actions/checkout@v3
32 | - uses: purcell/setup-emacs@master
33 | with:
34 | version: ${{ matrix.emacs_version }}
35 | - uses: conao3/setup-keg@master
36 |
37 | - run: keg install
38 | - run: keg run test-all
39 | env:
40 | UNDERCOVER_FORCE: true
41 | - uses: codecov/codecov-action@v2
42 |
43 | lint:
44 | runs-on: ubuntu-latest
45 | continue-on-error: ${{ true }}
46 | steps:
47 | - uses: actions/checkout@v1
48 | - uses: purcell/setup-emacs@master
49 | with:
50 | version: '29.4'
51 | - uses: conao3/setup-keg@master
52 | - run: keg lint
53 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | flycheck_*
2 | *.elc
3 | .keg/
4 | coverage-final.json
5 |
--------------------------------------------------------------------------------
/Keg:
--------------------------------------------------------------------------------
1 | (source gnu nongnu melpa)
2 |
3 | (package (mic
4 | (recipe . (:fetcher github :repo "ROCKTAKEY/mic"))))
5 |
6 | (dev-dependency undercover)
7 |
8 | (script
9 | (test
10 | (let ((test-files (mapcar (lambda (file) (file-relative-name file "."))
11 | (directory-files "test" t "\\.el$"))))
12 | (keg-shell
13 | `("keg" "emacs" "--batch" "-Q" "-L" "."
14 | ,@(mapcar (apply-partially #'concat "--load=") test-files)
15 | "-f" "ert-run-tests-batch-and-exit"))))
16 | (test-all
17 | (keg-shell "keg clean-elc"
18 | "keg run test"
19 | "keg build"
20 | "keg run test"
21 | "keg clean-elc")))
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/README.org:
--------------------------------------------------------------------------------
1 | [[https://github.com/ROCKTAKEY/mic][https://img.shields.io/github/tag/ROCKTAKEY/mic.svg?style=flat-square]]
2 | [[file:LICENSE][https://img.shields.io/github/license/ROCKTAKEY/mic.svg?style=flat-square]]
3 | [[https://codecov.io/gh/ROCKTAKEY/mic?branch=master][https://img.shields.io/codecov/c/github/ROCKTAKEY/mic.svg?style=flat-square]]
4 | [[https://github.com/ROCKTAKEY/mic/actions][https://img.shields.io/github/actions/workflow/status/ROCKTAKEY/mic/test.yml.svg?branch=master&style=flat-square]]
5 | * mic: Minimal and combinable configuration manager for Emacs
6 | #+BEGIN_QUOTE
7 | =mic= is uncustomizable. Define your own =mic=.
8 | #+END_QUOTE
9 |
10 | =mic= is minimal and combinable configuration manager for Emacs.
11 | This package is yet another =use-package= and =leaf=, but is also used with them (See [[#alternative][Alternative]]).
12 | =mic= is minimal, so if you would like to write complex configuration,
13 | =mic= is a little redundant. However, there is no problem. =mic= is combinable, in the other words, thought to be
14 | used as core to define your own, and more convenient =mic=.
15 | There are some functions to define your own =mic=. See [[#define-your-own-mic][Define your own mic]].
16 |
17 | * How to Use?
18 | For Emacs Lisp beginners, original =mic= macro is useful to configure your =init.el=.
19 | #+begin_src emacs-lisp :tangle yes
20 | (mic lsp-mode
21 | ;; These are transformed to `define-key' sexp.
22 | ;; Each argument is `(KEYMAP (KEYS . COMMAND)...)'.
23 | ;; KEYS is passed to `kbd'.
24 | :define-key
25 | ((global-map
26 | ("M-l" . #'lsp)))
27 |
28 | ;; These are same as `:define-key' argument,
29 | ;; but evaluated after loading the feature (`lsp-mode' for this example).
30 | ;; This is needed because `lsp-mode-map' is unavailable before `lsp'
31 | ;; loading.
32 | :define-key-after-load
33 | ((lsp-mode-map
34 | ("M-r" . #'lsp-rename)
35 | ("M-c" . #'lsp-execute-code-action)))
36 |
37 | ;; These are transformed to `with-eval-after-load' and `define-key' sexp.
38 | ;; Each argument is `(FEATURE (KEYMAP (KEYS . COMMAND)...))'.
39 | ;; `cdr' is same as `:define-key' arguments. Each `define-key' sexp is
40 | ;; evaluated after FEATURE is loaded.
41 | ;; This is needed because `dired-mode-map' is unavailable before `dired'
42 | ;; loading.
43 | :define-key-with-feature
44 | ((dired
45 | (dired-mode-map
46 | ("M-q" . #'lsp-dired-mode))))
47 |
48 | ;; These are transformed to `customize-set-variable' sexp.
49 | ;; Each argument is `(VARIABLE . VALUE)'.
50 | :custom
51 | ((lsp-sesstion-file . (expand-file-name "etc/.lsp-session-v1" user-emacs-directory))
52 | (lsp-log-io . t))
53 |
54 | ;; These are transformed to `add-hook' sexp.
55 | ;; Each argument is `(HOOK . FUNCTION)'.
56 | :hook
57 | ((c-mode-hook . #'lsp)
58 | (c++-mode-hook . #'lsp)
59 | (tex-mode-hook . #'lsp)
60 | (latex-mode-hook . #'lsp)
61 | (bibtex-mode-hook . #'lsp)
62 | (rust-mode-hook . #'lsp))
63 |
64 | ;; Each element is evaluated immediately when this `mic' sexp is evaluated.
65 | :eval
66 | ((message "This is evaluated when this `mic' sexp is evaluated.")
67 | (message "This is also evaluated."))
68 |
69 | ;; Each element will be evaluated after the package (`lsp-mode' for this example) is loaded.
70 | :eval-after-load
71 | ((message "This is evaluated when `lsp-mode' is loaded."))
72 |
73 | ;; Each element is evaluated immediately when this `mic' sexp is evaluated.
74 | ;; These are evaluated before `:eval' and `:eval-after-load' elements.
75 | ;; This is for such use as defining function to use `:custom' argument.
76 | :eval-before-all
77 | ((message "This is evaluated when this `mic' sexp is evaluated.")
78 | (message "These are evaluated before `:eval' and `:eval-after-load' sexp.")))
79 |
80 |
81 | ;; `mic' sexp above is expanded to:
82 | (prog1 'lsp-mode
83 | ;; `:eval-before-all'
84 | (message "This is evaluated when this `mic' sexp is evaluated.")
85 | (message "These are evaluated before `:eval' and `:eval-after-load' sexp.")
86 |
87 | ;; `:eval-after-load'
88 | (with-eval-after-load 'lsp-mode
89 | (message "This is evaluated when `lsp-mode' is loaded.")
90 | ;; `:define-key-after-load'
91 | (define-key lsp-mode-map
92 | (kbd "M-r")
93 | (function lsp-rename))
94 | (define-key lsp-mode-map
95 | (kbd "M-c")
96 | (function lsp-execute-code-action)))
97 |
98 | ;; `:eval'
99 | (message "This is evaluated when this `mic' sexp is evaluated.")
100 | (message "This is also evaluated.")
101 |
102 | ;; `:custom'
103 | (customize-set-variable 'lsp-sesstion-file
104 | (expand-file-name "etc/.lsp-session-v1" user-emacs-directory))
105 | (customize-set-variable 'lsp-log-io t)
106 |
107 | ;; `:define-key'
108 | (define-key global-map (kbd "M-l") #'lsp)
109 |
110 | ;; `:define-key-with-feature'
111 | (with-eval-after-load 'dired
112 | (define-key dired-mode-map (kbd "M-q") #'lsp-dired-mode))
113 |
114 | ;; `:hook'
115 | (add-hook 'c-mode-hook #'lsp)
116 | (add-hook 'c++-mode-hook #'lsp)
117 | (add-hook 'tex-mode-hook #'lsp)
118 | (add-hook 'latex-mode-hook #'lsp)
119 | (add-hook 'bibtex-mode-hook #'lsp)
120 | (add-hook 'rust-mode-hook #'lsp))
121 | #+end_src
122 |
123 | For Emacs Lisp expert, original =mic= is a little unsatisfactory or redundant.
124 | =mic= is not customizable, but you can define your own =mic= easily.
125 | 1. Determine parent. You can use as parent =mic=, =mic-core=, which is simpler =mic=.
126 | =mic-core= recieves only keywords start from =:eval=, such as =:eval=, =eval-after-load=.
127 | 2. Define filter functions. Each one recieves plist (property list) and returns plist.
128 | returned plist is passed to parent (such as =mic=, =mic-core=) or next filter.
129 | Note that filter function can get feature name as value of property =:name=.
130 | Of course, you can use pre-defined filters. =mic= is defined by some filters
131 | from the parent =mic-core=.
132 | 3. Define your own mic by =mic-defmic=. It recieves =NAME=, optional =DOCSTRING=,
133 | and keyword argument =FILTERS=. =NAME= is name of your own =mic=.
134 | =DOCSTRING= is the document string of yours. =FILTERS= are list of filter.
135 | As explained, filter recieves plist and returns plist. It filter plist to get
136 | desired behavior.
137 |
138 | #+begin_src emacs-lisp :tangle yes
139 | (defun my-filter-global-set-key-without-quote (plist)
140 | (let ((alist
141 | ;; Get value from your own keyword
142 | (plist-get plist :bind))
143 | sexps)
144 | (setq sexps
145 | ;; Transform each element
146 | (mapcar
147 | (lambda (arg)
148 | (let ((keys (car arg))
149 | (command (cdr arg)))
150 | `(global-set-key (kbd ,keys) #',command)))
151 | alist))
152 | ;; Put sexps to `:eval' arguments
153 | (mic-plist-put-append plist :eval sexps)
154 | ;; Don't forget to delete your own keyword!
155 | ;; When forget it, parent recieves it and may cause unexpected result.
156 | (mic-plist-delete plist :bind)
157 | plist))
158 |
159 | (mic-defmic mymic
160 | ;; Parent is here. You can also use `mic-core'.
161 | mic
162 | :filters
163 | '(my-filter-global-set-key-without-quote
164 | ;; You can add other filters below
165 | ))
166 |
167 | ;; Then you can use `mymic' like:
168 | (mymic simple
169 | :bind
170 | (("C-d" . delete-forward-char)
171 | ("C-x l" . toggle-truncate-lines))
172 | ;; Of course parent keywords are accepted.
173 | :custom
174 | ((kill-whole-line . t)
175 | (set-mark-command-repeat-pop . t)
176 | (mark-ring-max . 50)))
177 |
178 | ;; `mymic' sexp is expanded to:
179 | (mic simple
180 | :custom
181 | ((kill-whole-line . t)
182 | (set-mark-command-repeat-pop . t)
183 | (mark-ring-max . 50))
184 | :eval
185 | ((global-set-key (kbd "C-d") #'delete-forward-char)
186 | (global-set-key (kbd "C-x l") #'toggle-truncate-lines)))
187 |
188 | ;; Expanded to:
189 | (mic-core simple
190 | :eval
191 | ((global-set-key (kbd "C-d") #'delete-forward-char)
192 | (global-set-key (kbd "C-x l") #'toggle-truncate-lines)
193 | (customize-set-variable 'kill-whole-line t)
194 | (customize-set-variable 'set-mark-command-repeat-pop t)
195 | (customize-set-variable 'mark-ring-max 50))
196 | :eval-after-load nil)
197 |
198 | ;; Expanded to:
199 | (prog1 'simple
200 | (global-set-key (kbd "C-d") #'delete-forward-char)
201 | (global-set-key (kbd "C-x l") #'toggle-truncate-lines)
202 | (customize-set-variable 'kill-whole-line t)
203 | (customize-set-variable 'set-mark-command-repeat-pop t)
204 | (customize-set-variable 'mark-ring-max 50))
205 | #+end_src
206 |
207 | * Use =mic-core=, minimum one
208 | :PROPERTIES:
209 | :CUSTOM_ID: mic-core
210 | :END:
211 | =mic-core= is minimum. It can recieves only several keywords:
212 | - =:eval=
213 | - =:eval-after-load=
214 | - =:eval-after-others=
215 | - =:eval-after-others-after-load=
216 | - =:eval-before-all=
217 | - =:eval-installation=
218 |
219 | Each element of =:eval= arguments are evaluated.
220 | Time to evaluate is different.
221 |
222 | ** =:eval=, =:eval-after-others=, =:eval-before-all=
223 | Each element of these arguments are evaluated when the =mic= sexp is evaluated.
224 | The order is:
225 | - =:eval-before-all=
226 | - (=with-eval-after-load= sexp, explained on [[#eval-after-load][=eval-after-load= keyword section]], is evaluated)
227 | - =:eval=
228 | - =:eval-after-others=
229 |
230 | #+begin_src emacs-lisp :tangle yes
231 | (mic-core feature-name
232 | :eval
233 | ((message "eval1")
234 | (message "eval2"))
235 | :eval-after-others
236 | ((message "eval-after-others1")
237 | (message "eval-after-others2"))
238 | :eval-before-all
239 | ((message "eval-before-all1")
240 | (message "eval-before-all2"))
241 | :eval-after-load
242 | ((message "eval-after-load1")
243 | (message "eval-after-load2")))
244 |
245 | ;; Expanded to:
246 | (prog1 'feature-name
247 | (message "eval-before-all1")
248 | (message "eval-before-all2")
249 | (with-eval-after-load 'feature-name
250 | (message "eval-after-load1")
251 | (message "eval-after-load2"))
252 | (message "eval1")
253 | (message "eval2")
254 | (message "eval-after-others1")
255 | (message "eval-after-others2"))
256 | #+end_src
257 |
258 | =:eval-before-all= exists because a filter function appends sexp to =:eval= argument.
259 | When some action should be evaluated before all action added by other filters,
260 | you can put it to =:eval-before-all= argument. *Note that it should NOT be used
261 | by filters.* Any filter should not use this. If it is used by filters,
262 | users cannot make their sexp to be evaluate before filter sexps.
263 |
264 | =:eval-after-others= exists because similar reason to =:eval-before-all=.
265 | Some action should be evaluated after all action added by other filters.
266 | Because of same reasons as =:eval-before-all=, *it should NOT be used
267 | by filters*.
268 |
269 | ** =:eval-after-load=, =:eval-after-others-after-load=
270 | :PROPERTIES:
271 | :CUSTOM_ID: eval-after-load
272 | :END:
273 | Each element of these arguments are evaluated after the package is loaded.
274 | The evaluated order is:
275 | - =:eval-after-load=
276 | - =:eval-after-others-after-load=
277 |
278 | #+begin_src emacs-lisp :tangle yes
279 | (mic-core feature-name
280 | :eval-after-load
281 | ((message "eval-after-load1")
282 | (message "eval-after-load2"))
283 | :eval-after-others-after-load
284 | ((message "eval-after-others-after-load1")
285 | (message "eval-after-others-after-load2")))
286 |
287 | ;; Expanded to:
288 | (prog1 'feature-name
289 | (with-eval-after-load 'feature-name
290 | (message "eval-after-load1")
291 | (message "eval-after-load2")
292 | (message "eval-aftepr-others-after-load1")
293 | (message "eval-after-others-after-load2")))
294 | #+end_src
295 |
296 | =:eval-after-others-after-load= exists because similar reason to =:eval-after-others=.
297 | Some action should be evaluated after all action added by other filters.
298 | Because of same reasons as =:eval-before-all=, *it should NOT be used
299 | by filters*.
300 |
301 | ** =:eval-installation=
302 | :PROPERTIES:
303 | :CUSTOM_ID: eval-installation
304 | :END:
305 | Each element of this argument is evaluated before evaluation of other =:eval*= argument except =:eval-before-all=.
306 | This exists because sexp to install the package is evaluated before sexp which uses package features.
307 |
308 | #+begin_src emacs-lisp :tangle yes
309 | (mic-core feature-name
310 | :eval-before-all
311 | ((message "before all2")
312 | (message "before all1"))
313 | :eval-installation
314 | ((message "install1")
315 | (message "install2"))
316 | :eval-after-load
317 | ((message "eval-after-load1")
318 | (message "eval-after-load2"))
319 | :eval-after-others-after-load
320 | ((message "eval-after-others-after-load1")
321 | (message "eval-after-others-after-load2"))
322 | :eval
323 | ((message "eval1")
324 | (message "eval2")))
325 |
326 | ;; Expanded to:
327 | (prog1 'feature-name
328 | (message "before all2")
329 | (message "before all1")
330 | (message "install1")
331 | (message "install2")
332 | (with-eval-after-load 'feature-name
333 | (message "eval-after-load1")
334 | (message "eval-after-load2")
335 | (message "eval-after-others-after-load1")
336 | (message "eval-after-others-after-load2"))
337 | (message "eval1")
338 | (message "eval2"))
339 | #+end_src
340 |
341 | =:eval-after-others-after-load= exists because similar reason to =:eval-after-others=.
342 | Some action should be evaluated after all action added by other filters.
343 | Because of same reasons as =:eval-before-all=, *it should NOT be used
344 | by filters*.
345 |
346 | * Use default =mic=
347 | =mic= is minimal for use. =mic-core= is minimum core, but it is not enough to use as it is.
348 | In addition to keywords allowed by [[#mic-core][=mic-core=]], it allows some keyword arguments:
349 | - =:autoload-interactive=
350 | - =:autoload-noninteractive=
351 | - =:auto-mode=
352 | - =:custom=
353 | - =:custom-after-load=
354 | - =:declare-function=
355 | - =:define-key=
356 | - =:define-key-after-load=
357 | - =:define-key-with-feature=
358 | - =:defvar-noninitial=
359 | - =:face=
360 | - =:hook=
361 | - =:package=
362 | - =:require=
363 | - =:require-after=
364 |
365 | ** =:autoload-interactive=, =:autoload-noninteractive=
366 |
367 | These are transformed to =autoload= sexps. Each element is function to autoload.
368 | Since =autoload= should be informed whether the function is =interactive= or not,
369 | both =:autoload-interactive= and =:autoload-noninteractive= exist.
370 |
371 | #+begin_src emacs-lisp :tangle yes
372 | (mic feature-name
373 | :autoload-interactive
374 | (interactive-func1
375 | interactive-func2)
376 | :autoload-noninteractive
377 | (noninteractive-func3
378 | noninteractive-func4))
379 |
380 | ;; Expanded to:
381 | (mic-core feature-name :eval
382 | ((autoload #'interactive-func1 "feature-name" nil t)
383 | (autoload #'interactive-func2 "feature-name" nil t)
384 | (autoload #'noninteractive-func3 "feature-name")
385 | (autoload #'noninteractive-func4 "feature-name"))
386 | :eval-after-load nil)
387 |
388 | ;; Expanded to:
389 | (prog1 'feature-name
390 | (autoload #'interactive-func1 "feature-name" nil t)
391 | (autoload #'interactive-func2 "feature-name" nil t)
392 | (autoload #'noninteractive-func3 "feature-name")
393 | (autoload #'noninteractive-func4 "feature-name"))
394 | #+end_src
395 |
396 | ** =:auto-mode=
397 | It is transformed to sexp like =(add-to-list 'auto-mode-alist ...)=.
398 | Each element of the value should be valid as an element of =auto-mode-alist=.
399 |
400 | #+begin_src emacs-lisp :tangle yes
401 | (mic feature-name
402 | :auto-mode
403 | (("\\.html?\\'" . web-mode)
404 | ("\\.css\\'" . web-mode)))
405 |
406 | ;; Expanded to:
407 | (mic-core feature-name :eval-installation
408 | ((add-to-list 'auto-mode-alist '("\\.html?\\'" . web-mode))
409 | (add-to-list 'auto-mode-alist '("\\.css\\'" . web-mode)))
410 | :eval nil :eval-after-load nil)
411 |
412 | ;; Expanded to:
413 | (prog1 'feature-name
414 | (add-to-list 'auto-mode-alist '("\\.html?\\'" . web-mode))
415 | (add-to-list 'auto-mode-alist '("\\.css\\'" . web-mode)))
416 |
417 |
418 | #+end_src
419 |
420 | ** =:custom=, =:custom-after-load=
421 | These are transformed to =customize-set-variable= sexps.
422 | Each element is =(VARIABLE . VALUE)=.
423 | Each =VARIABLE= is set to =VALUE=.
424 | Sexp from =:custom= argument are evaluated when the =mic= sexp is evaluated,
425 | while sexp from =:custom-after-load= argument are evaluated after the feature is loaded.
426 | =:custom-after-load= is used when you want to use initial value of customized variable
427 | or function defined in the feature.
428 |
429 | #+begin_src emacs-lisp :tangle yes
430 | (mic feature-name
431 | :custom
432 | ((variable1 . 1)
433 | ;; VALUE is evaluated
434 | (variable2 . (+ 1 1)))
435 | :custom-after-load
436 | ;; You can use the initial value of `variable3'
437 | ((variable3 . (+ variable3 1))
438 | ;; You can use function defined in the feature (for this example `feature-name')
439 | (variable2 . (function-defined-in-feature-name))))
440 |
441 | ;; Expanded to:
442 | (mic-core feature-name
443 | :eval
444 | ((customize-set-variable 'variable1 1)
445 | (customize-set-variable 'variable2
446 | (+ 1 1)))
447 | :eval-after-load
448 | ((customize-set-variable 'variable3
449 | (+ variable3 1))
450 | (customize-set-variable 'variable2
451 | (function-defined-in-feature-name))))
452 |
453 | ;; Expanded to:
454 | (prog1 'feature-name
455 | (with-eval-after-load 'feature-name
456 | ;; `variable3' is already defined.
457 | (customize-set-variable 'variable3
458 | (+ variable3 1))
459 | ;; `function-defined-in-feature-name' is already defined.
460 | (customize-set-variable 'variable2
461 | (function-defined-in-feature-name)))
462 | (customize-set-variable 'variable1 1)
463 | (customize-set-variable 'variable2
464 | (+ 1 1)))
465 | #+end_src
466 |
467 | ** =declare-function=, =defvar-noninitial=
468 | These arguments declare functions and variables.
469 | Each element of =declare-function= / =defvar-noninitial= is symbol as function/variable.
470 | They exist in order to suppress warning of undefined functions/variables.
471 |
472 | #+begin_src emacs-lisp :tangle yes
473 | (mic feature-name
474 | :declare-function
475 | (function1
476 | function2)
477 | :defvar-noninitial
478 | (variable1
479 | variable2))
480 |
481 | ;; Expanded to:
482 | (mic-core feature-name
483 | :eval
484 | ((declare-function function1 "ext:feature-name")
485 | (declare-function function2 "ext:feature-name")
486 | (defvar variable1)
487 | (defvar variable2))
488 | :eval-after-load nil)
489 |
490 | ;; Expanded to:
491 | (prog1 'feature-name
492 | ;; They declare that the functions `function1' and `function2' is defined in
493 | ;; the feature `feature-name'.
494 | (declare-function function1 "ext:feature-name")
495 | (declare-function function2 "ext:feature-name")
496 | ;; They declare that the variables `variable1' and `variable2' will be defined.
497 | ;; `defvar' without initial value declares symbol as variable.
498 | (defvar variable1)
499 | (defvar variable2))
500 | #+end_src
501 |
502 | ** =:define-key=, =:define-key-after-load=, =:define-key-with-feature=
503 | These arguments is transformed to =define-key= sexps.
504 | On =:define-key= or =:define-key-after-load=, each element of the argument is
505 | =(KEYMAP (KEYS . COMMAND)...)=. =KEYMAP= is keymap. =KEYS= is passed to =kbd=.
506 | =COMMAND= is interactive function.
507 |
508 | On =:define-key-with-feature=, each element is =(FEATURE (KEYMAP (KEYS . COMMAND)...))=.
509 | =FEATURE= is feature, and the =define-key= sexp is evaluated after loading the =FEATURE=.
510 | This exists in order to define =COMMAND= in the feature with =KEYS= to =KEYMAP= defined in =FEATURE=.
511 | Use it to make sure that =KEYMAP= is defined.
512 |
513 | #+begin_src emacs-lisp :tangle yes
514 | (mic feature-name
515 | :define-key
516 | ;; (KEYMAP (KEYS . COMMAND)...)
517 | ((global-map
518 | ;; #' is needed
519 | ("M-l" . #'feature-name-command1))
520 | (prog-mode-map
521 | ;; #' is needed
522 | ("M-a" . #'feature-name-comman2)))
523 |
524 | :define-key-after-load
525 | ;; When `feature-name-mode-map' is defined in `feature-name',
526 | ;; use `:define-key-after-load'.
527 | ((feature-name-mode-map
528 | ("M-r" . #'feature-name-command3)
529 | ("M-c" . #'feature-name-command4)))
530 |
531 |
532 | ;; When `other-feature-mode-map' is defined in `other-feature', which is not `feature-name',
533 | ;; use `:define-key-with-feature'.
534 | :define-key-with-feature
535 | ((other-feature
536 | (other-feature-mode-map
537 | ("M-q" . #'feature-name-command5)))))
538 |
539 | ;; Expanded to:
540 | (mic-core feature-name
541 | :eval
542 | ((define-key global-map (kbd "M-l") #'feature-name-command1)
543 | (define-key prog-mode-map (kbd "M-a") #'feature-name-comman2)
544 | (with-eval-after-load 'other-feature
545 | (define-key other-feature-mode-map (kbd "M-q") #'feature-name-command5)))
546 | :eval-after-load
547 | ((define-key feature-name-mode-map (kbd "M-r") #'feature-name-command3)
548 | (define-key feature-name-mode-map (kbd "M-c") #'feature-name-command4)))
549 |
550 | ;; Expanded to:
551 | (prog1 'feature-name
552 | (with-eval-after-load 'feature-name
553 | ;; `:define-key-after-load'
554 | (define-key feature-name-mode-map (kbd "M-r") #'feature-name-command3)
555 | (define-key feature-name-mode-map (kbd "M-c") #'feature-name-command4))
556 | ;; `:define-key'
557 | (define-key global-map (kbd "M-l") #'feature-name-command1)
558 | (define-key prog-mode-map (kbd "M-a") #'feature-name-comman2)
559 | ;; `:define-key-with-feature'
560 | (with-eval-after-load 'other-feature
561 | (define-key other-feature-mode-map (kbd "M-q") #'feature-name-command5)))
562 | #+end_src
563 |
564 | ** =:face=
565 | This is transformed to =custom-set-faces= sexp.
566 | Each element is =(FACE-SYMBOL . FACE-DEFINITION)=.
567 |
568 | #+begin_src emacs-lisp :tangle yes
569 | (mic feature-name
570 | :face
571 | ((face-1
572 | . ((t (:foreground "red" :height 10.0))))
573 | (face-2
574 | . ((t (:background "#006000" :foreground "white" :bold t))))))
575 |
576 | ;; Expanded to:
577 | (mic-core feature-name
578 | :eval
579 | ((custom-set-faces
580 | '(face-1
581 | ((t (:foreground "red" :height 10.0))))
582 | '(face-2
583 | ((t (:background "#006000" :foreground "white" :bold t))))))
584 | :eval-after-load nil)
585 |
586 | ;; Expanded to:
587 | (prog1 'feature-name
588 | (custom-set-faces
589 | '(face-1
590 | ((t (:foreground "red" :height 10.0))))
591 | '(face-2
592 | ((t (:background "#006000" :foreground "white" :bold t))))))
593 | #+end_src
594 |
595 | ** =:hook=
596 | This is transformed to =add-hook= sexp.
597 | Each element is =(HOOK . FUNCTION)=.
598 |
599 | #+begin_src emacs-lisp :tangle yes
600 | (mic feature-name
601 | :hook
602 | ;; #' is needed
603 | ((hook1 . #'function1)
604 | (hook2 . #'function2)
605 | ;; `lambda' is allowed (but not recommended)
606 | (hook3 . (lambda (arg) 1))))
607 |
608 | ;; Expanded to:
609 | (mic-core feature-name
610 | :eval
611 | ((add-hook 'hook1 #'function1)
612 | (add-hook 'hook2 #'function2)
613 | (add-hook 'hook3 (lambda (arg) 1)))
614 | :eval-after-load nil)
615 |
616 | ;; Expanded to:
617 | (prog1 'feature-name
618 | (add-hook 'hook1 #'function1)
619 | (add-hook 'hook2 #'function2)
620 | (add-hook 'hook3 (lambda (arg) 1)))
621 | #+end_src
622 |
623 | ** =:package=
624 | This is transformed to =package-install= sexps.
625 | Each arguments are =PKG= used by =package-install=.
626 |
627 | The expandation result is complicated, because it is annoying to fetch package archives many times.
628 |
629 |
630 | #+begin_src emacs-lisp :tangle yes
631 | (mic feature-name
632 | :package
633 | (package-name1
634 | package-name2))
635 |
636 | ;; Expanded to:
637 | (mic-core feature-name
638 | :eval
639 | ;; When package is not installed
640 | ((unless (package-installed-p 'package-name1)
641 | ;; Ensure package is exists in archive
642 | (when (assq 'package-name1 package-archive-contents)
643 | (ignore-errors
644 | (package-install 'package-name1)))
645 | (unless (package-installed-p 'package-name1)
646 | ;; Refresh (fetch) new archive
647 | (package-refresh-contents)
648 | (condition-case _
649 | (package-install 'package-name1)
650 | (error
651 | (warn "Package %s is not found" 'package-name1)))))
652 |
653 | (unless (package-installed-p 'package-name2)
654 | (when (assq 'package-name2 package-archive-contents)
655 | (ignore-errors
656 | (package-install 'package-name2)))
657 | (unless (package-installed-p 'package-name2)
658 | (package-refresh-contents)
659 | (condition-case _
660 | (package-install 'package-name2)
661 | (error
662 | (warn "Package %s is not found" 'package-name2))))))
663 | :eval-after-load nil)
664 |
665 | ;; Expand to:
666 | (prog1 'feature-name
667 | (unless (package-installed-p 'package-name1)
668 | (when (assq 'package-name1 package-archive-contents)
669 | (ignore-errors
670 | (package-install 'package-name1)))
671 | (unless (package-installed-p 'package-name1)
672 | (package-refresh-contents)
673 | (condition-case _
674 | (package-install 'package-name1)
675 | (error
676 | (warn "Package %s is not found" 'package-name1)))))
677 | (unless (package-installed-p 'package-name2)
678 | (when (assq 'package-name2 package-archive-contents)
679 | (ignore-errors
680 | (package-install 'package-name2)))
681 | (unless (package-installed-p 'package-name2)
682 | (package-refresh-contents)
683 | (condition-case _
684 | (package-install 'package-name2)
685 | (error
686 | (warn "Package %s is not found" 'package-name2))))))
687 | #+end_src
688 |
689 | ** =:require=
690 | This is transformed to =require= sexps.
691 | Each element is feature symbol and required on =:eval=.
692 |
693 | #+begin_src emacs-lisp :tangle yes
694 | (mic feature-name
695 | :require
696 | (feat1
697 | feat2))
698 |
699 | ;; Expand to:
700 | (mic-core feature-name
701 | :eval-installation nil
702 | :eval
703 | ((require 'feat1)
704 | (require 'feat2))
705 | :eval-after-load nil)
706 |
707 | ;; Expand to:
708 | (prog1 'feature-name
709 | (require 'feat1)
710 | (require 'feat2))
711 | #+end_src
712 |
713 | ** =:require-after=
714 | This is transformed to =require= sexps in =with-eval-after-load= section.
715 | Each element is alist. =car= of each element is feature symbol which is
716 | used as first argument of =with-eval-after-load=.
717 | =cdr= of each element is list of features required after the =car=.
718 |
719 | This is used when you should require package after another one but
720 | there is no functions to call so =autoload= cannot be used.
721 |
722 | #+begin_src emacs-lisp :tangle yes
723 | (mic feature-name
724 | :require-after
725 | ((feat-after1
726 | . (feat1 feat2))
727 | (feat-after2
728 | feat3
729 | feat4)))
730 |
731 | ;; Expand to:
732 | (mic-core feature-name
733 | :eval-installation nil
734 | :eval
735 | ((with-eval-after-load 'feat-after1
736 | (require 'feat1)
737 | (require 'feat2))
738 | (with-eval-after-load 'feat-after2
739 | (require 'feat3)
740 | (require 'feat4)))
741 | :eval-after-load nil)
742 |
743 | ;; Expand to:
744 | (prog1 'feature-name
745 | (with-eval-after-load 'feat-after1
746 | (require 'feat1)
747 | (require 'feat2))
748 | (with-eval-after-load 'feat-after2
749 | (require 'feat3)
750 | (require 'feat4)))
751 | #+end_src
752 |
753 | * Define your own =mic=
754 | :PROPERTIES:
755 | :CUSTOM_ID: define-your-own-mic
756 | :END:
757 | You do not like =mic= behavior? It is OK. You can define your own =mic=!
758 | There are some ways to define it:
759 | - Use =mic-defmic=
760 | - Use =defmacro=
761 |
762 | ** Define your own =mic= with =mic-defmic=
763 | If you would like to add keywords, or to make some keywords more simple,
764 | you can define =filter= and apply it to =mic= (or =mic-core=, and another =mic=, any parent is allowed).
765 |
766 | *** What is a filter?
767 | The filter recieves one argument, =PLIST= (plist, property list), and returns =RETURNED-PLIST=.
768 | It filters or transforms it into returned plist.
769 | It is better to divide filters by every keyword, because of reusability.
770 |
771 | 1. Each filter recieves 1 argument =PLIST=, which is plist (property list).
772 | 2. Each filter returns =RETURNED-PLIST=, which is plist.
773 | 3. =PLIST= is given by user or filter before.
774 | 4. =PLIST= have feature name =:name= property.
775 | 5. =RETURNED-PLIST= is passed to next filter or parent =mic= (=mic=, =mic-core=, or another).
776 | 6. =RETURNED-PLIST= should have same value of =:name= property.
777 | 7. The property only used by your filter should be removed in =RETURNED-PLIST=.
778 |
779 | Here is example:
780 | #+begin_src emacs-lisp :tangle yes
781 | (defun my-filter-global-set-key-without-quote (plist)
782 | (let ((alist
783 | ;; Get value from your own keyword
784 | (plist-get plist :bind))
785 | sexps)
786 | (setq sexps
787 | ;; Transform each element
788 | (mapcar
789 | (lambda (arg)
790 | (let ((keys (car arg))
791 | (command (cdr arg)))
792 | `(global-set-key (kbd ,keys) #',command)))
793 | alist))
794 | ;; Put sexps to `:eval' arguments
795 | (mic-plist-put-append plist :eval sexps)
796 | ;; Don't forget to delete your own keyword!
797 | ;; When forget it, parent recieves it and may cause unexpected result.
798 | (mic-plist-delete plist :bind)
799 | plist))
800 |
801 | ;; `defmic' defines new `mic' (see "Define mic with mic-defmic" section for more infomation)
802 | (mic-defmic yourmic
803 | mic ; Derived from `mic'
804 | :filters '(my-filter-global-set-key-without-quote))
805 |
806 | ;; Here is `yourmic' expression
807 | (yourmic package-name
808 | ;; New keyword you added by `my-filter-global-set-key-without-quote'
809 | :bind
810 | (("M-a" . beginning-of-defun)
811 | ("M-e" . end-of-defun))
812 | ;; Of course keywords for `mic', which is original of `yourmic', is allowed.
813 | :hook ((after-init-hook . #'ignore)))
814 |
815 | ;; Then first `PLIST' is:
816 | '( :name package-name
817 | :bind (("M-a" . beginning-of-defun)
818 | ("M-e" . end-of-defun))
819 | :hook ((after-init-hook . #'ignore)))
820 |
821 | ;; When you expand the sexp before, the filter you defined is called like:
822 | (my-filter-global-set-key-without-quote
823 | '( :name package-name
824 | :bind (("M-a" . beginning-of-defun)
825 | ("M-e" . end-of-defun))
826 | :hook ((after-init-hook . #'ignore))))
827 |
828 | ;; It returns `RETURNED-PLIST':
829 | '( :name package-name
830 | :hook ((after-init-hook function ignore))
831 | :eval
832 | ((global-set-key (kbd "M-a") #'beginning-of-defun)
833 | (global-set-key (kbd "M-e") #'end-of-defun)))
834 |
835 | ;; The `RETURNED-PLIST' is passed to a next filter if exists.
836 | ;; You use only one filter in definition,
837 | ;; so it is expanded to:
838 | (mic package-name
839 | :hook ((after-init-hook . #'ignore))
840 | :eval
841 | ((global-set-key (kbd "M-a") #'beginning-of-defun)
842 | (global-set-key (kbd "M-e") #'end-of-defun)))
843 | #+end_src
844 |
845 | *** Pre-defined filters
846 | Some pre-defined filter, unused by =mic= definition, are available in =mic-filter.el=.
847 |
848 | **** Filters for package manager
849 | - =mic-filter-ell-get=
850 | - =mic-filter-straight=
851 | - =mic-filter-quelpa=
852 | For more infomation, see docstring of each filter.
853 |
854 | #+begin_src emacs-lisp :tangle yes
855 | ;;; el-get
856 | (mic-defmic mic-with-el-get mic
857 | :filters '(mic-filter-el-get))
858 |
859 | (mic-with-el-get hydra
860 | :el-get ((hydra :repo "abo-abo/hydra" :fetcher github)))
861 |
862 | ;; Expanded to:
863 | (mic hydra
864 | :eval-installation
865 | ((el-get-bundle hydra :repo "abo-abo/hydra" :fetcher github)))
866 | #+end_src
867 |
868 | #+begin_src emacs-lisp :tangle yes
869 | ;;; quelpa
870 | (mic-defmic mic-with-quelpa mic
871 | :filters '(mic-filter-quelpa))
872 |
873 | (mic-with-quelpa hydra
874 | :quelpa ((hydra :repo "abo-abo/hydra" :fetcher github)))
875 |
876 | ;; Expanded to:
877 | (mic hydra
878 | :eval-installation
879 | ((quelpa
880 | '(hydra :repo "abo-abo/hydra" :fetcher github))))
881 | #+end_src
882 |
883 | #+begin_src emacs-lisp :tangle yes
884 | ;;; straight
885 | (mic-defmic mic-with-straight mic
886 | :filters '(mic-filter-straight))
887 |
888 | (mic-with-straight hydra
889 | :straight ((hydra :repo "abo-abo/hydra" :host github)))
890 |
891 | ;; Expanded to:
892 | (mic hydra
893 | :eval-installation
894 | ((straight-use-package
895 | '(hydra :repo "abo-abo/hydra" :host github))))
896 | #+end_src
897 |
898 | **** Key definition
899 | - =mic-filter-define-key-general=, =mic-filter-general-define-key=
900 | - =mic-filter-mykie=
901 | - =mic-filter-hydra=
902 | - =mic-filter-pretty-hydra=, =mic-filter-pretty-hydra+=
903 | - =mic-filter-mode-hydra=
904 |
905 | Here is summaries and examples for these filters.
906 | See a docstring and definition of each filter for more information.
907 |
908 | ***** general.el
909 | [[https://github.com/noctuid/general.el][general.el]] makes key definition more convenient.
910 | There are some filters for integration with it:
911 | - =mic-filter-define-key-general=
912 | - =mic-filter-general-define-key=
913 | The both are expanded to =general-define-key= call.
914 |
915 | =mic-filter-define-key-general=, which uses a =:define-key-general= keyword, is compatible with =:define-key= keyword.
916 | In the other words, the syntax like =((keymap (key . function)...)...)= is allowed but =general-define-key= is used as backend.
917 |
918 | On the other hand, =mic-filter-general-define-key=, which uses =:general-define-key= keyword, uses =general-define-key= syntax.
919 | So you can use =:keymap= or =:prefix= keyword. Each element of the value of =:general-define-key= is directly passed to =general-define-key=.
920 |
921 | #+begin_src emacs-lisp :tangle yes
922 | (mic-defmic mic-with-define-key-general mic
923 | :filters
924 | '(mic-filter-define-key-general))
925 |
926 | (mic-with-define-key-general package-name
927 | :define-key-general
928 | ((keymap1
929 | ("C-d" . #'func1)
930 | ("C-q" . #'func2))
931 | (override
932 | ("C-a" . #'func3)
933 | ("C-e" . #'func4))))
934 |
935 | ;; Expanded to:
936 | (mic package-name
937 | :eval
938 | ((general-define-key :keymaps 'keymap1
939 | "C-d" (function func1)
940 | "C-q" (function func2))
941 | (general-define-key :keymaps 'override
942 | "C-a" (function func3)
943 | "C-e" (function func4))))
944 | #+end_src
945 |
946 | ***** Mykie.el
947 | [[https://github.com/yuutayamada/mykie-el][Mykie.el]] is is multiplexer of key definition.
948 | There is filter for mykie:
949 | - =mic-filter-mykie=
950 |
951 | =mic-filter-mykie=, which uses a =:mykie= keyword, creates =mykie:define-key= sexp.
952 | Each element of the value on =:mykie= keyword is a cons cell like =((keymap (key [:keyword function1] ...)...)...)=.
953 | =car= of each element, which is keymap, and each element of =cdr= of each element of the value is passed to =mykie:define-key=.
954 |
955 | #+begin_src emacs-lisp :tangle yes
956 |
957 | #+end_src
958 |
959 | #+begin_src emacs-lisp :tangle yes
960 | (mic-defmic mic-with-filter-mykie mic
961 | :filters
962 | '(mic-filter-mykie))
963 |
964 | (mic-with-filter-mykie package-name
965 | :mykie
966 | ((global-map
967 | ("C-w" :default hydra-window-resizer/body :region kill-region))))
968 |
969 | ;; Expanded to:
970 | (mic package-name
971 | :eval
972 | ((mykie:define-key global-map "C-w" :default hydra-window-resizer/body :region kill-region)))
973 | #+end_src
974 |
975 | ***** Hydra
976 | [[https://github.com/abo-abo/hydra][Hydra]] makes Emacs bindings stick around.
977 | There is a filter for integration of Hydra:
978 | - =mic-filter-hydra=
979 |
980 | =mic-filter-hydra=, which uses a =:hydra= keyword, creates =defhydra= sexp.
981 | Each element of the value on the =:hydra= keyword is passed to =defhydra= directly.
982 |
983 | #+begin_src emacs-lisp :tangle yes
984 | (mic-defmic mic-with-hydra mic
985 | :filters '(mic-filter-hydra))
986 |
987 | (mic-with-hydra package-name
988 | :hydra
989 | ;; Spacing induces good indent
990 | (( hydra-window-resizer ()
991 | ("p" shrink-window "shrink")
992 | ("n" enlarge-window "enlarge")
993 | ("f" enlarge-window-horizontally "enlarge-horizontally")
994 | ("b" shrink-window-horizontally "shrink-horizontally")
995 | ("" shrink-window)
996 | ("" enlarge-window)
997 | ("" enlarge-window-horizontally)
998 | ("" shrink-window-horizontally)
999 | ("q" nil "quit"))))
1000 |
1001 | ;; Expanded to:
1002 | (mic package-name
1003 | :eval
1004 | ((defhydra hydra-window-resizer nil
1005 | ("p" shrink-window "shrink" :exit nil :cmd-name hydra-window-resizer/shrink-window :column nil)
1006 | ("n" enlarge-window "enlarge")
1007 | ("f" enlarge-window-horizontally "enlarge-horizontally")
1008 | ("b" shrink-window-horizontally "shrink-horizontally")
1009 | ("" shrink-window)
1010 | ("" enlarge-window)
1011 | ("" enlarge-window-horizontally)
1012 | ("" shrink-window-horizontally)
1013 | ("q" nil "quit"))))
1014 | #+end_src
1015 |
1016 | ***** pretty-hydra
1017 | [[https://github.com/jerrypnz/major-mode-hydra.el#pretty-hydra][Pretty Hydra]] defines prettier hydra.
1018 | There is some filters for integration of it:
1019 | - =mic-filter-pretty-hydra=
1020 | - =mic-filter-pretty-hydra+=
1021 |
1022 | =mic-filter-pretty-hydra= uses =:pretty-hydra=, whereas =mic-filter-pretty-hydra+= uses =:pretty-hydra+=.
1023 | Each element is passed to =pretty-hydra-define=, which defines new hydra, or =pretty-hydra-define+=, which appends to existing hydra if exist.
1024 | The both have absolutely same syntax. Each element is passed to each defining macros directly.
1025 |
1026 | #+begin_src emacs-lisp :tangle yes
1027 | (mic-defmic mic-with-pretty-hydra mic
1028 | :filters '(mic-filter-pretty-hydra
1029 | mic-filter-pretty-hydra+))
1030 |
1031 | ;;; `:pretty-hydra'
1032 | (mic-with-pretty-hydra package-name
1033 | :pretty-hydra
1034 | (( hydra-window-resizer ()
1035 | ("Alphabet"
1036 | (("p" shrink-window "shrink")
1037 | ("n" enlarge-window "enlarge")
1038 | ("f" enlarge-window-horizontally "enlarge-horizontally")
1039 | ("b" shrink-window-horizontally "shrink-horizontally"))
1040 | "Arrow"
1041 | (("" shrink-window)
1042 | ("" enlarge-window)
1043 | ("" enlarge-window-horizontally)
1044 | ("" shrink-window-horizontally))
1045 | "Quit"
1046 | ("q" nil "quit")))))
1047 |
1048 | ;; Expanded to:
1049 | (mic package-name
1050 | :eval
1051 | ((pretty-hydra-define hydra-window-resizer nil
1052 | ("Alphabet"
1053 | (("p" shrink-window "shrink")
1054 | ("n" enlarge-window "enlarge")
1055 | ("f" enlarge-window-horizontally "enlarge-horizontally")
1056 | ("b" shrink-window-horizontally "shrink-horizontally"))
1057 | "Arrow"
1058 | (("" shrink-window "shrink-window")
1059 | ("" enlarge-window "enlarge-window")
1060 | ("" enlarge-window-horizontally "enlarge-window-horizontally")
1061 | ("" shrink-window-horizontally "shrink-window-horizontally"))
1062 | "Quit"
1063 | ("q" nil "quit")))))
1064 |
1065 |
1066 | ;;; `:pretty-hydra+'
1067 | (mic-with-pretty-hydra package-name
1068 | :pretty-hydra+
1069 | (( hydra-window-resizer ()
1070 | ("Vim-like"
1071 | (("h" enlarge-window-horizontally "enlarge-horizontally")
1072 | ("j" shrink-window "shrink")
1073 | ("k" enlarge-window "enlarge")
1074 | ("l" shrink-window-horizontally "shrink-horizontally"))))))
1075 |
1076 | ;; Expanded to:
1077 | (mic package-name
1078 | :eval
1079 | ((pretty-hydra-define+ hydra-window-resizer nil
1080 | ("Vim-like"
1081 | (("h" enlarge-window-horizontally "enlarge-horizontally")
1082 | ("j" shrink-window "shrink")
1083 | ("k" enlarge-window "enlarge")
1084 | ("l" shrink-window-horizontally "shrink-horizontally"))))))
1085 | #+end_src
1086 |
1087 | ***** major-mode-hydra
1088 | [[https://github.com/jerrypnz/major-mode-hydra.el#major-mode-hydra][Major Mode Hydra]] defines major-mode specific hydra function, =major-mode-hydra=.
1089 | There is a filter for integration of it:
1090 | - =mic-filter-mode-hydra=
1091 |
1092 | =mic-filter-mode-hydra= uses a =:mode-hydra= keyword.
1093 | Each element of the value of the keyword is passed to =major-mode-hydra-define= directly.
1094 |
1095 | #+begin_src emacs-lisp :tangle yes
1096 | (mic-defmic mic-with-mode-hydra mic
1097 | :filters '(mic-filter-mode-hydra
1098 | mic-filter-mode-hydra+))
1099 |
1100 | ;;; `:mode-hydra'
1101 | (mic-with-mode-hydra package-name
1102 | :mode-hydra
1103 | (( c-mode (:title "C Mode" :quit-key "q")
1104 | ("Alphabet"
1105 | (("p" shrink-window "shrink")
1106 | ("n" enlarge-window "enlarge")
1107 | ("f" enlarge-window-horizontally "enlarge-horizontally")
1108 | ("b" shrink-window-horizontally "shrink-horizontally"))
1109 | "Arrow"
1110 | (("" shrink-window)
1111 | ("" enlarge-window)
1112 | ("" enlarge-window-horizontally)
1113 | ("" shrink-window-horizontally))))))
1114 |
1115 | ;; Expanded to:
1116 | (mic package-name
1117 | :eval
1118 | ((major-mode-hydra-define c-mode
1119 | (:title "C Mode" :quit-key "q")
1120 | ("Alphabet"
1121 | (("p" shrink-window "shrink")
1122 | ("n" enlarge-window "enlarge")
1123 | ("f" enlarge-window-horizontally "enlarge-horizontally")
1124 | ("b" shrink-window-horizontally "shrink-horizontally"))
1125 | "Arrow"
1126 | (("" shrink-window "shrink-window")
1127 | ("" enlarge-window "enlarge-window")
1128 | ("" enlarge-window-horizontally "enlarge-window-horizontally")
1129 | ("" shrink-window-horizontally "shrink-window-horizontally"))))))
1130 |
1131 | ;;; `:mode-hydra+'
1132 | (mic-with-mode-hydra package-name
1133 | :mode-hydra+
1134 | (( c-mode (:title "C Mode" :quit-key "q")
1135 | ("Alphabet"
1136 | (("p" shrink-window "shrink")
1137 | ("n" enlarge-window "enlarge")
1138 | ("f" enlarge-window-horizontally "enlarge-horizontally")
1139 | ("b" shrink-window-horizontally "shrink-horizontally"))
1140 | "Arrow"
1141 | (("" shrink-window)
1142 | ("" enlarge-window)
1143 | ("" enlarge-window-horizontally)
1144 | ("" shrink-window-horizontally))))))
1145 |
1146 | ;; Expanded to:
1147 | (mic package-name :eval
1148 | ((major-mode-hydra-define+ c-mode
1149 | (:title "C Mode" :quit-key "q" :hint nil :color teal :separator "═")
1150 | ("Alphabet"
1151 | (("p" shrink-window "shrink")
1152 | ("n" enlarge-window "enlarge")
1153 | ("f" enlarge-window-horizontally "enlarge-horizontally")
1154 | ("b" shrink-window-horizontally "shrink-horizontally"))
1155 | "Arrow"
1156 | (("" shrink-window "shrink-window")
1157 | ("" enlarge-window "enlarge-window")
1158 | ("" enlarge-window-horizontally "enlarge-window-horizontally")
1159 | ("" shrink-window-horizontally "shrink-window-horizontally"))))))
1160 | #+end_src
1161 |
1162 | **** Alternative of filters
1163 |
1164 | ***** Hook
1165 | - =mic-filter-hook-list= ::
1166 | This is almost same as =mic-filter-hook=, but =car= of each element of the value should be list of hook,
1167 | and the =cdr= should be list of function (should be quoted).
1168 | =:hook-list= is used as keyword.
1169 | - =mic-filter-hook-list-maybe= ::
1170 | This is almost same as =mic-filter-hook=, but =car= of each element of the value should be list of hook or just one hook,
1171 | and the =cdr= should be list of function or just one function (should *NOT* be quoted).
1172 | =:hook-list-maybe= is used as keyword.
1173 | - =mic-filter-hook-quote= ::
1174 | This is almost same as =mic-filter-hook=, but =cdr= of each element of the value should not be quoted.
1175 | =:hook-quote= is used as keyword.
1176 |
1177 | *** Helper for defining a filter
1178 | There are some helpers for defining a filter.
1179 |
1180 | **** Utilities
1181 | Usually, a filter proceeds filtering by 4 steps:
1182 | 1. Get data on a specific keyword in =PLIST=
1183 | 2. Convert data to sexp
1184 | 3. Append the sexp to value on =:eval= in =PLIST=
1185 | 4. Delete the specific keyword from =PLIST=
1186 |
1187 | There are some macros to help step 3. and 4. in =mic-utils.el=.
1188 | - =mic-plist-put-append=, which helps step 3., takes three arguments,
1189 | =PLIST=, =PROP=, which means keyword, and =VAL=.
1190 | It get a value on =PROP= in =PLIST=, and appends =VAL= to the value.
1191 | - =mic-plist-delete=, which helps step 4., takes one obligatory argument =PLIST=,
1192 | and extra arguments =PROPS=.
1193 | It removes =PROPS= keywords from =PLIST= and return it.
1194 |
1195 | **** =deffilter=
1196 | To define a simple filter or to modify an existing filter,
1197 | you can use =mic-deffilter-*= macros in =mic-deffilter.el=.
1198 | See each macro definition and docstring for more information.
1199 |
1200 | - =mic-deffilter-alias= :: Induce alias keyword.
1201 | #+begin_src emacs-lisp :tangle yes
1202 | (mic-deffilter-alias example-filter-alias :alias :origin)
1203 |
1204 | (example-filter-alias '(:alias "Hello"))
1205 | ;; =>
1206 | (:origin "Hello")
1207 | #+end_src
1208 | - =mic-deffilter-const= :: Put constant value on keyword.
1209 | #+begin_src emacs-lisp :tangle yes
1210 | (mic-deffilter-const example-filter-const
1211 | "Optional docstring."
1212 | :eval '((message "Hello")))
1213 |
1214 | ;; Add a :eval keyword when it does not exist.
1215 | (example-filter-const '(:other-keyword "Hi"))
1216 | ;; =>
1217 | (:other-keyword "Hi" :eval ((message "Hello")))
1218 |
1219 | ;; Overwrite when a :eval keyword exists.
1220 | (example-filter-const '(:eval ((message "Good bye")) :other-keyword "Hi"))
1221 | ;; =>
1222 | (:eval ((message "Hello")) :other-keyword "Hi")
1223 | #+end_src
1224 | - =mic-deffilter-const-append= :: Append constant value on keyword.
1225 | #+begin_src emacs-lisp :tangle yes
1226 | (mic-deffilter-const-append example-filter-const-append
1227 | :eval '((message "Hello")))
1228 |
1229 | ;; Same as `mic-deffilter-const' when any :eval keyword does not exist.
1230 | (example-filter-const-append '(:other-keyword "Hi"))
1231 | ;; =>
1232 | (:other-keyword "Hi" :eval ((message "Hello")))
1233 |
1234 | ;; Append the value when the a :eval keyword exists.
1235 | (example-filter-const-append '(:eval ((message "Good bye")) :other-keyword "Hi"))
1236 | ;; =>
1237 | (:eval ((message "Good bye") (message "Hello")) :other-keyword "Hi")
1238 | #+end_src
1239 | - =mic-deffilter-ignore= :: Just remove value on keyword.
1240 | #+begin_src emacs-lisp :tangle yes
1241 | (mic-deffilter-ignore example-filter-ignore
1242 | :ignore-me)
1243 |
1244 | (example-filter-ignore '(:ignore-me "Ignored" :remain-me "Remained"))
1245 | ;; =>
1246 | (:remain-me "Remained")
1247 | #+end_src
1248 | - =mic-deffilter-nonlist-to-list= :: If value is not list, wrap it into list.
1249 | #+begin_src emacs-lisp :tangle yes
1250 | (mic-deffilter-nonlist-to-list example-filter-nonlist-to-list
1251 | :package)
1252 |
1253 | (example-filter-nonlist-to-list '(:package t))
1254 | ;; =>
1255 | (:package (t))
1256 | #+end_src
1257 | - =mic-deffilter-replace-keyword-append= :: From an existing filter, define a new filter which uses another keywords as input and output.
1258 | Value is appended to the keyword for output.
1259 | #+begin_src emacs-lisp :tangle yes
1260 | ;; Original filter: `mic-filter-mykie'
1261 | (mic-filter-mykie '(:mykie ((global-map ("C-a" :default beginning-of-line)))))
1262 | ;; =>
1263 | (:eval ((mykie:define-key global-map "C-a" :default beginning-of-line)))
1264 |
1265 |
1266 | (mic-deffilter-replace-keyword-append example-filter-replace-keyword-append
1267 | mic-filter-mykie
1268 | :mykie-after-load :mykie
1269 | '((:eval . :eval-after-load)))
1270 |
1271 | ;; An input keyword and an output keyword is replaced
1272 | (example-filter-replace-keyword-append '(:mykie-after-load ((global-map ("C-a" :default beginning-of-line)))))
1273 | ;; =>
1274 | (:eval-after-load ((mykie:define-key global-map "C-a" :default beginning-of-line)))
1275 | #+end_src
1276 | - =mic-deffilter-convert-after-load= :: From an existing filter, define a new filter which outputs an =:eval-after-load= keyword instead of =:eval=.
1277 | It is same as =(mic-deffilter-replace-keyword-append name filter old-keyword new-keyword '((:eval . :eval-after-load)))=.
1278 | #+begin_src emacs-lisp :tangle yes
1279 | ;; Original filter: `mic-filter-mykie'
1280 | (mic-filter-mykie '(:mykie ((global-map ("C-a" :default beginning-of-line)))))
1281 | ;; =>
1282 | (:eval ((mykie:define-key global-map "C-a" :default beginning-of-line)))
1283 |
1284 |
1285 | (mic-deffilter-convert-after-load example-filter-convert-after-load
1286 | mic-filter-mykie
1287 | :mykie-after-load :mykie)
1288 |
1289 | ;; An input keyword and an output keyword is replaced
1290 | (example-filter-convert-after-load '(:mykie-after-load ((global-map ("C-a" :default beginning-of-line)))))
1291 | ;; =>
1292 | (:eval-after-load ((mykie:define-key global-map "C-a" :default beginning-of-line)))
1293 | #+end_src
1294 | - =mic-deffilter-t-to-name= :: Replace =t= with feature name in a list keyword.
1295 | #+begin_src emacs-lisp :tangle yes
1296 | (mic-deffilter-t-to-name example-filter-t-to-name
1297 | :replace)
1298 |
1299 | ;; :name keyword is needed in addition to :replace keyword
1300 | (example-filter-t-to-name '(:name feature-name :replace (1 2 3 t 5 6 t)))
1301 | ;; =>
1302 | (:name feature-name :replace (1 2 3 feature-name 5 6 feature-name))
1303 | #+end_src
1304 | - =mic-deffilter-validate= :: Return a recieved plist except that it validates and sieves keyword in the plist
1305 | to confirm the returned plist has no invalid keywords.
1306 | #+begin_src emacs-lisp :tangle yes
1307 | (mic-deffilter-validate example-filter-validate
1308 | :name :key1 :key2)
1309 |
1310 | (example-filter-validate '(:name feature-name :key1 "Hello" :key2 "Hi" :key3 "Bad" :key4 "Sad"))
1311 | ;; =>
1312 | (:name feature-name :key1 "Hello" :key2 "Hi")
1313 | ;; In addition, warnings are displayed like:
1314 | ;; Warning (emacs): 'mic' feature-name: The keyword :key3 is not allowed by filter 'example-filter-validate'
1315 | ;; Warning (emacs): 'mic' feature-name: The keyword :key4 is not allowed by filter 'example-filter-validate'
1316 | #+end_src
1317 |
1318 | *** Define =mic= with =mic-defmic=
1319 | =mic-defmic= recieves arguments: =NAME=, =PANRENT=, optional =DOCSTRING=, keyword argument =FILTERS=.
1320 | =NAME= is your new =mic= macro name. =PARENT= is parent =mic=, which recieves =RETURNED-PLIST= at last.
1321 | =FILTERS= is list of your filters. When your =mic= recieves plist, the plist is filtered by all of your =FILTERS= in order,
1322 | then the plist is passed to =PARENT=.
1323 |
1324 | Here is example:
1325 | #+begin_src emacs-lisp :tangle yes
1326 | ;; Define `mymic'
1327 | (mic-defmic mymic
1328 | ;; Parent is here. You can also use `mic-core'.
1329 | mic
1330 | :filters
1331 | '(my-filter-global-set-key-without-quote
1332 | ;; You can add other filters below
1333 | )
1334 | ;; You can comment out the line below to catch, warn and ignore errors.
1335 | ;; :error-protection? t
1336 | )
1337 |
1338 | ;; Then you can use `mymic' like:
1339 | (mymic simple
1340 | :bind
1341 | (("C-d" . delete-forward-char)
1342 | ("C-x l" . toggle-truncate-lines))
1343 | ;; Of course parent keywords are accepted.
1344 | :custom
1345 | ((kill-whole-line . t)
1346 | (set-mark-command-repeat-pop . t)
1347 | (mark-ring-max . 50)))
1348 |
1349 | ;; Expanded to:
1350 | (mic simple
1351 | :custom
1352 | ((kill-whole-line . t)
1353 | (set-mark-command-repeat-pop . t)
1354 | (mark-ring-max . 50))
1355 | :eval
1356 | ((global-set-key (kbd "C-d") #'delete-forward-char)
1357 | (global-set-key (kbd "C-x l") #'toggle-truncate-lines)))
1358 | #+end_src
1359 |
1360 | When you would like to use =mic-core= as =PARENT=, =mic-filter-core-validate= is useful to validate plist.
1361 | *Please put it tail of =FILTERS= if you use it.*
1362 |
1363 | **** Error protection
1364 | If you want your =mic= to catch, warn and dismiss errors and to continue evaluation, set =:error-protection?= =t=.
1365 | #+begin_src emacs-lisp :tangle yes
1366 | (mic-defmic mymic-with-error-protection
1367 | ;; Parent is here. You can also use `mic-core'.
1368 | mic
1369 | :filters
1370 | '(my-filter-global-set-key-without-quote)
1371 | :error-protection? t)
1372 |
1373 | (mymic-with-error-protection simple
1374 | :bind
1375 | (("C-d" . delete-forward-char)
1376 | ("C-x l" . toggle-truncate-lines))
1377 | ;; Of course parent keywords are accepted.
1378 | :custom
1379 | ((kill-whole-line . t)
1380 | (set-mark-command-repeat-pop . t)
1381 | (mark-ring-max . 50)))
1382 |
1383 | ;; Expanded to:
1384 | (condition-case-unless-debug error ; Catch error
1385 | (mic simple
1386 | :custom
1387 | ((kill-whole-line . t)
1388 | (set-mark-command-repeat-pop . t)
1389 | (mark-ring-max . 50))
1390 | :eval
1391 | ((global-set-key (kbd "C-d") (function delete-forward-char))
1392 | (global-set-key (kbd "C-x l") (function toggle-truncate-lines))))
1393 | ;; Warn caught error but continue evaluation
1394 | (error
1395 | (warn "`%s' %s: evaluation error: %s" 'mymic-with-error-protection 'simple
1396 | (error-message-string error))))
1397 | #+end_src
1398 |
1399 | **** Accept non-plist input
1400 | Like =use-package= and =leaf=, you can define =mic= which accepts non-plist input.
1401 | If you want to do so, you should pass =:inputter= argument to =mic-defmic=.
1402 | =INPUTTER= is a function which takes one argument =INPUT=, and transform it into =PLIST= as returned value.
1403 |
1404 | Simply, you can use =mic-definputter-pseudo-plist= defined in [[file:mic-definputter.el]]
1405 | to define inputter like =use-package= or =leaf=.
1406 | it takes two arguments =NAME= and =LISTIZED-KEYWORDS=. =NAME= is a name of the inputter function,
1407 | and =LISTIZED-KEYWORDS= is list of keyword whose value can be passed multiple times.
1408 | #+begin_src emacs-lisp :tangle yes
1409 | (mic-definputter-pseudo-plist my-inputter
1410 | '(:eval :eval-after-load :define-key))
1411 |
1412 | (mic-defmic mymic-with-inputter mic
1413 | :inputter #'my-inputter)
1414 |
1415 | (mymic-with-inputter feature-name
1416 | :eval
1417 | ;; Like `use-package', you can put multiple sexps after :eval, instead of list of sexp
1418 | (message "Hello")
1419 | (message "Good bye")
1420 |
1421 | :eval-after-load
1422 | (message "Hello, after load")
1423 | (message "Good bye, after load")
1424 |
1425 | ;; Instead, list of sexp is not allowed
1426 | ;; :eval-after-load
1427 | ;; ((message "Hello, after load")
1428 | ;; (message "Good bye, after load"))
1429 |
1430 | :define-key
1431 | (global-map
1432 | ("M-a" . #'beginning-of-defun))
1433 | (esc-map
1434 | ("e" . #'end-of-defun))
1435 |
1436 | ;; Other keyword is not affected by inputter
1437 | :package
1438 | (ivy hydra))
1439 | #+end_src
1440 |
1441 | **** Adopt a parent other than =mic=, =mic-core= and its derivation
1442 | You can use other configuration managers, such as [[https://github.com/jwiegley/use-package][use-package]] and [[https://github.com/conao3/leaf.el][leaf.el]].
1443 | However, filters defined by =mic= output keyword for =mic= family, such as =:eval=, =:eval-after-load=.
1444 | So you should tell =mic-defmic= how to adapt outputs to its parent by =:adapter= option.
1445 | The adapter takes one argument =PLIST=, and returns a list to pass to the parent.
1446 |
1447 | Two adapter are pre-defined:
1448 | - =mic-adapter-use-package= :: Adapter for =use-package=.
1449 | - =mic-adapter-leaf= :: Adapter for =leaf=.
1450 |
1451 | #+begin_src emacs-lisp :tangle yes
1452 | (mic-defmic mic-with-use-package use-package
1453 | :filters '(mic-filter-define-key-with-feature)
1454 | :adapter #'mic-adapter-use-package)
1455 |
1456 | (mic-with-use-package feature-name
1457 | :define-key-with-feature
1458 | ((org
1459 | (org-mode-map
1460 | ("M-a" . #'feature-name-command))))
1461 | ;; You can use `use-package' feature
1462 | :bind
1463 | (("M-a" . beginning-of-defun)
1464 | ("M-e" . end-of-defun)))
1465 |
1466 | ;; Expanded to:
1467 | (use-package feature-name
1468 | :bind
1469 | (("M-a" . beginning-of-defun)
1470 | ("M-e" . end-of-defun))
1471 | ;; :defer is needed to wrap :config section around `eval-after-load'
1472 | :defer t
1473 | :init
1474 | (with-eval-after-load 'org
1475 | (define-key org-mode-map (kbd "M-a") (function feature-name-command))))
1476 | #+end_src
1477 |
1478 |
1479 | ** Define your own =mic= with =defmacro=
1480 | When you read here, you should know =defmacro=.
1481 | You can do anything with =defmacro=. =mic-defmic= is easy way to define your =mic=,
1482 | but may be not enough for you, because of restriction. Then *I RECOMMEND to use =defmacro=*.
1483 | I am looking forward to seeing your =mic= defined by =defmacro=!
1484 |
1485 | * Alternative
1486 | :PROPERTIES:
1487 | :CUSTOM_ID: alternative
1488 | :END:
1489 | There are some alternatives:
1490 | - [[https://github.com/jwiegley/use-package][=use-package=]]
1491 | - [[https://github.com/conao3/leaf.el][=leaf=]]
1492 |
1493 | They are more easy to use, but sometimes have less expressive ability.
1494 | =mic= is more simple and has more expressive ability, but sometimes more redundant.
1495 | It is just your preference.
1496 |
1497 | In addition, they are customizable, while =mic= is not customizable, but re-definable.
1498 | You can define your own =mic= according to your preference, with =mic= help.
1499 | Of course you can define your own =mic= with =use-package= or =leaf= as backend.
1500 |
1501 | * Contribute
1502 | When you think you would like to share your filter or your own =mic=, use GitHub Discussion.
1503 | Of course your =mic= defined by =defmacro=. Any issue is welcome.
1504 |
1505 | * License
1506 | This package is licensed by GPLv3. See [[file:LICENSE][LICENSE]].
1507 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | codecov:
2 | notify:
3 | after_n_builds: 7
4 |
--------------------------------------------------------------------------------
/mic-adapter.el:
--------------------------------------------------------------------------------
1 | ;;; mic-adapter.el --- Adapters for mic -*- lexical-binding: t; -*-
2 |
3 | ;; Copyright (C) 2023 by ROCKTAKEY
4 |
5 | ;; Author: ROCKTAKEY
6 |
7 | ;; This program is free software; you can redistribute it and/or modify
8 | ;; it under the terms of the GNU General Public License as published by
9 | ;; the Free Software Foundation, either version 3 of the License, or
10 | ;; (at your option) any later version.
11 |
12 | ;; This program is distributed in the hope that it will be useful,
13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ;; GNU General Public License for more details.
16 |
17 | ;; You should have received a copy of the GNU General Public License
18 | ;; along with this program. If not, see .
19 |
20 | ;;; Commentary:
21 |
22 | ;; Adapters for mic
23 |
24 | ;;; Code:
25 |
26 | (require 'mic-utils)
27 |
28 | (defun mic-adapter-use-package (plist)
29 | "An adapter from `mic-core'-like input PLIST to `use-package'.
30 | It takes one argument PLIST, and transforms it into `use-package' sexp."
31 | (let ((eval (plist-get plist :eval))
32 | ;; Variable named `eval-after-load' is warned by `package-lint', so use `eal' instead.
33 | (eal (plist-get plist :eval-after-load))
34 | (eval-after-others (plist-get plist :eval-after-others))
35 | (eval-after-others-after-load (plist-get plist :eval-after-others-after-load))
36 | (eval-before-all (plist-get plist :eval-before-all))
37 | (eval-installation (plist-get plist :eval-installation))
38 | (rest-plist plist))
39 | (mic-plist-delete rest-plist
40 | :eval
41 | :eval-after-load
42 | :eval-after-others
43 | :eval-after-others-after-load
44 | :eval-before-all
45 | :eval-installation
46 | :defer)
47 | `(,@rest-plist
48 | :defer ,(if (plist-member plist :defer) (plist-get plist :defer) t)
49 | ,@(when (or eval-before-all eval-installation)
50 | `(:preface
51 | ,@eval-before-all
52 | ,@eval-installation))
53 | ,@(when (or eval eval-after-others)
54 | `(:init
55 | ,@eval
56 | ,@eval-after-others))
57 | ,@(when (or eal eval-after-others-after-load)
58 | `(:config
59 | ,@eal
60 | ,@eval-after-others-after-load)))))
61 |
62 | (defun mic-adapter-leaf (plist)
63 | "An adapter from `mic-core'-like input PLIST to `leaf'.
64 | It takes one argument PLIST, and transforms it into `leaf' sexp."
65 | (let ((eval (plist-get plist :eval))
66 | ;; Variable named `eval-after-load' is warned by `package-lint', so use `eal' instead.
67 | (eal (plist-get plist :eval-after-load))
68 | (eval-after-others (plist-get plist :eval-after-others))
69 | (eval-after-others-after-load (plist-get plist :eval-after-others-after-load))
70 | (eval-before-all (plist-get plist :eval-before-all))
71 | (eval-installation (plist-get plist :eval-installation))
72 | (rest-plist plist))
73 | (mic-plist-delete rest-plist
74 | :eval
75 | :eval-after-load
76 | :eval-after-others
77 | :eval-after-others-after-load
78 | :eval-before-all
79 | :eval-installation)
80 | `(,@rest-plist
81 | ,@(when (or eval-before-all eval-installation)
82 | `(:preface
83 | ,@eval-before-all
84 | ,@eval-installation))
85 | ,@(when (or eval eval-after-others)
86 | `(:init
87 | ,@eval
88 | ,@eval-after-others))
89 | ,@(when (or eal eval-after-others-after-load)
90 | `(
91 | ;; Sometimes :config is not wrapped around `with-eval-after-load',
92 | ;; so use :defer-config instead.
93 | :defer-config
94 | ,@eal
95 | ,@eval-after-others-after-load)))))
96 |
97 | (provide 'mic-adapter)
98 |
99 | ;;; mic-adapter.el ends here
100 |
--------------------------------------------------------------------------------
/mic-deffilter.el:
--------------------------------------------------------------------------------
1 | ;;; mic-deffilter.el --- Definer of filter for mic -*- lexical-binding: t; -*-
2 |
3 | ;; Copyright (C) 2022 ROCKTAKEY
4 |
5 | ;; Author: ROCKTAKEY
6 |
7 | ;; This program is free software; you can redistribute it and/or modify
8 | ;; it under the terms of the GNU General Public License as published by
9 | ;; the Free Software Foundation, either version 3 of the License, or
10 | ;; (at your option) any later version.
11 |
12 | ;; This program is distributed in the hope that it will be useful,
13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ;; GNU General Public License for more details.
16 |
17 | ;; You should have received a copy of the GNU General Public License
18 | ;; along with this program. If not, see .
19 |
20 | ;;; Commentary:
21 |
22 | ;; Definer of filter for mic
23 |
24 | ;;; Code:
25 |
26 | (require 'cl-lib)
27 | (require 'mic-utils)
28 |
29 | ;;;###autoload
30 | (defmacro mic-deffilter-alias (name alias target &optional docstring)
31 | "Define filter function named NAME with document DOCSTRING.
32 | The filter recieves plist and returns plist.
33 | It make alias named ALIAS to TARGET."
34 | (declare (indent defun))
35 | (let ((key (make-symbol "key"))
36 | (value (make-symbol "value"))
37 | (result (make-symbol "result")))
38 | `(defun ,name (plist)
39 | ,(or docstring
40 | (format "Filter for `mic'.
41 | It return PLIST but value on %s is replaced with %s."
42 | alias target))
43 | (let (,result)
44 | (while plist
45 | (let ((,key (pop plist))
46 | (,value (pop plist)))
47 | (when (eq ,key ,alias)
48 | (setq ,key ,target))
49 | (push ,key ,result)
50 | (push ,value ,result)))
51 | (nreverse ,result)))))
52 |
53 | ;;;###autoload
54 | (defmacro mic-deffilter-const (name &optional docstring &rest plist)
55 | "Define filter function named NAME with document DOCSTRING.
56 | The filter recieves plist and returns plist.
57 | It replace value on each property in PLIST with each value in PLIST."
58 | (declare (indent defun))
59 | (unless (stringp docstring)
60 | (push docstring plist)
61 | (setq docstring nil))
62 | `(defun ,name (plist)
63 | ,(or docstring
64 | (format "Filter for `mic'.
65 | It return PLIST but each value on some property below is replaced:
66 | %s" (pp-to-string plist)))
67 | ,@(let (result)
68 | (while plist
69 | (let ((key (pop plist))
70 | (value (pop plist)))
71 | (push `(mic-plist-put plist ,key ,value) result)))
72 | (nreverse result))
73 | plist))
74 |
75 | ;;;###autoload
76 | (defmacro mic-deffilter-const-append (name &optional docstring &rest plist)
77 | "Define filter function named NAME with document DOCSTRING.
78 | The filter recieves plist and returns plist.
79 | It append each value in PLIST on each property to recieved plist."
80 | (declare (indent defun))
81 | (unless (stringp docstring)
82 | (push docstring plist)
83 | (setq docstring nil))
84 | `(defun ,name (plist)
85 | ,(or docstring
86 | (format "Filter for `mic'.
87 | It return PLIST but each value on some property below is appended:
88 | %s" (pp-to-string plist)))
89 | ,@(let (result)
90 | (while plist
91 | (let ((key (pop plist))
92 | (value (pop plist)))
93 | (push `(mic-plist-put-append plist ,key ,value) result)))
94 | (nreverse result))
95 | plist))
96 |
97 | ;;;###autoload
98 | (defmacro mic-deffilter-ignore (name keyword &optional docstring)
99 | "Define filter function named NAME with document DOCSTRING.
100 | The filter recieves plist and returns plist.
101 | If the value on KEYWORD in PLIST exists, remove it."
102 | (declare (indent defun))
103 | `(defun ,name (plist)
104 | ,(or docstring
105 | (format "Filter for `mic'.
106 | If the value on %s in PLIST exists, remove it."
107 | keyword))
108 | (cl-remf plist ,keyword)
109 | plist))
110 |
111 | ;;;###autoload
112 | (defmacro mic-deffilter-nonlist-to-list (name keyword &optional docstring)
113 | "Define filter function named NAME with document DOCSTRING.
114 | The filter recieves plist and returns plist.
115 | If the value on KEYWORD in PLIST is not list NON-LIST,
116 | replace it with `(NON-LIST)'."
117 | (declare (indent defun))
118 | (let ((value (make-symbol "value")))
119 | `(defun ,name (plist)
120 | ,(or docstring
121 | (format "Filter for `mic'.
122 | If the value on %s in PLIST is symbol SYMBOL, replace to '(SYMBOL)."
123 | keyword))
124 | (let ((,value (plist-get plist ,keyword)))
125 | (unless (listp ,value)
126 | (mic-plist-put
127 | plist
128 | ,keyword
129 | (list ,value)))
130 | plist))))
131 |
132 | ;;;###autoload
133 | (defmacro mic-deffilter-replace-keyword-append
134 | (name filter new-keyword old-keyword replacement-alist &optional docstring)
135 | "Define filter function named NAME with document DOCSTRING.
136 | The filter recieves PLIST and returns plist.
137 | The filter conduct same procedure as FILTER, but input and output keyword is
138 | altered.
139 | 1. NEW-KEYWORD is used as an input keyword instead of OLD-KEYWORD.
140 | 2. Output keywords are replaced according to REPLACEMENT-ALIST.
141 | Each `cdr' keyword is replaced with `car' keyword in REPLACEMENT-ALIST."
142 | (declare (indent defun))
143 | `(defun ,name (plist)
144 | ,(or docstring
145 | (format "Filter for `mic'.
146 | Almost same as `%s' but input keyword is `%s' instead of `%s',
147 | and output is replaced according to a alist:
148 | %s
149 | where each `cdr' keyword is replaced with `car'."
150 | filter new-keyword old-keyword replacement-alist))
151 | (let ((inner-output-plist (,filter (list ,old-keyword (plist-get plist ,new-keyword)))))
152 | (mic-plist-replace-keywords inner-output-plist ,replacement-alist)
153 | (while inner-output-plist
154 | (let ((key (pop inner-output-plist))
155 | (value (pop inner-output-plist)))
156 | (mic-plist-put-append plist
157 | key value))))
158 | (mic-plist-delete plist ,new-keyword)))
159 |
160 | ;;;###autoload
161 | (defmacro mic-deffilter-convert-after-load
162 | (name filter new-keyword old-keyword &optional docstring)
163 | "Define filter function named NAME with document DOCSTRING.
164 | This macro makes FILTER `after-load'-ized.
165 |
166 | The filter recieves PLIST and returns plist.
167 | The filter conduct same procedure as FILTER, but input and output keyword is
168 | altered.
169 | 1. NEW-KEYWORD is used as an input keyword instead of OLD-KEYWORD.
170 | 2. `:eval' is replaced with `:eval-after-load' in output."
171 | (declare (indent defun))
172 | `(mic-deffilter-replace-keyword-append
173 | ,name ,filter ,new-keyword ,old-keyword
174 | '((:eval . :eval-after-load))
175 | ,docstring))
176 |
177 | ;;;###autoload
178 | (defmacro mic-deffilter-t-to-name (name keyword &optional docstring)
179 | "Define filter function named NAME with document DOCSTRING.
180 | The filter recieves plist and returns plist.
181 | It replaces element t of the list value on KEYWORD in PLIST
182 | to the value on `:name'."
183 | (declare (indent defun))
184 | `(defun ,name (plist)
185 | ,(or docstring
186 | (format "Filter for `mic'.
187 | It return PLIST but element t of the list value on %s
188 | is replaced to the value on `:name'."
189 | keyword))
190 | (mic-plist-put
191 | plist
192 | ,keyword
193 | (mapcar
194 | (lambda (arg)
195 | (if (eq arg t)
196 | (plist-get plist :name)
197 | arg))
198 | (plist-get plist ,keyword)))))
199 |
200 | ;;;###autoload
201 | (defmacro mic-deffilter-validate (name &optional docstring &rest keywords)
202 | "Define filter function named NAME with document DOCSTRING.
203 | The filter recieves PLIST and return PLIST.
204 | It validates PLIST properties and warn if PLIST has invalid properties.
205 | KEYWORDS is a list of valid properties."
206 | (declare (indent defun))
207 | (unless (stringp docstring)
208 | (push docstring keywords)
209 | (setq docstring nil))
210 | (let ((temp-plist (make-symbol "temp-plist"))
211 | (result (make-symbol "result"))
212 | (key (make-symbol "key"))
213 | (value (make-symbol "value")))
214 | `(defun ,name (plist)
215 | ,(or docstring
216 | (format "Filter for `mic'.
217 | It validates PLIST properties and warn if PLIST has invalid properties.
218 | The valid properties are:
219 | %s" (mapconcat
220 | (lambda (arg)
221 | (concat "`" (symbol-name arg) "'"))
222 | keywords
223 | "\n")))
224 | (let ((,temp-plist plist)
225 | ,result)
226 | (while ,temp-plist
227 | (let ((,key (pop ,temp-plist))
228 | (,value (pop ,temp-plist)))
229 | (if (not (memq ,key (append ',keywords '(:name))))
230 | (warn "`mic' %s: The keyword %s is not allowed by filter `%s'" (plist-get plist :name) ,key ',name)
231 | (push ,key ,result)
232 | (push ,value ,result))))
233 | (nreverse ,result)))))
234 |
235 | (provide 'mic-deffilter)
236 | ;;; mic-deffilter.el ends here
237 |
--------------------------------------------------------------------------------
/mic-definputter.el:
--------------------------------------------------------------------------------
1 | ;;; mic-definputter.el --- Inputter for `mic' -*- lexical-binding: t; -*-
2 |
3 | ;; Copyright (C) 2023 by ROCKTAKEY
4 |
5 | ;; This program is free software; you can redistribute it and/or modify
6 | ;; it under the terms of the GNU General Public License as published by
7 | ;; the Free Software Foundation, either version 3 of the License, or
8 | ;; (at your option) any later version.
9 |
10 | ;; This program is distributed in the hope that it will be useful,
11 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | ;; GNU General Public License for more details.
14 |
15 | ;; You should have received a copy of the GNU General Public License
16 | ;; along with this program. If not, see .
17 |
18 | ;;; Commentary:
19 |
20 | ;; Inputter for `mic'
21 |
22 | ;;; Code:
23 |
24 | (require 'mic-utils)
25 |
26 | (defmacro mic-definputter-pseudo-plist (name listized-keywords &optional docstring)
27 | "Define an inputter, named NAME and documented by DOCSTRING, for `mic-defmic'.
28 | Here is description for an inputter defined by this.
29 | ---
30 | Any keywords in LISTIZED-KEYWORDS are allowed to put muptiple value,
31 | whereas the other keywords are not allowed.
32 |
33 | For example, when LISTIZED-KEYWORDS is (:a :b), lists below are allowed:
34 | (:a 1
35 | :b 4 5 6
36 | :c 7)
37 | (:a 1 2
38 | :c 7)
39 | (:a 1
40 | :b 2 4
41 | :a 5
42 | :c 9)
43 | but lists below are not allowed:
44 | (:a 1 2
45 | :c 3 4) ; Because :c is not allowed to be multiple.
46 | (:a 1
47 | :c 2
48 | :b 4
49 | :c 4) ; Because :c is not allowed to be multiple.
50 |
51 | This function returns plist whose value on keyword in LISTIZED-KEYWORDS is
52 | put together into one list.
53 |
54 | For example, when LISTIZED-KEYWORDS is (:a :b),
55 | lists below are transformed like:
56 | (:a 1
57 | :b 4 5 6
58 | :c 7)
59 | ;; =>
60 | (:a (1)
61 | :b (4 5 6)
62 | :c 7)"
63 | (declare (indent defun))
64 | (let ((listized-keywords (eval listized-keywords)))
65 | `(defun ,name (pseudo-plist)
66 | ,(or docstring
67 | (format "Inputter for `mic'.
68 | It takes one argument PSEUDO-PLIST and return normalized plist.
69 | Keywords listed below are allowed to put muptiple value,
70 | whereas the other keywords are not allowed:
71 | %s
72 | For more information, see `mic-definputter-pseudo-plist'."
73 | (mapconcat
74 | (lambda (arg)
75 | (concat "- `" (pp-to-string arg) "'"))
76 | listized-keywords
77 | "\n")))
78 | (let (redundant-plist)
79 | (let (key)
80 | (while pseudo-plist
81 | (let ((now (pop pseudo-plist)))
82 | (if (keywordp now)
83 | (setq key now)
84 | (mic-plist-put-append redundant-plist key (list now))))))
85 | (let (normalized-plist)
86 | (while redundant-plist
87 | (let ((key (pop redundant-plist))
88 | (value (pop redundant-plist)))
89 | (mic-plist-put normalized-plist key
90 | (if (seq-some
91 | (apply-partially #'eq key)
92 | ',listized-keywords)
93 | value
94 | (when (cdr value)
95 | (error "`%s': keyword `%s' is not allowed to be passed multiple times"
96 | ',name key))
97 | (car value)))))
98 | normalized-plist)))))
99 |
100 | (provide 'mic-definputter)
101 |
102 | ;;; mic-definputter.el ends here
103 |
--------------------------------------------------------------------------------
/mic-filter.el:
--------------------------------------------------------------------------------
1 | ;;; mic-filter.el --- Filter definitions for mic -*- lexical-binding: t; -*-
2 |
3 | ;; Copyright (C) 2022 ROCKTAKEY
4 |
5 | ;; Author: ROCKTAKEY
6 |
7 | ;; This program is free software; you can redistribute it and/or modify
8 | ;; it under the terms of the GNU General Public License as published by
9 | ;; the Free Software Foundation, either version 3 of the License, or
10 | ;; (at your option) any later version.
11 |
12 | ;; This program is distributed in the hope that it will be useful,
13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ;; GNU General Public License for more details.
16 |
17 | ;; You should have received a copy of the GNU General Public License
18 | ;; along with this program. If not, see .
19 |
20 | ;;; Commentary:
21 |
22 | ;; Filter definitions for `mic'.
23 |
24 | ;;; Code:
25 | (require 'mic)
26 |
27 | (defsubst mic-make-sexp-hook-list (alist)
28 | "Create `add-hook' sexp from ALIST.
29 | `car' of each element is list of HOOK, and `cdr' is list of FUNCTION.
30 | FUNCTION should be quoted."
31 | (mapcan
32 | (lambda (arg)
33 | (let ((hooks (car arg))
34 | (functions (cdr arg)))
35 | (mapcan
36 | (lambda (hook)
37 | (mapcar
38 | (lambda (function)
39 | `(add-hook ',hook ,function))
40 | functions))
41 | hooks)))
42 | alist))
43 |
44 | (defun mic-filter-hook-list (plist)
45 | "Append sexp from value of :hook-list to value of :eval on PLIST.
46 | Sexp is generated by `mic-make-sexp-hook-list'."
47 | (mic-plist-put-append plist :eval
48 | (mic-make-sexp-hook-list
49 | (plist-get plist :hook-list)))
50 | (mic-plist-delete plist :hook-list))
51 |
52 | (defsubst mic-make-sexp-hook-list-maybe (alist)
53 | "Create `add-hook' sexp from ALIST.
54 | `car' of each element is list of HOOK or just a HOOK,
55 | and `cdr' is list of FUNCTION or just a FUNCTION.
56 | FUNCTION should not be quoted."
57 | (mic-make-sexp-hook-list
58 | (mapcar
59 | (lambda (arg)
60 | (let ((hooks (car arg))
61 | (functions (cdr arg)))
62 | (unless (listp hooks)
63 | (setq hooks (list hooks)))
64 | (unless (listp functions)
65 | (setq functions (list functions)))
66 | (setq functions
67 | (mapcar
68 | (lambda (function)
69 | `#',function)
70 | functions))
71 | (cons hooks functions)))
72 | alist)))
73 |
74 | (defun mic-filter-hook-list-maybe (plist)
75 | "Append sexp from value of :hook-list-maybe to value of :eval on PLIST.
76 | Sexp is generated by `mic-make-sexp-hook-list-maybe'."
77 | (mic-plist-put-append plist :eval
78 | (mic-make-sexp-hook-list-maybe
79 | (plist-get plist :hook-list-maybe)))
80 | (mic-plist-delete plist :hook-list-maybe))
81 |
82 | (defsubst mic-make-sexp-hook-quote (alist)
83 | "Create `add-hook' sexp from ALIST.
84 | `car' of each element is HOOK, and `cdr' is FUNCTION.
85 | FUNCTION should not be quoted."
86 | (mapcar
87 | (lambda (arg)
88 | `(add-hook ',(car arg) #',(cdr arg)))
89 | alist))
90 |
91 | (defun mic-filter-hook-quote (plist)
92 | "Append sexp from value of :hook to value of :eval on PLIST.
93 | Sexp is generated by `mic-make-sexp-hook-quote'."
94 | (mic-plist-put-append plist :eval
95 | (mic-make-sexp-hook-quote
96 | (plist-get plist :hook-quote)))
97 | (mic-plist-delete plist :hook-quote)
98 | plist)
99 |
100 |
101 |
102 | (defun mic-filter-el-get (plist)
103 | "Create `el-get-bundle' sexp from PLIST and append.
104 | It is appended to value of `:eval-installation'.
105 | This filter use `:el-get' keyword."
106 | (mic-plist-put-append
107 | plist :eval-installation
108 | (mapcar
109 | (lambda (arg)
110 | `(el-get-bundle ,@(if (listp arg) arg (list arg))))
111 | (plist-get plist :el-get)))
112 | (mic-plist-delete plist :el-get))
113 |
114 | (defun mic-filter-quelpa (plist)
115 | "Create `quelpa' sexp from PLIST and append.
116 | It is appended to value of `:eval-installation'.
117 | This filter use `:quelpa' keyword."
118 | (mic-plist-put-append
119 | plist :eval-installation
120 | (mapcar
121 | (lambda (arg)
122 | `(quelpa ',arg))
123 | (plist-get plist :quelpa)))
124 | (mic-plist-delete plist :quelpa))
125 |
126 | (defun mic-filter-straight (plist)
127 | "Create `straight-use-package' sexp from PLIST and append.
128 | It is appended to value of `:eval-installation'.
129 | This filter use `:straight' keyword."
130 | (mic-plist-put-append
131 | plist :eval-installation
132 | (mapcar
133 | (lambda (arg)
134 | `(straight-use-package ',arg))
135 | (plist-get plist :straight)))
136 | (mic-plist-delete plist :straight))
137 |
138 |
139 |
140 | (defsubst mic-make-sexp-define-key-general (alist)
141 | "Create `general-define-key' sexp from ALIST.
142 | `car' of each element is keymap, and `cdr' is list whose element is
143 | \(KEY-STRING . COMMAND)."
144 | (mapcar
145 | (lambda (keymap-binds-alist)
146 | (let ((keymap (car keymap-binds-alist))
147 | (binds (cdr keymap-binds-alist)))
148 | `(general-define-key
149 | :keymaps ',keymap
150 | ,@(mapcan
151 | (lambda (bind)
152 | (let ((key (car bind))
153 | (command (cdr bind)))
154 | `(,key ,command)))
155 | binds))))
156 | alist))
157 |
158 | (defun mic-filter-define-key-general (plist)
159 | "Append sexp from value of :general-define-key to value of :eval on PLIST.
160 | Sexp is generated by `mic-make-sexp-define-key-general'."
161 | (mic-plist-put-append plist :eval
162 | (mic-make-sexp-define-key-general
163 | (plist-get plist :define-key-general)))
164 | (mic-plist-delete plist :define-key-general)
165 | plist)
166 |
167 | (defsubst mic-make-sexp-general-define-key (list)
168 | "Create `general-define-key' sexp from LIST.
169 | Each list element of LIST is passed to `general-define-key' as arguments."
170 | (mapcar
171 | (lambda (arg)
172 | `(general-define-key
173 | ,@arg))
174 | list))
175 |
176 | (defun mic-filter-general-define-key (plist)
177 | "Append sexp from value of :general-define-key to value of :eval on PLIST.
178 | Sexp is generated by `mic-make-sexp-general-define-key'."
179 | (mic-plist-put-append plist :eval
180 | (mic-make-sexp-general-define-key
181 | (plist-get plist :general-define-key)))
182 | (mic-plist-delete plist :general-define-key)
183 | plist)
184 |
185 |
186 |
187 | (defun mic-filter-hydra (plist)
188 | "Create `defhydra' sexp from PLIST and append to value of `:eval'.
189 | This filter use `:hydra' keyword."
190 | (mic-plist-put-append
191 | plist :eval
192 | (mapcar
193 | (lambda (arg)
194 | `(defhydra ,@arg))
195 | (plist-get plist :hydra)))
196 | (mic-plist-delete plist :hydra))
197 |
198 | (defun mic-filter-mode-hydra (plist)
199 | "Create `major-mode-hydra-define' sexp from PLIST and append to value of `:eval'.
200 | This filter use `:mode-hydra' keyword."
201 | (mic-plist-put-append
202 | plist :eval
203 | (mapcar
204 | (lambda (arg)
205 | `(major-mode-hydra-define ,@arg))
206 | (plist-get plist :mode-hydra)))
207 | (mic-plist-delete plist :mode-hydra))
208 |
209 | (defun mic-filter-mode-hydra+ (plist)
210 | "Make `major-mode-hydra-define+' sexp from PLIST and append to value of `:eval'.
211 | This filter use `:mode-hydra+' keyword."
212 | (mic-plist-put-append
213 | plist :eval
214 | (mapcar
215 | (lambda (arg)
216 | `(major-mode-hydra-define+ ,@arg))
217 | (plist-get plist :mode-hydra+)))
218 | (mic-plist-delete plist :mode-hydra+))
219 |
220 | (defun mic-filter-mykie (plist)
221 | "Create `mykie:define-key' sexp from PLIST and append to value of `:eval'.
222 | This filter use `:mykie' keyword."
223 | (mic-plist-put-append
224 | plist :eval
225 | (cl-mapcan
226 | (lambda (keymap-binds-alist)
227 | (let ((keymap (car keymap-binds-alist))
228 | (binds (cdr keymap-binds-alist)))
229 | (mapcar
230 | (lambda (bind)
231 | (let ((key (car bind))
232 | (args (cdr bind)))
233 | `(mykie:define-key ,keymap ,key ,@args)))
234 | binds)))
235 | (plist-get plist :mykie)))
236 | (mic-plist-delete plist :mykie))
237 |
238 | (defun mic-filter-pretty-hydra (plist)
239 | "Create `pretty-hydra-define' sexp from PLIST and append to value of `:eval'.
240 | This filter use `:pretty-hydra' keyword."
241 | (mic-plist-put-append
242 | plist :eval
243 | (mapcar
244 | (lambda (arg)
245 | `(pretty-hydra-define ,@arg))
246 | (plist-get plist :pretty-hydra)))
247 | (mic-plist-delete plist :pretty-hydra))
248 |
249 | (defun mic-filter-pretty-hydra+ (plist)
250 | "Create `pretty-hydra-define+' sexp from PLIST and append to value of `:eval'.
251 | This filter use `:pretty-hydra+' keyword."
252 | (mic-plist-put-append
253 | plist :eval
254 | (mapcar
255 | (lambda (arg)
256 | `(pretty-hydra-define+ ,@arg))
257 | (plist-get plist :pretty-hydra+)))
258 | (mic-plist-delete plist :pretty-hydra+))
259 |
260 | (provide 'mic-filter)
261 | ;;; mic-filter.el ends here
262 |
--------------------------------------------------------------------------------
/mic-utils.el:
--------------------------------------------------------------------------------
1 | ;;; mic-utils.el --- Utility for mic -*- lexical-binding: t; -*-
2 |
3 | ;; Copyright (C) 2022 ROCKTAKEY
4 |
5 | ;; Author: ROCKTAKEY
6 |
7 | ;; This program is free software; you can redistribute it and/or modify
8 | ;; it under the terms of the GNU General Public License as published by
9 | ;; the Free Software Foundation, either version 3 of the License, or
10 | ;; (at your option) any later version.
11 |
12 | ;; This program is distributed in the hope that it will be useful,
13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ;; GNU General Public License for more details.
16 |
17 | ;; You should have received a copy of the GNU General Public License
18 | ;; along with this program. If not, see .
19 |
20 | ;;; Commentary:
21 |
22 | ;; Utility for mic
23 |
24 | ;;; Code:
25 |
26 | (require 'cl-lib)
27 |
28 | ;;;###autoload
29 | (defmacro mic-plist-put (plist prop val)
30 | "Same as `plist-put', but fine when PLIST is nil.
31 | Change value in PLIST of PROP to VAL."
32 | `(setq ,plist
33 | (if ,plist
34 | (plist-put ,plist ,prop ,val)
35 | (list ,prop ,val))))
36 |
37 | ;;;###autoload
38 | (defmacro mic-plist-put-append (plist prop val)
39 | "Append VAL to value in PLIST of PROP."
40 | `(when ,val
41 | (setq ,plist
42 | (if ,plist
43 | (plist-put ,plist ,prop (append (plist-get ,plist ,prop) ,val))
44 | (list ,prop (append (plist-get ,plist ,prop) ,val))))))
45 |
46 | ;;;###autoload
47 | (defmacro mic-plist-delete (plist &rest props)
48 | "Delete PROPS and their values from PLIST."
49 | (let ((original-plist (cl-gensym "plist"))
50 | (result (cl-gensym "result"))
51 | (key (cl-gensym "key"))
52 | (value (cl-gensym "value")))
53 | `(let ((,original-plist ,plist)
54 | ,result)
55 | (while ,original-plist
56 | (let ((,key (pop ,original-plist))
57 | (,value (pop ,original-plist)))
58 | (unless (memq ,key ',props)
59 | (push ,key ,result)
60 | (push ,value ,result))))
61 | (setq ,plist (nreverse ,result)))))
62 |
63 | ;;;###autoload
64 | (defun mic-plist-replace-keywords (plist replacement-alist)
65 | "Replace each keyword in PLIST.
66 | Each element of REPLACEMENT-ALIST is (FROM . TO),
67 | where keyword FROM is replaced with keyword TO."
68 | (mapc
69 | (lambda (cons)
70 | (let ((from (car cons))
71 | (to (cdr cons)))
72 | (if-let ((value (plist-member plist from)))
73 | (setcar value to))))
74 | replacement-alist)
75 | plist)
76 |
77 | (provide 'mic-utils)
78 | ;;; mic-utils.el ends here
79 |
--------------------------------------------------------------------------------
/test/mic-adapter-test.el:
--------------------------------------------------------------------------------
1 | ;;; mic-adapter-test.el --- test for mic-adapter
2 |
3 | ;; Copyright (C) 2023 ROCKTAKEY
4 |
5 | ;; Author: ROCKTAKEY
6 | ;; Keywords:
7 |
8 | ;; Version: 0.0.0
9 | ;; Package-Requires:
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 | ;;
26 |
27 | ;;; Code:
28 |
29 | (require 'ert)
30 |
31 | (require 'undercover)
32 | (undercover "*.el"
33 | (:report-format 'codecov)
34 | (:report-file "coverage-final.json")
35 | (:send-report nil))
36 |
37 | (require 'mic-adapter)
38 |
39 | (ert-deftest mic-adapter-use-package ()
40 | (should
41 | (equal
42 | (mic-adapter-use-package '( :eval ((message "eval"))
43 | :eval-after-load ((message "eval-after-load"))
44 | :eval-after-others ((message "eval-after-others"))
45 | :eval-after-others-after-load ((message "eval-after-others-after-load"))
46 | :eval-before-all ((message "eval-before-all"))
47 | :eval-installation ((message "eval-installation"))))
48 | '( :defer t
49 | :preface
50 | (message "eval-before-all")
51 | (message "eval-installation")
52 | :init
53 | (message "eval")
54 | (message "eval-after-others")
55 | :config
56 | (message "eval-after-load")
57 | (message "eval-after-others-after-load"))))
58 |
59 | (should
60 | (equal
61 | (mic-adapter-use-package '( :eval ((message "eval"))
62 | :eval-after-load ((message "eval-after-load"))
63 | :eval-after-others ((message "eval-after-others"))
64 | :eval-after-others-after-load ((message "eval-after-others-after-load"))
65 | :eval-before-all ((message "eval-before-all"))
66 | :eval-installation ((message "eval-installation"))
67 | :defer nil))
68 | '( :defer nil
69 | :preface
70 | (message "eval-before-all")
71 | (message "eval-installation")
72 | :init
73 | (message "eval")
74 | (message "eval-after-others")
75 | :config
76 | (message "eval-after-load")
77 | (message "eval-after-others-after-load"))))
78 |
79 | (should
80 | (equal
81 | (mic-adapter-use-package '( :eval ((message "eval"))
82 | :eval-after-load ((message "eval-after-load"))
83 | :eval-after-others ((message "eval-after-others"))
84 | :eval-after-others-after-load ((message "eval-after-others-after-load"))
85 | :eval-before-all ((message "eval-before-all"))
86 | :eval-installation ((message "eval-installation"))
87 | :bind (("M-a" . beginning-of-defun))))
88 | '( :bind (("M-a" . beginning-of-defun))
89 | :defer t
90 | :preface
91 | (message "eval-before-all")
92 | (message "eval-installation")
93 | :init
94 | (message "eval")
95 | (message "eval-after-others")
96 | :config
97 | (message "eval-after-load")
98 | (message "eval-after-others-after-load")))))
99 |
100 | (ert-deftest mic-adapter-leaf ()
101 | (should
102 | (equal
103 | (mic-adapter-leaf '( :eval ((message "eval"))
104 | :eval-after-load ((message "eval-after-load"))
105 | :eval-after-others ((message "eval-after-others"))
106 | :eval-after-others-after-load ((message "eval-after-others-after-load"))
107 | :eval-before-all ((message "eval-before-all"))
108 | :eval-installation ((message "eval-installation"))))
109 | '( :preface
110 | (message "eval-before-all")
111 | (message "eval-installation")
112 | :init
113 | (message "eval")
114 | (message "eval-after-others")
115 | :defer-config
116 | (message "eval-after-load")
117 | (message "eval-after-others-after-load"))))
118 |
119 | (should
120 | (equal
121 | (mic-adapter-leaf '( :eval ((message "eval"))
122 | :eval-after-load ((message "eval-after-load"))
123 | :eval-after-others ((message "eval-after-others"))
124 | :eval-after-others-after-load ((message "eval-after-others-after-load"))
125 | :eval-before-all ((message "eval-before-all"))
126 | :eval-installation ((message "eval-installation"))
127 | :bind (("M-a" . beginning-of-defun))))
128 | '( :bind (("M-a" . beginning-of-defun))
129 | :preface
130 | (message "eval-before-all")
131 | (message "eval-installation")
132 | :init
133 | (message "eval")
134 | (message "eval-after-others")
135 | :defer-config
136 | (message "eval-after-load")
137 | (message "eval-after-others-after-load")))))
138 |
139 |
140 |
141 | (provide 'mic-adapter-test)
142 | ;;; mic-adapter-test.el ends here
143 |
--------------------------------------------------------------------------------
/test/mic-deffilter-test.el:
--------------------------------------------------------------------------------
1 | ;;; mic-deffilter-test.el --- test for mic-deffilter
2 |
3 | ;; Copyright (C) 2022 ROCKTAKEY
4 |
5 | ;; Author: ROCKTAKEY
6 | ;; Keywords:
7 |
8 | ;; Version: 0.0.0
9 | ;; Package-Requires:
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 | ;;
26 |
27 | ;;; Code:
28 |
29 | (require 'ert)
30 | (require 'cl-lib)
31 |
32 | (require 'undercover)
33 | (undercover "*.el"
34 | (:report-format 'codecov)
35 | (:report-file "coverage-final.json")
36 | (:send-report nil))
37 |
38 | (require 'mic-deffilter)
39 |
40 | (mic-deffilter-alias mic-test-mic-deffilter-alias
41 | :foo :baz)
42 |
43 | (ert-deftest mic-deffilter-alias ()
44 | (let* ((init '(:foo 1 :bar 2))
45 | (result (mic-test-mic-deffilter-alias init)))
46 | (should (equal (plist-get result :foo) nil))
47 | (should (equal (plist-get result :baz) 1))
48 | (should (equal (plist-get result :bar) 2))))
49 |
50 | (mic-deffilter-const mic-test-mic-deffilter-const
51 | :foo t
52 | :bar '(2 4))
53 |
54 | (ert-deftest mic-deffilter-const ()
55 | (let* ((init '(:foo 1 :bar 2))
56 | (result (mic-test-mic-deffilter-const init)))
57 | (should (equal (plist-get result :foo) t))
58 | (should (equal (plist-get result :bar) '(2 4)))))
59 |
60 | (mic-deffilter-const-append mic-test-mic-deffilter-const-append
61 | :foo '(t)
62 | :bar '(3 4))
63 |
64 | (ert-deftest mic-deffilter-const-append ()
65 | (let* ((init '(:foo (1) :bar (2)))
66 | (result (mic-test-mic-deffilter-const-append init)))
67 | (should (equal (plist-get result :foo) '(1 t)))
68 | (should (equal (plist-get result :bar) '(2 3 4)))))
69 |
70 | (mic-deffilter-ignore mic-test-mic-deffilter-ignore
71 | :foo)
72 |
73 | (ert-deftest mic-deffilter-ignore ()
74 | (let* ((init '(:foo (1) :bar (2)))
75 | (result (mic-test-mic-deffilter-ignore init)))
76 | (should (equal result '(:bar (2)))))
77 |
78 | (let* ((init '(:bar (2) :foo (1)))
79 | (result (mic-test-mic-deffilter-ignore init)))
80 | (should (equal result '(:bar (2))))))
81 |
82 | (mic-deffilter-nonlist-to-list mic-test-mic-deffilter-nonlist-to-list :foo)
83 |
84 | (ert-deftest mic-deffilter-nonlist-to-list ()
85 | (let* ((init '(:foo (1 t x) :bar (4)))
86 | (result (mic-test-mic-deffilter-nonlist-to-list init)))
87 | (should (equal (plist-get result :foo) '(1 t x)))
88 | (should (equal (plist-get result :bar) '(4))))
89 |
90 | (let* ((init '(:foo a :bar (4)))
91 | (result (mic-test-mic-deffilter-nonlist-to-list init)))
92 | (should (equal (plist-get result :foo) '(a)))
93 | (should (equal (plist-get result :bar) '(4)))))
94 |
95 | (mic-deffilter-replace-keyword-append mic-test-mic-deffilter-replace-keyword-append
96 | (lambda (plist)
97 | (list :eval (plist-get plist :foo)))
98 | :foo-after-load :foo
99 | '((:eval . :eval-after-load)))
100 |
101 | (ert-deftest mic-deffilter-replace-keyword-append ()
102 | (let* ((init '(:foo-after-load ((message "Hello")) :hoge 2))
103 | (result (mic-test-mic-deffilter-replace-keyword-append init)))
104 | (should-not (plist-get result :foo-after-load))
105 | (should-not (plist-get result :eval))
106 | (should (equal (plist-get result :eval-after-load) '((message "Hello"))))
107 | (should (equal (plist-get result :hoge) 2))))
108 |
109 | (mic-deffilter-convert-after-load mic-test-mic-deffilter-convert-after-load
110 | (lambda (plist)
111 | (list :eval (plist-get plist :foo)))
112 | :foo-after-load :foo)
113 |
114 | (ert-deftest mic-deffilter-convert-after-load ()
115 | (let* ((init '(:foo-after-load ((message "Hello")) :hoge 2))
116 | (result (mic-test-mic-deffilter-convert-after-load init)))
117 | (should-not (plist-get result :foo-after-load))
118 | (should-not (plist-get result :eval))
119 | (should (equal (plist-get result :eval-after-load) '((message "Hello"))))
120 | (should (equal (plist-get result :hoge) 2))))
121 |
122 | (mic-deffilter-t-to-name mic-test-mic-deffilter-t-to-name :foo)
123 |
124 | (ert-deftest mic-deffilter-t-to-name ()
125 | (let* ((init '(:name name :foo (1 t x) :bar (4)))
126 | (result (mic-test-mic-deffilter-t-to-name init)))
127 | (should (equal (plist-get result :foo) '(1 name x)))
128 | (should (equal (plist-get result :bar) '(4)))))
129 |
130 | (mic-deffilter-validate mic-test-mic-deffilter-validate
131 | :foo :bar)
132 |
133 | (ert-deftest mic-deffilter-validate ()
134 | (let* ((init '(:foo t :bar 2))
135 | (result (mic-test-mic-deffilter-validate init)))
136 | (should (equal (plist-get result :foo) t))
137 | (should (equal (plist-get result :bar) 2)))
138 |
139 | (let* ((init '(:foo 1 :hoge 2)))
140 | (cl-letf (((symbol-function 'warn) (lambda (&rest arg) (error "Error"))))
141 | (should-error (mic-test-mic-deffilter-validate init)))))
142 |
143 | (provide 'mic-deffilter-test)
144 | ;;; mic-deffilter-test.el ends here
145 |
--------------------------------------------------------------------------------
/test/mic-definputter-test.el:
--------------------------------------------------------------------------------
1 | ;;; mic-definputter-test.el --- test for mic-definputter
2 |
3 | ;; Copyright (C) 2023 ROCKTAKEY
4 |
5 | ;; Author: ROCKTAKEY
6 | ;; Keywords:
7 |
8 | ;; Version: 0.0.0
9 | ;; Package-Requires:
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 | ;;
26 |
27 | ;;; Code:
28 |
29 | (require 'ert)
30 |
31 | (require 'undercover)
32 | (undercover "*.el"
33 | (:report-format 'codecov)
34 | (:report-file "coverage-final.json")
35 | (:send-report nil))
36 |
37 | (require 'mic-definputter)
38 |
39 | (mic-definputter-pseudo-plist inputter-eval
40 | '(:eval :eval-after-load))
41 |
42 | (ert-deftest mic-definputter-pseudo-plist ()
43 | (should
44 | (equal
45 | (inputter-eval '(:eval
46 | (message "Hello")
47 | (message "Hi")
48 | (message "Good bye")
49 | :eval-after-load
50 | (message "Good afternoon")
51 | (message "Good evening")
52 | :require (cl-lib)))
53 |
54 | '(:eval
55 | ((message "Hello")
56 | (message "Hi")
57 | (message "Good bye"))
58 | :eval-after-load
59 | ((message "Good afternoon")
60 | (message "Good evening"))
61 | :require (cl-lib)))))
62 |
63 | (provide 'mic-definputter-test)
64 | ;;; mic-definputter-test.el ends here
65 |
--------------------------------------------------------------------------------
/test/mic-filter-test.el:
--------------------------------------------------------------------------------
1 | ;;; mic-filter-test.el --- test for mic-filter
2 |
3 | ;; Copyright (C) 2022 ROCKTAKEY
4 |
5 | ;; Author: ROCKTAKEY
6 | ;; Keywords:
7 |
8 | ;; Version: 0.0.0
9 | ;; Package-Requires:
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 | ;;
26 |
27 | ;;; Code:
28 |
29 | (require 'ert)
30 |
31 | (require 'undercover)
32 | (undercover "*.el"
33 | (:report-format 'codecov)
34 | (:report-file "coverage-final.json")
35 | (:send-report nil))
36 |
37 | (require 'mic-filter)
38 |
39 | (ert-deftest mic-filter-hook-list ()
40 | (should (equal
41 | (mic-filter-hook-list
42 | '(:hook-list (((after-init-hook before-init-hook) . (#'func1 #'func2))
43 | ((switch-buffer-hook lsp-mode-hook) . ((lambda () 1) #'func3)))))
44 | '(:eval ((add-hook 'after-init-hook #'func1)
45 | (add-hook 'after-init-hook #'func2)
46 | (add-hook 'before-init-hook #'func1)
47 | (add-hook 'before-init-hook #'func2)
48 | (add-hook 'switch-buffer-hook (lambda () 1))
49 | (add-hook 'switch-buffer-hook #'func3)
50 | (add-hook 'lsp-mode-hook (lambda () 1))
51 | (add-hook 'lsp-mode-hook #'func3))))))
52 |
53 | (ert-deftest mic-filter-hook-list-maybe ()
54 | (should (equal
55 | (mic-filter-hook-list-maybe
56 | '(:hook-list-maybe (((after-init-hook before-init-hook) . (func1 func2))
57 | ((switch-buffer-hook lsp-mode-hook) . func3))))
58 | '(:eval ((add-hook 'after-init-hook #'func1)
59 | (add-hook 'after-init-hook #'func2)
60 | (add-hook 'before-init-hook #'func1)
61 | (add-hook 'before-init-hook #'func2)
62 | (add-hook 'switch-buffer-hook #'func3)
63 | (add-hook 'lsp-mode-hook #'func3))))))
64 |
65 | (ert-deftest mic-filter-hook-quote ()
66 | (should (equal
67 | (mic-filter-hook-quote
68 | '(:hook-quote ((after-init-hook . func1)
69 | (switch-buffer-hook . (lambda () 1)))))
70 | '(:eval
71 | ((add-hook 'after-init-hook #'func1)
72 | (add-hook 'switch-buffer-hook #'(lambda nil 1)))))))
73 |
74 |
75 |
76 | (ert-deftest mic-filter-straight ()
77 | (should (equal (mic-filter-straight '(:straight (x y (emacs-ja
78 | :host github
79 | :repo "ayatakesi/ayatakesi.github.io"
80 | :files ("emacs/26.1/emacs-ja.info")))))
81 | '(:eval-installation ((straight-use-package 'x)
82 | (straight-use-package 'y)
83 | (straight-use-package
84 | '(emacs-ja :host github :repo "ayatakesi/ayatakesi.github.io"
85 | :files ("emacs/26.1/emacs-ja.info")))))))
86 | (should (eq (plist-get (mic-filter-straight '( :straight (x y (emacs-ja
87 | :host github
88 | :repo "ayatakesi/ayatakesi.github.io"
89 | :files ("emacs/26.1/emacs-ja.info")))
90 | :foo bar))
91 | :foo)
92 | 'bar)))
93 |
94 | (ert-deftest mic-filter-el-get ()
95 | (should (equal (mic-filter-el-get
96 | '(:el-get (x y
97 | (zenburn-theme
98 | :url "https://raw.githubusercontent.com/bbatsov/zenburn-emacs/master/zenburn-theme.el"
99 | (load-theme 'zenburn t)))))
100 | '(:eval-installation ((el-get-bundle x)
101 | (el-get-bundle y)
102 | (el-get-bundle
103 | zenburn-theme
104 | :url "https://raw.githubusercontent.com/bbatsov/zenburn-emacs/master/zenburn-theme.el"
105 | (load-theme 'zenburn t))))))
106 | (should (eq (plist-get (mic-filter-el-get
107 | '( :el-get (x y
108 | (zenburn-theme
109 | :url "https://raw.githubusercontent.com/bbatsov/zenburn-emacs/master/zenburn-theme.el"
110 | (load-theme 'zenburn t)))
111 | :foo bar))
112 | :foo)
113 | 'bar)))
114 |
115 | (ert-deftest mic-filter-quelpa ()
116 | (should (equal (mic-filter-quelpa '(:quelpa (package-name
117 | (hydra :repo "abo-abo/hydra" :fetcher github))))
118 | '(:eval-installation ((quelpa 'package-name)
119 | (quelpa '(hydra :repo "abo-abo/hydra" :fetcher github)))))))
120 |
121 |
122 |
123 | (ert-deftest mic-filter-define-key-general ()
124 | (should (equal (mic-filter-define-key-general
125 | '(:define-key-general
126 | ((keymap1
127 | ("C-d" . #'func1)
128 | ("C-q" . #'func2))
129 | (override
130 | ("C-a" . #'func3)
131 | ("C-e" . #'func4)))))
132 | '(:eval
133 | ((general-define-key
134 | :keymaps 'keymap1
135 | "C-d" #'func1
136 | "C-q" #'func2)
137 | (general-define-key
138 | :keymaps 'override
139 | "C-a" #'func3
140 | "C-e" #'func4))))))
141 |
142 | (ert-deftest mic-filter-general-define-key ()
143 | (should (equal (mic-filter-general-define-key
144 | '(:general-define-key
145 | (( :keymaps 'keymap1
146 | "C-d" #'func1
147 | "C-q" #'func2)
148 | ( :keymaps 'override
149 | "C-a" #'func3
150 | "C-e" #'func4))))
151 | '(:eval
152 | ((general-define-key
153 | :keymaps 'keymap1
154 | "C-d" #'func1
155 | "C-q" #'func2)
156 | (general-define-key
157 | :keymaps 'override
158 | "C-a" #'func3
159 | "C-e" #'func4))))))
160 |
161 |
162 |
163 | (ert-deftest mic-filter-hydra ()
164 | (should (equal (mic-filter-hydra
165 | '(:hydra ((hydra-window-resizer
166 | nil
167 | ("p" shrink-window "shrink")
168 | ("n" enlarge-window "enlarge")
169 | ("f" enlarge-window-horizontally "enlarge-horizontally")
170 | ("b" shrink-window-horizontally "shrink-horizontally")
171 | ;; ("k" shrink-window)
172 | ;; ("j" enlarge-window)
173 | ;; ("l" enlarge-window-horizontally)
174 | ;; ("h" shrink-window-horizontally)
175 | ("" shrink-window)
176 | ("" enlarge-window)
177 | ("" enlarge-window-horizontally)
178 | ("" shrink-window-horizontally)
179 | ("q" nil "quit")))))
180 | '(:eval
181 | ((defhydra hydra-window-resizer nil
182 | ("p" shrink-window "shrink")
183 | ("n" enlarge-window "enlarge")
184 | ("f" enlarge-window-horizontally "enlarge-horizontally")
185 | ("b" shrink-window-horizontally "shrink-horizontally")
186 | ("" shrink-window)
187 | ("" enlarge-window)
188 | ("" enlarge-window-horizontally)
189 | ("" shrink-window-horizontally)
190 | ("q" nil "quit")))))))
191 |
192 | (ert-deftest mic-filter-mode-hydra ()
193 | (should (equal (mic-filter-mode-hydra
194 | '(:mode-hydra
195 | (( c-mode (:title "C Mode" :quit-key "q")
196 | ("Alphabet"
197 | (("p" shrink-window "shrink")
198 | ("n" enlarge-window "enlarge")
199 | ("f" enlarge-window-horizontally "enlarge-horizontally")
200 | ("b" shrink-window-horizontally "shrink-horizontally"))
201 | "Arrow"
202 | (("" shrink-window)
203 | ("" enlarge-window)
204 | ("" enlarge-window-horizontally)
205 | ("" shrink-window-horizontally)))))))
206 | '(:eval
207 | ((major-mode-hydra-define c-mode (:title "C Mode" :quit-key "q")
208 | ("Alphabet"
209 | (("p" shrink-window "shrink")
210 | ("n" enlarge-window "enlarge")
211 | ("f" enlarge-window-horizontally "enlarge-horizontally")
212 | ("b" shrink-window-horizontally "shrink-horizontally"))
213 | "Arrow"
214 | (("" shrink-window)
215 | ("" enlarge-window)
216 | ("" enlarge-window-horizontally)
217 | ("" shrink-window-horizontally)))))))))
218 |
219 | (ert-deftest mic-filter-mode-hydra+ ()
220 | (should (equal (mic-filter-mode-hydra+
221 | '(:mode-hydra+
222 | (( c-mode (:title "C Mode" :quit-key "q")
223 | ("Alphabet"
224 | (("p" shrink-window "shrink")
225 | ("n" enlarge-window "enlarge")
226 | ("f" enlarge-window-horizontally "enlarge-horizontally")
227 | ("b" shrink-window-horizontally "shrink-horizontally"))
228 | "Arrow"
229 | (("" shrink-window)
230 | ("" enlarge-window)
231 | ("" enlarge-window-horizontally)
232 | ("" shrink-window-horizontally)))))))
233 | '(:eval
234 | ((major-mode-hydra-define+ c-mode (:title "C Mode" :quit-key "q")
235 | ("Alphabet"
236 | (("p" shrink-window "shrink")
237 | ("n" enlarge-window "enlarge")
238 | ("f" enlarge-window-horizontally "enlarge-horizontally")
239 | ("b" shrink-window-horizontally "shrink-horizontally"))
240 | "Arrow"
241 | (("" shrink-window)
242 | ("" enlarge-window)
243 | ("" enlarge-window-horizontally)
244 | ("" shrink-window-horizontally)))))))))
245 |
246 | (ert-deftest mic-filter-mykie ()
247 | (should (equal (mic-filter-mykie
248 | '(:mykie ((global-map ("C-w" :default hydra-window-resizer/body :region kill-region)))))
249 | '(:eval ((mykie:define-key global-map "C-w"
250 | :default hydra-window-resizer/body
251 | :region kill-region))))))
252 |
253 | (ert-deftest mic-filter-pretty-hydra ()
254 | (should (equal (mic-filter-pretty-hydra
255 | '(:pretty-hydra
256 | ((hydra-window-resizer
257 | nil
258 | ("Alphabet"
259 | (("p" shrink-window "shrink")
260 | ("n" enlarge-window "enlarge")
261 | ("f" enlarge-window-horizontally "enlarge-horizontally")
262 | ("b" shrink-window-horizontally "shrink-horizontally"))
263 | "Arrow"
264 | (("" shrink-window)
265 | ("" enlarge-window)
266 | ("" enlarge-window-horizontally)
267 | ("" shrink-window-horizontally))
268 | "Quit"
269 | ("q" nil "quit"))))))
270 | '(:eval
271 | ((pretty-hydra-define hydra-window-resizer nil
272 | ("Alphabet"
273 | (("p" shrink-window "shrink")
274 | ("n" enlarge-window "enlarge")
275 | ("f" enlarge-window-horizontally "enlarge-horizontally")
276 | ("b" shrink-window-horizontally "shrink-horizontally"))
277 | "Arrow"
278 | (("" shrink-window)
279 | ("" enlarge-window)
280 | ("" enlarge-window-horizontally)
281 | ("" shrink-window-horizontally))
282 | "Quit"
283 | ("q" nil "quit"))))))))
284 |
285 | (ert-deftest mic-filter-pretty-hydra+ ()
286 | (should (equal (mic-filter-pretty-hydra+
287 | '(:pretty-hydra+
288 | ((hydra-window-resizer
289 | nil
290 | ("Alphabet"
291 | (("p" shrink-window "shrink")
292 | ("n" enlarge-window "enlarge")
293 | ("f" enlarge-window-horizontally "enlarge-horizontally")
294 | ("b" shrink-window-horizontally "shrink-horizontally"))
295 | "Arrow"
296 | (("" shrink-window)
297 | ("" enlarge-window)
298 | ("" enlarge-window-horizontally)
299 | ("" shrink-window-horizontally))
300 | "Quit"
301 | ("q" nil "quit"))))))
302 | '(:eval
303 | ((pretty-hydra-define+ hydra-window-resizer nil
304 | ("Alphabet"
305 | (("p" shrink-window "shrink")
306 | ("n" enlarge-window "enlarge")
307 | ("f" enlarge-window-horizontally "enlarge-horizontally")
308 | ("b" shrink-window-horizontally "shrink-horizontally"))
309 | "Arrow"
310 | (("" shrink-window)
311 | ("" enlarge-window)
312 | ("" enlarge-window-horizontally)
313 | ("" shrink-window-horizontally))
314 | "Quit"
315 | ("q" nil "quit"))))))))
316 |
317 | (provide 'mic-filter-test)
318 | ;;; mic-filter-test.el ends here
319 |
--------------------------------------------------------------------------------
/test/mic-test.el:
--------------------------------------------------------------------------------
1 | ;;; mic-test.el --- Test for mic
2 |
3 | ;; Copyright (C) 2022 ROCKTAKEY
4 |
5 | ;; Author: ROCKTAKEY
6 |
7 | ;; This program is free software; you can redistribute it and/or modify
8 | ;; it under the terms of the GNU General Public License as published by
9 | ;; the Free Software Foundation, either version 3 of the License, or
10 | ;; (at your option) any later version.
11 |
12 | ;; This program is distributed in the hope that it will be useful,
13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ;; GNU General Public License for more details.
16 |
17 | ;; You should have received a copy of the GNU General Public License
18 | ;; along with this program. If not, see .
19 |
20 | ;;; Commentary:
21 |
22 | ;; Test for mic
23 |
24 | ;;; Code:
25 |
26 | (require 'ert)
27 |
28 | (require 'undercover)
29 | (undercover "*.el"
30 | (:report-format 'codecov)
31 | (:report-file "coverage-final.json")
32 | (:send-report nil))
33 |
34 | (require 'mic)
35 | (require 'mic-utils)
36 |
37 | (defmacro mic-ert-macroexpand-1 (name &rest args)
38 | "Define test named NAME.
39 | The test compare macro expandation of `car' of each element of ARGS with `cdr' of it.
40 | The test defined by this expands macro once."
41 | (declare (indent defun))
42 | `(ert-deftest ,name ()
43 | ,@(mapcar
44 | (lambda (arg)
45 | `(should (equal (macroexpand-1 ',(car arg))
46 | ',(cdr arg))))
47 | args)))
48 |
49 | (defmacro mic-ert-macroexpand-2 (name &rest args)
50 | "Define test named NAME.
51 | The test compare macro expandation of `car' of each element of ARGS with `cdr' of it.
52 | The test defined by this expands macro twice."
53 | (declare (indent defun))
54 | `(ert-deftest ,name ()
55 | ,@(mapcar
56 | (lambda (arg)
57 | `(should (equal (macroexpand-1 (macroexpand-1 ',(car arg)))
58 | ',(cdr arg))))
59 | args)))
60 |
61 |
62 |
63 | (mic-ert-macroexpand-2 mic-autoload-interactive
64 | ((mic feature-name
65 | :autoload-interactive
66 | (find-file
67 | write-file))
68 | . (prog1 'feature-name
69 | (autoload 'find-file "feature-name" nil t)
70 | (autoload 'write-file "feature-name" nil t))))
71 |
72 | (mic-ert-macroexpand-2 mic-autoload-noninteractive
73 | ((mic feature-name
74 | :autoload-noninteractive
75 | (cl-map
76 | cl-mapcar))
77 | . (prog1 'feature-name
78 | (autoload 'cl-map "feature-name")
79 | (autoload 'cl-mapcar "feature-name"))))
80 |
81 | (mic-ert-macroexpand-2 mic-auto-mode
82 | ((mic feature-name
83 | :auto-mode
84 | (("\\.html?\\'" . web-mode)
85 | ("\\.css\\'" . web-mode)))
86 | . (prog1 'feature-name
87 | (add-to-list 'auto-mode-alist '("\\.html?\\'" . web-mode))
88 | (add-to-list 'auto-mode-alist '("\\.css\\'" . web-mode)))))
89 |
90 | (mic-ert-macroexpand-2 mic-custom
91 | ((mic feature-name
92 | :custom
93 | ((a . 1)
94 | (b . (+ 1 2))))
95 | . (prog1 'feature-name
96 | (customize-set-variable 'a 1)
97 | (customize-set-variable 'b
98 | (+ 1 2)))))
99 |
100 | (mic-ert-macroexpand-2 mic-custom-after-load
101 | ((mic feature-name
102 | :custom-after-load
103 | ((a . 1)
104 | (b . (+ 1 2))))
105 | . (prog1 'feature-name
106 | (with-eval-after-load 'feature-name
107 | (customize-set-variable 'a 1)
108 | (customize-set-variable 'b
109 | (+ 1 2))))))
110 |
111 | (mic-ert-macroexpand-2 mic-declare-function
112 | ((mic feature-name
113 | :declare-function
114 | (find-file
115 | write-file))
116 | . (prog1 'feature-name
117 | (declare-function find-file "ext:feature-name")
118 | (declare-function write-file "ext:feature-name"))))
119 |
120 | (mic-ert-macroexpand-2 mic-define-key
121 | ((mic feature-name
122 | :define-key
123 | ((global-map
124 | ("C-t" . #'other-window)
125 | ("C-n" . #'next-window))
126 | (prog-mode-map
127 | ("M-a" . #'beginning-of-buffer)
128 | ("M-e" . #'end-of-buffer))))
129 | . (prog1 'feature-name
130 | (define-key global-map (kbd "C-t") #'other-window)
131 | (define-key global-map (kbd "C-n") #'next-window)
132 | (define-key prog-mode-map (kbd "M-a") #'beginning-of-buffer)
133 | (define-key prog-mode-map (kbd "M-e") #'end-of-buffer))))
134 |
135 | (mic-ert-macroexpand-2 mic-define-key-after-load
136 | ((mic feature-name
137 | :define-key-after-load
138 | ((c-mode-map
139 | ("C-t" . #'other-window)
140 | ("C-n" . #'next-window))
141 | (c++-mode-map
142 | ("M-a" . #'beginning-of-buffer)
143 | ("M-e" . #'end-of-buffer))))
144 | . (prog1 'feature-name
145 | (with-eval-after-load 'feature-name
146 | (define-key c-mode-map (kbd "C-t") #'other-window)
147 | (define-key c-mode-map (kbd "C-n") #'next-window)
148 | (define-key c++-mode-map (kbd "M-a") #'beginning-of-buffer)
149 | (define-key c++-mode-map (kbd "M-e") #'end-of-buffer)))))
150 |
151 | (mic-ert-macroexpand-2 mic-define-key-with-feature
152 | ((mic feature-name
153 | :define-key-with-feature
154 | ((cc-mode
155 | (c-mode-map
156 | ("C-t" . #'other-window)
157 | ("C-n" . #'next-window))
158 | (c++-mode-map
159 | ("M-a" . #'beginning-of-buffer)
160 | ("M-e" . #'end-of-buffer)))
161 | (python
162 | (python-mode-map
163 | ("C-t" . #'python-check)))))
164 | . (prog1 'feature-name
165 | (with-eval-after-load 'cc-mode
166 | (define-key c-mode-map (kbd "C-t") #'other-window)
167 | (define-key c-mode-map (kbd "C-n") #'next-window)
168 | (define-key c++-mode-map (kbd "M-a") #'beginning-of-buffer)
169 | (define-key c++-mode-map (kbd "M-e") #'end-of-buffer))
170 | (with-eval-after-load 'python
171 | (define-key python-mode-map (kbd "C-t") #'python-check)))))
172 |
173 | (mic-ert-macroexpand-2 mic-defvar-noninitial
174 | ((mic feature-name
175 | :defvar-noninitial
176 | (skk-jisyo
177 | skk-use-azik))
178 | . (prog1 'feature-name
179 | (defvar skk-jisyo)
180 | (defvar skk-use-azik))))
181 |
182 | (mic-ert-macroexpand-2 mic-eval
183 | ((mic feature-name
184 | :eval
185 | ((message "Hello")
186 | (message "World")))
187 | . (prog1 'feature-name
188 | (message "Hello")
189 | (message "World"))))
190 |
191 | (mic-ert-macroexpand-2 mic-eval-after-load
192 | ((mic feature-name
193 | :eval-after-load
194 | ((message "Hello")
195 | (message "World")))
196 | . (prog1 'feature-name
197 | (with-eval-after-load 'feature-name
198 | (message "Hello")
199 | (message "World")))))
200 |
201 | (mic-ert-macroexpand-2 mic-eval-after-others
202 | ((mic feature-name
203 | :custom
204 | ((skk-jisyo . "~/skk-jisyo"))
205 | :eval
206 | ((message "before")
207 | (message "custom"))
208 | :eval-after-others
209 | ((message "after")
210 | (message "custom")))
211 | . (prog1 'feature-name
212 | (message "before")
213 | (message "custom")
214 | (customize-set-variable
215 | 'skk-jisyo
216 | "~/skk-jisyo")
217 | (message "after")
218 | (message "custom"))))
219 |
220 | (mic-ert-macroexpand-2 mic-eval-after-others-after-load
221 | ((mic feature-name
222 | :custom-after-load
223 | ((skk-jisyo . "~/skk-jisyo"))
224 | :eval-after-load
225 | ((message "before")
226 | (message "custom"))
227 | :eval-after-others-after-load
228 | ((message "after")
229 | (message "custom")))
230 | . (prog1 'feature-name
231 | (with-eval-after-load 'feature-name
232 | (message "before")
233 | (message "custom")
234 | (customize-set-variable
235 | 'skk-jisyo
236 | "~/skk-jisyo")
237 | (message "after")
238 | (message "custom")))))
239 |
240 | (mic-ert-macroexpand-2 mic-eval-before-all
241 | ((mic feature-name
242 | :custom
243 | ((skk-jisyo . "~/skk-jisyo"))
244 | :eval
245 | ((message "before")
246 | (message "custom"))
247 | :eval-before-all
248 | ((message "before")
249 | (message "all")))
250 | . (prog1 'feature-name
251 | (message "before")
252 | (message "all")
253 | (message "before")
254 | (message "custom")
255 | (customize-set-variable
256 | 'skk-jisyo
257 | "~/skk-jisyo"))))
258 |
259 | (mic-ert-macroexpand-2 mic-face
260 | ((mic feature-name
261 | :face
262 | ((aw-leading-char-face
263 | . ((t (:foreground "red" :height 10.0))))
264 | (aw-mode-line-face
265 | . ((t (:background "#006000" :foreground "white" :bold t))))))
266 | . (prog1 'feature-name
267 | (custom-set-faces
268 | '(aw-leading-char-face
269 | ((t (:foreground "red" :height 10.0))))
270 | '(aw-mode-line-face
271 | ((t (:background "#006000" :foreground "white" :bold t))))))))
272 |
273 | (mic-ert-macroexpand-2 mic-hook
274 | ((mic feature-name
275 | :hook
276 | ((after-init-hook . #'ignore)
277 | (prog-mode-hook . (lambda ()))))
278 | . (prog1 'feature-name
279 | (add-hook 'after-init-hook #'ignore)
280 | (add-hook 'prog-mode-hook (lambda ())))))
281 |
282 | (mic-ert-macroexpand-2 mic-package
283 | ((mic feature-name
284 | :package
285 | (package-1
286 | package-2))
287 | . (prog1 'feature-name
288 | (unless (package-installed-p 'package-1)
289 | (when (assq 'package-1 package-archive-contents)
290 | (ignore-errors
291 | (package-install 'package-1)))
292 | (unless (package-installed-p 'package-1)
293 | (package-refresh-contents)
294 | (condition-case _
295 | (package-install 'package-1)
296 | (error
297 | (warn "Package %s is not found" 'package-1)))))
298 | (unless (package-installed-p 'package-2)
299 | (when (assq 'package-2 package-archive-contents)
300 | (ignore-errors
301 | (package-install 'package-2)))
302 | (unless (package-installed-p 'package-2)
303 | (package-refresh-contents)
304 | (condition-case _
305 | (package-install 'package-2)
306 | (error
307 | (warn "Package %s is not found" 'package-2))))))))
308 |
309 | (mic-ert-macroexpand-2 mic-require
310 | ((mic feature-name
311 | :require
312 | (feat1
313 | feat2))
314 | . (prog1 'feature-name
315 | (require 'feat1)
316 | (require 'feat2))))
317 |
318 | (mic-ert-macroexpand-2 mic-require-after
319 | ((mic feature-name
320 | :require-after
321 | ((feat-after1
322 | . (feat1 feat2))
323 | (feat-after2
324 | feat3
325 | feat4)))
326 | . (prog1 'feature-name
327 | (with-eval-after-load 'feat-after1
328 | (require 'feat1)
329 | (require 'feat2))
330 | (with-eval-after-load 'feat-after2
331 | (require 'feat3)
332 | (require 'feat4)))))
333 |
334 |
335 |
336 | (mic-ert-macroexpand-1 mic-apply-filter
337 | ((mic-apply-filter plist name-var
338 | filter1
339 | filter2
340 | filter3)
341 | . (progn
342 | (mic-plist-put plist :name name-var)
343 | (setq plist
344 | (thread-last plist filter1 filter2 filter3))
345 | (mic-plist-delete plist :name))))
346 |
347 | (mic-ert-macroexpand-1 mic-defmic-macroexpand-1
348 | ((mic-defmic macro-name parent-name
349 | "docstring"
350 | :filters '(filter1 filter2))
351 | . (defmacro macro-name (feature-name &rest input)
352 | "docstring"
353 | (declare (indent defun))
354 | (let ((plist (identity input)))
355 | (mic-apply-filter plist feature-name
356 | filter1 filter2)
357 | (backquote
358 | (parent-name ,feature-name ,@(identity plist))))))
359 | ((mic-defmic macro-name parent-name
360 | :filters '(filter1 filter2))
361 | . (defmacro macro-name (feature-name &rest input)
362 | "`mic' alternative defined by `mic-defmic'.
363 | Configure about FEATURE-NAME by INPUT.
364 |
365 | Information:
366 | - Filters:
367 | - `filter1'
368 | - `filter2'
369 | - Parent: `parent-name'
370 | - Error protection: nil
371 | - Adapter: `identity'
372 | - Inputter: `identity'
373 |
374 | For more information, see `mic-defmic'."
375 | (declare (indent defun))
376 | (let ((plist (identity input)))
377 | (mic-apply-filter plist feature-name filter1 filter2)
378 | (backquote
379 | (parent-name ,feature-name ,@(identity plist)))))))
380 |
381 |
382 |
383 | (mic-deffilter-const-append mic-test-filter-const-1
384 | :foo '(1)
385 | :bar '(2 3))
386 |
387 | (mic-deffilter-const-append mic-test-filter-const-2
388 | :foo '(4 5)
389 | :baz '(6 7))
390 |
391 | (mic-defmic mic-test-mic-defmic-filters parent-name
392 | :filters '(mic-test-filter-const-1 mic-test-filter-const-2))
393 |
394 | (mic-ert-macroexpand-1 mic-defmic-filters
395 | ((mic-test-mic-defmic-filters feature-name
396 | :foo (123)
397 | :bar (456))
398 | . (parent-name feature-name
399 | :foo
400 | (123 1 4 5)
401 | :bar
402 | (456 2 3)
403 | :baz
404 | (6 7))))
405 |
406 | (mic-defmic mic-test-mic-defmic-error-protection parent-name
407 | :filters '(mic-test-filter-const-1 mic-test-filter-const-2)
408 | :error-protection? t)
409 |
410 | (ert-deftest mic-defmic-error-protection ()
411 | (should
412 | (pcase (macroexpand-1
413 | '(mic-test-mic-defmic-error-protection feature-name
414 | :foo (123)
415 | :bar (456)))
416 | (`(condition-case-unless-debug ,error
417 | (parent-name feature-name :foo
418 | (123 1 4 5)
419 | :bar
420 | (456 2 3)
421 | :baz
422 | (6 7))
423 | (error
424 | (warn "`%s' %s: evaluation error: %s" 'mic-test-mic-defmic-error-protection 'feature-name
425 | (error-message-string ,error))))
426 | t)
427 | (_ nil))))
428 |
429 | (mic-defmic mic-test-mic-defmic-adapter parent-name
430 | :filters '(mic-test-filter-const-1 mic-test-filter-const-2)
431 | :adapter
432 | (lambda (plist)
433 | "Replace :foo with :hoge in PLIST.
434 | Then, duplicate value on :bar."
435 | (let (result)
436 | (while plist
437 | (let ((key (pop plist))
438 | (value (pop plist)))
439 | (when (eq key :foo)
440 | (setq key :hoge))
441 | (push key result)
442 | (push value result)
443 | (when (eq key :bar)
444 | (push value result))))
445 | (nreverse result))))
446 |
447 | (mic-ert-macroexpand-1 mic-defmic-adapter
448 | ((mic-test-mic-defmic-adapter feature-name
449 | :foo (123)
450 | :bar (456))
451 | . (parent-name feature-name
452 | :hoge
453 | (123 1 4 5)
454 | :bar
455 | (456 2 3)
456 | (456 2 3)
457 | :baz
458 | (6 7))))
459 |
460 | (mic-defmic mic-test-mic-defmic-inputter parent-name
461 | :filters '(mic-test-filter-const-1 mic-test-filter-const-2)
462 | :inputter
463 | (lambda (psudo-plist)
464 | "Take PSUDO-PLIST.
465 | :foo is allowed to be append."
466 | (let (result key)
467 | (while psudo-plist
468 | (let ((now (pop psudo-plist)))
469 | (if (keywordp now)
470 | (setq key now)
471 | (if (eq key :foo)
472 | (mic-plist-put-append result key (list now))
473 | (mic-plist-put result key now)))))
474 | result)))
475 |
476 | (mic-ert-macroexpand-1 mic-defmic-inputter
477 | ((mic-test-mic-defmic-inputter feature-name
478 | :foo 123
479 | :bar (456)
480 | :foo 789 101)
481 | . (parent-name feature-name
482 | :foo
483 | (123 789 101 1 4 5)
484 | :bar
485 | (456 2 3)
486 | :baz
487 | (6 7))))
488 |
489 | (provide 'mic-test)
490 | ;;; mic-test.el ends here
491 |
--------------------------------------------------------------------------------
/test/mic-utils-test.el:
--------------------------------------------------------------------------------
1 | ;;; mic-utils-test.el --- test for mic-utils
2 |
3 | ;; Copyright (C) 2022 ROCKTAKEY
4 |
5 | ;; Author: ROCKTAKEY
6 | ;; Keywords:
7 |
8 | ;; Version: 0.0.0
9 | ;; Package-Requires:
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 | ;;
26 |
27 | ;;; Code:
28 |
29 | (require 'ert)
30 |
31 | (require 'undercover)
32 | (undercover "*.el"
33 | (:report-format 'codecov)
34 | (:report-file "coverage-final.json")
35 | (:send-report nil))
36 |
37 | (require 'mic-utils)
38 |
39 | (ert-deftest mic-plist-put ()
40 | (let ((plist '(:foo 1 :bar 2)))
41 | (mic-plist-put plist :baz 3)
42 | (should (eq (plist-get plist :foo) 1))
43 | (should (eq (plist-get plist :bar) 2))
44 | (should (eq (plist-get plist :baz) 3)))
45 |
46 | (let (plist)
47 | (mic-plist-put plist :baz 3)
48 | (should (eq (plist-get plist :baz) 3))))
49 |
50 | (ert-deftest mic-plist-put-append ()
51 | (let ((plist '(:foo 1 :bar 2)))
52 | (mic-plist-put-append plist :baz '(3))
53 | (should (eq (plist-get plist :foo) 1))
54 | (should (eq (plist-get plist :bar) 2))
55 | (should (equal (plist-get plist :baz) '(3))))
56 |
57 | (let ((plist '(:foo (1) :bar (2))))
58 | (mic-plist-put-append plist :bar '(3))
59 | (should (equal (plist-get plist :foo) '(1)))
60 | (should (equal (plist-get plist :bar) '(2 3))))
61 |
62 | (let (plist)
63 | (mic-plist-put-append plist :baz '(3))
64 | (should (equal (plist-get plist :baz) '(3)))))
65 |
66 | (ert-deftest mic-plist-delete ()
67 | (let ((plist '(:foo 1 :bar 2)))
68 | (mic-plist-delete plist :foo)
69 | (should-not (plist-get plist :foo))
70 | (should (eq (plist-get plist :bar) 2))
71 | (should (equal plist '(:bar 2))))
72 |
73 | (let ((plist '(:foo 1 :bar 2)))
74 | (mic-plist-delete plist :bar)
75 | (should (eq (plist-get plist :foo) 1))
76 | (should-not (plist-get plist :bar))
77 | (should (equal plist '(:foo 1))))
78 |
79 | (let ((plist '(:foo 1 :bar 2 :baz 3)))
80 | (mic-plist-delete plist :bar :foo)
81 | (should-not (plist-get plist :foo))
82 | (should-not (plist-get plist :bar))
83 | (should (eq (plist-get plist :baz) 3))
84 | (should (equal plist '(:baz 3)))))
85 |
86 | (ert-deftest mic-plist-replace-keywords ()
87 | (let ((result
88 | (mic-plist-replace-keywords '(:foo 1 :bar 2 :baz 3)
89 | '((:foo . :hoge)
90 | (:bar . :fuga)))))
91 | (should-not (plist-get result :foo))
92 | (should-not (plist-get result :bar))
93 | (should (eq (plist-get result :hoge) 1))
94 | (should (eq (plist-get result :fuga) 2))
95 | (should (eq (plist-get result :baz) 3))))
96 |
97 | (provide 'mic-utils-test)
98 | ;;; mic-utils-test.el ends here
99 |
--------------------------------------------------------------------------------