branch: elpa/casual
commit a86074d50cef13d288d98a51e06cbaf38b000bb7
Merge: 7b3040aea5 c790dfb6fd
Author: Charles Choi <[email protected]>
Commit: GitHub <[email protected]>
Merge pull request #181 from kickingvegas/137-build-casual-make
Add Casual Make
---
README.org | 9 +-
docs/casual.info | 142 +++++++++---
docs/casual.org | 56 +++++
.../casual-make-automatic-variables-screenshot.png | Bin 0 -> 202163 bytes
docs/images/casual-make-mode-select-screenshot.png | Bin 0 -> 95538 bytes
docs/images/casual-make-screenshot.png | Bin 0 -> 257598 bytes
docs/make-mode.org | 76 +++++++
lisp/Makefile | 6 +-
lisp/Makefile-make-mode.make | 30 +++
lisp/casual-make-settings.el | 75 +++++++
lisp/casual-make-utils.el | 239 +++++++++++++++++++++
lisp/casual-make.el | 85 ++++++++
lisp/casual.el | 4 +
tests/Makefile | 5 +
tests/casual-make-test-utils.el | 70 ++++++
tests/test-casual-make-settings.el | 52 +++++
tests/test-casual-make-utils.el | 90 ++++++++
tests/test-casual-make.el | 75 +++++++
18 files changed, 983 insertions(+), 31 deletions(-)
diff --git a/README.org b/README.org
index 5d4117df6e..5ff910a845 100644
--- a/README.org
+++ b/README.org
@@ -41,6 +41,7 @@ Editorially, all design decisions for Casual are ultimately
the opinion of Charl
- [[#image-elisp-library-casual-image][Image (Elisp library:
~casual-image~)]]
- [[#info-elisp-library-casual-info][Info (Elisp library: ~casual-info~)]]
- [[#i-search-elisp-library-casual-isearch][I-Search (Elisp library:
~casual-isearch~)]]
+ - [[#make-elisp-library-casual-make][Make (Elisp library: ~casual-make~)]]
- [[#re-builder-elisp-library-casual-re-builder][Re-Builder (Elisp library:
~casual-re-builder~)]]
- [[#requirements][Requirements]]
- [[#install][Install]]
@@ -110,7 +111,12 @@ An interface for the Info documentation system.
An interface for the many commands supported by I-Search.
[[file:docs/isearch.org][file:docs/images/casual-isearch-tmenu.png]]
-
+
+** [[file:docs/make-mode.org][Make]] (Elisp library: ~casual-make~)
+An interface to ~make-mode~, a mode tailored for editing a Makefile.
+
+[[file:docs/make-mode.org][file:docs/images/casual-make-screenshot.png]]
+
** [[file:docs/re-builder.org][Re-Builder]] (Elisp library:
~casual-re-builder~)
An interface for the Emacs regular expression tool.
@@ -141,6 +147,7 @@ Configuration of a particular Casual user interface is
performed per mode. Go to
- [[file:docs/image.org::*Install][Image]]
- [[file:docs/info.org::*Install][Info]]
- [[file:docs/isearch.org::*Install][I-Search]]
+- [[file:docs/make-mode.org::*Install][Make]]
- [[file:docs/re-builder.org::*Install][Re-Builder]]
** Upgrading to Casual 2.x
diff --git a/docs/casual.info b/docs/casual.info
index 37b690af4f..96416eb874 100644
--- a/docs/casual.info
+++ b/docs/casual.info
@@ -1,4 +1,4 @@
-This is casual.info, produced by makeinfo version 7.1 from casual.texi.
+This is casual.info, produced by makeinfo version 7.2 from casual.texi.
INFO-DIR-SECTION Emacs misc features
START-INFO-DIR-ENTRY
@@ -65,9 +65,9 @@ Casual Modes
* Image::
* Info::
* I-Search::
+* Make::
* RE-Builder::
-
File: casual.info, Node: Motivations, Next: Requirements, Prev: Top, Up:
Top
@@ -210,6 +210,7 @@ The following modes are supported by Casual:
* Image::
* Info::
* I-Search::
+* Make::
* RE-Builder::
@@ -876,7 +877,7 @@ References
• *note Info: (info)Top.
-File: casual.info, Node: I-Search, Next: RE-Builder, Prev: Info, Up:
Casual Modes
+File: casual.info, Node: I-Search, Next: Make, Prev: Info, Up: Casual Modes
4.10 I-Search
=============
@@ -928,9 +929,88 @@ References
• *note I-Search: (emacs)Incremental Search.
-File: casual.info, Node: RE-Builder, Prev: I-Search, Up: Casual Modes
+File: casual.info, Node: Make, Next: RE-Builder, Prev: I-Search, Up:
Casual Modes
+
+4.11 Make
+=========
+
+Casual Make is a user interface to ‘make-mode’, a mode tailored for
+editing a Makefile.
+
+Configuration
+=============
+
+In your initialization file, bind the Transient ‘casual-make-tmenu’ to
+your key binding of preference. Two suggested bindings are ‘M-m’ or
+‘C-c m’.
+
+ (keymap-set makefile-mode-map "M-m" #'casual-make-tmenu)
+
+Usage
+=====
+
+It is recommended that some basic knowledge of the *make* command is
+known before using Casual Make.
+
+Basic Usage
+-----------
+
+When in a Makefile buffer, use ‘M-m’ (or your binding of choice) to
+raise the menu ‘casual-make-tmenu’. You will be presented with a menu
+with the following sections:
+
+ • Edit
+
+ Commands for editing the makefile. Note that the backslash and
+ comment commands require a region to be selected.
+
+ • Pickup as targets
+
+ Commands for synchronizing ‘make-mode’ with the target definitions
+ in the makefile. Use these commands if trying to insert a target
+ (via the ':' binding) does not include a target you have recently
+ entered.
+
+ • Misc
+
+ Miscellaneous commands related to working with a makefile.
+
+ • Navigate
+
+ Commands to support navigation within the makefile.
+
+Makefile Type Selection
+-----------------------
+
+As there are different variants of *make* and makefile formats, you can
+configure the mode for different specific makefile types. This can be
+done by selecting 'm' keybinding in ‘casual-make-tmenu’.
+
+Automatic Variables
+-------------------
+
+Casual Make provides a menu to enter GNU Make-style automatic variables.
+Note that each keybinding is identical to the automatic variable it
+represents to both reinforce its declaration and to avoid making another
+mapping. This menu is available from the 'i' keybinding in
+‘casual-make-tmenu’.
+
+Unless you edit makefiles frequently, it is very unlikely to be able to
+recall what an automatic variable declaration means. Casual Make
+provides the command ‘casual-make-identify-autovar-region’ to identify a
+selected automatic variable via the binding '.' in ‘casual-make-tmenu’.
+A short description of the automatic variable is shown in the
+mini-buffer.
+
+References
+==========
+
+ • *note Automatic Variables: (make)Automatic Variables.
+
+
+File: casual.info, Node: RE-Builder, Prev: Make, Up: Casual Modes
-4.11 RE-Builder
+4.12 RE-Builder
===============
Casual RE-Builder is a user interface for RE-Builder. Its top level
@@ -1205,6 +1285,9 @@ File: casual.info, Node: Index, Next: Variable Index,
Prev: Acknowledgments,
* Info Configuration: Info. (line 12)
* Info Usage: Info. (line 20)
* ISearch: I-Search. (line 6)
+* Make: Make. (line 6)
+* Make Configuration: Make. (line 12)
+* Make Usage: Make. (line 21)
* Motivations: Motivations. (line 5)
* RE-Builder: RE-Builder. (line 6)
* RE-Builder Configuration: RE-Builder. (line 12)
@@ -1227,37 +1310,38 @@ File: casual.info, Node: Variable Index, Prev: Index,
Up: Top
* casual-lib-hide-navigation: UX Conventions. (line 21)
* casual-lib-use-unicode: UX Conventions. (line 36)
-
Tag Table:
Node: Top228
-Node: Motivations1915
-Node: Requirements3408
-Node: Transient Conventions3672
-Node: Casual Modes5400
-Node: Agenda6383
-Node: Bookmarks7940
-Node: Calc10215
-Node: Calendar13356
-Node: Dired14704
-Node: EditKit17929
-Node: IBuffer19653
-Node: Image21718
-Node: Info22959
-Node: I-Search24425
-Node: RE-Builder25592
-Node: UX Conventions29003
-Node: Customization31704
-Node: Feedback & Discussion32078
-Node: Sponsorship32496
-Node: About32790
-Node: Acknowledgments33067
-Node: Index33449
-Node: Variable Index37132
+Node: Motivations1923
+Node: Requirements3416
+Node: Transient Conventions3680
+Node: Casual Modes5408
+Node: Agenda6400
+Node: Bookmarks7957
+Node: Calc10232
+Node: Calendar13373
+Node: Dired14721
+Node: EditKit17946
+Node: IBuffer19670
+Node: Image21735
+Node: Info22976
+Node: I-Search24442
+Node: Make25603
+Node: RE-Builder27975
+Node: UX Conventions31382
+Node: Customization34083
+Node: Feedback & Discussion34457
+Node: Sponsorship34875
+Node: About35169
+Node: Acknowledgments35446
+Node: Index35828
+Node: Variable Index39730
End Tag Table
Local Variables:
coding: utf-8
+Info-documentlanguage: en
End:
diff --git a/docs/casual.org b/docs/casual.org
index c94c29adb8..ed0d06815e 100644
--- a/docs/casual.org
+++ b/docs/casual.org
@@ -695,7 +695,63 @@ The main menu for Casual I-Search is organized into the
following sections:
#+TEXINFO: @unnumberedsec References
- [[info:emacs#Incremental Search][I-Search]]
+
+
+** Make
+#+CINDEX: Make
+
+Casual Make is a user interface to ~make-mode~, a mode tailored for editing a
Makefile.
+
+#+TEXINFO: @unnumberedsec Configuration
+#+CINDEX: Make Configuration
+
+In your initialization file, bind the Transient ~casual-make-tmenu~ to your
key binding of preference. Two suggested bindings are ~M-m~ or ~C-c m~.
+
+#+begin_src elisp :lexical no
+ (keymap-set makefile-mode-map "M-m" #'casual-make-tmenu)
+#+end_src
+
+#+TEXINFO: @unnumberedsec Usage
+#+CINDEX: Make Usage
+
+It is recommended that some basic knowledge of the *make* command is known
before using Casual Make.
+
+#+TEXINFO: @unnumberedsubsec Basic Usage
+
+When in a Makefile buffer, use ~M-m~ (or your binding of choice) to raise the
menu ~casual-make-tmenu~. You will be presented with a menu with the following
sections:
+
+- Edit
+
+ Commands for editing the makefile. Note that the backslash and comment
commands require a region to be selected.
+- Pickup as targets
+
+ Commands for synchronizing ~make-mode~ with the target definitions in the
makefile. Use these commands if trying to insert a target (via the ':' binding)
does not include a target you have recently entered.
+
+- Misc
+
+ Miscellaneous commands related to working with a makefile.
+
+- Navigate
+
+ Commands to support navigation within the makefile.
+
+
+#+TEXINFO: @unnumberedsubsec Makefile Type Selection
+
+As there are different variants of *make* and makefile formats, you can
configure the mode for different specific makefile types. This can be done by
selecting 'm' keybinding in ~casual-make-tmenu~.
+
+#+TEXINFO: @unnumberedsubsec Automatic Variables
+
+Casual Make provides a menu to enter GNU Make-style automatic variables. Note
that each keybinding is identical to the automatic variable it represents to
both reinforce its declaration and to avoid making another mapping. This menu
is available from the 'i' keybinding in ~casual-make-tmenu~.
+
+Unless you edit makefiles frequently, it is very unlikely to be able to recall
what an automatic variable declaration means. Casual Make provides the command
~casual-make-identify-autovar-region~ to identify a selected automatic variable
via the binding '.' in ~casual-make-tmenu~. A short description of the
automatic variable is shown in the mini-buffer.
+
+
+#+TEXINFO: @unnumberedsec References
+- [[info:make#Automatic Variables][Automatic Variables]]
+
+
** RE-Builder
#+CINDEX: RE-Builder
diff --git a/docs/images/casual-make-automatic-variables-screenshot.png
b/docs/images/casual-make-automatic-variables-screenshot.png
new file mode 100644
index 0000000000..41ba0256da
Binary files /dev/null and
b/docs/images/casual-make-automatic-variables-screenshot.png differ
diff --git a/docs/images/casual-make-mode-select-screenshot.png
b/docs/images/casual-make-mode-select-screenshot.png
new file mode 100644
index 0000000000..4f5c334741
Binary files /dev/null and b/docs/images/casual-make-mode-select-screenshot.png
differ
diff --git a/docs/images/casual-make-screenshot.png
b/docs/images/casual-make-screenshot.png
new file mode 100644
index 0000000000..eec1982eaa
Binary files /dev/null and b/docs/images/casual-make-screenshot.png differ
diff --git a/docs/make-mode.org b/docs/make-mode.org
new file mode 100644
index 0000000000..fb16580779
--- /dev/null
+++ b/docs/make-mode.org
@@ -0,0 +1,76 @@
+[[../README.org][❮ Back to Casual]]
+
+* Casual Make
+
+Casual Make is a user interface to ~make-mode~, a mode tailored for editing a
Makefile.
+
+[[file:images/casual-make-screenshot.png]]
+
+* Install
+
+In your initialization file, bind the Transient ~casual-make-tmenu~ to your
key binding of preference. Two suggested bindings are ~M-m~ or ~C-c m~.
+
+#+begin_src elisp :lexical no
+ (require 'casual-make) ; optional
+ (keymap-set makefile-mode-map "M-m" #'casual-make-tmenu)
+#+end_src
+
+* Usage
+
+It is recommended that some basic knowledge of the *make* command is known
before using Casual Make.
+
+** Basic Usage
+
+When in a Makefile buffer, use ~M-m~ (or your binding of choice) to raise the
menu ~casual-make-tmenu~. You will be presented with a menu with the following
sections:
+
+- Edit
+
+ Commands for editing the makefile. Note that the backslash and comment
commands require a region to be selected.
+
+- Pickup as targets
+
+ Commands for synchronizing ~make-mode~ with the target definitions in the
makefile. Use these commands if trying to insert a target (via the ':' binding)
does not include a target you have recently entered.
+
+- Misc
+
+ Miscellaneous commands related to working with a makefile.
+
+- Navigate
+
+ Commands to support navigation within the makefile.
+
+** Makefile Type Selection
+
+As there are different variants of *make* and makefile formats, you can
configure the mode for different specific makefile types. This can be done by
selecting 'm' keybinding in ~casual-make-tmenu~.
+
+[[file:images/casual-make-mode-select-screenshot.png]]
+
+
+** Automatic Variables
+
+Casual Make provides a menu to enter GNU Make-style
[[https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html][automatic
variables]]. Note that each keybinding is identical to the automatic variable
it represents to both reinforce its declaration and to avoid making another
mapping. This menu is available from the 'i' keybinding in ~casual-make-tmenu~.
+
+[[file:images/casual-make-automatic-variables-screenshot.png]]
+
+Unless you edit makefiles frequently, it is very unlikely to be able to recall
what an automatic variable declaration means. Casual Make provides the command
~casual-make-identify-autovar-region~ to identify a selected automatic variable
via the binding '.' in ~casual-make-tmenu~. A short description of the
automatic variable is shown in the mini-buffer.
+
+** Unicode Symbol Support
+By enabling “Use Unicode Symbols” from the Settings menu, Casual Make will use
Unicode symbols as appropriate in its menus.
+
+* Sponsorship
+If you enjoy using Casual Make, consider making a modest financial
contribution to help support its development and maintenance.
+
+[[https://www.buymeacoffee.com/kickingvegas][file:images/default-yellow.png]]
+
+* See Also
+- [[file:agenda.org][Agenda]]
+- [[file:bookmarks.org][Bookmarks]]
+- [[file:calc.org][Calc]]
+- [[file:calendar.org][Calendar]]
+- [[file:dired.org][Dired]]
+- [[file:editkit.org][EditKit (numerous editing commands)]]
+- [[file:ibuffer.org][IBuffer]]
+- [[file:image.org][Image]]
+- [[file:info.org][Info]]
+- [[file:isearch.org][I-Search]]
+- [[file:re-builder.org][RE-Builder]]
diff --git a/lisp/Makefile b/lisp/Makefile
index 70436c76be..3e6d5d8623 100644
--- a/lisp/Makefile
+++ b/lisp/Makefile
@@ -28,9 +28,9 @@ editkit-tests \
ibuffer-tests \
info-tests \
isearch-tests \
+make-mode-tests \
re-builder-tests
-
.PHONY: lib-tests
lib-tests:
$(MAKE) -C $(SRC_DIR) -f Makefile-lib.make tests
@@ -75,6 +75,10 @@ info-tests:
isearch-tests:
$(MAKE) -C $(SRC_DIR) -f Makefile-isearch.make tests
+.PHONY: make-mode-tests
+make-mode-tests:
+ $(MAKE) -C $(SRC_DIR) -f Makefile-make-mode.make tests
+
.PHONY: re-builder-tests
re-builder-tests:
$(MAKE) -C $(SRC_DIR) -f Makefile-re-builder.make tests
diff --git a/lisp/Makefile-make-mode.make b/lisp/Makefile-make-mode.make
new file mode 100644
index 0000000000..f567e868f0
--- /dev/null
+++ b/lisp/Makefile-make-mode.make
@@ -0,0 +1,30 @@
+##
+# Copyright (C) 2025 Charles Y. Choi
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+include Makefile--defines.make
+
+PACKAGE_NAME=casual-make
+ELISP_INCLUDES=casual-make-utils.el \
+casual-make-settings.el
+ELISP_PACKAGES=
+ELISP_TEST_INCLUDES=casual-make-test-utils.el
+PACKAGE_PATHS= \
+-L $(EMACS_ELPA_DIR)/compat-current \
+-L $(EMACS_ELPA_DIR)/seq-current \
+-L $(EMACS_ELPA_DIR)/transient-current \
+-L $(CASUAL_LIB_LISP_DIR)
+
+include Makefile--rules.make
diff --git a/lisp/casual-make-settings.el b/lisp/casual-make-settings.el
new file mode 100644
index 0000000000..da861b1fc8
--- /dev/null
+++ b/lisp/casual-make-settings.el
@@ -0,0 +1,75 @@
+;;; casual-make-settings.el --- Casual Make Settings -*- lexical-binding: t;
-*-
+
+;; Copyright (C) 2025 Charles Y. Choi
+
+;; Author: Charles Choi <[email protected]>
+;; Keywords: tools
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+
+;;; Code:
+(require 'make-mode)
+(require 'casual-lib)
+
+(transient-define-prefix casual-make-settings-tmenu ()
+ "Casual Make settings menu."
+ ["Make: Settings"
+ ["Customize"
+ ("G" "Makefile Group" casual-make--customize-group)
+ (casual-lib-customize-unicode)
+ (casual-lib-customize-hide-navigation)]]
+
+ [:class transient-row
+ (casual-lib-quit-one)
+ ("a" "About" casual-make-about :transient nil)
+ (casual-lib-quit-all)])
+
+(defun casual-make--customize-group ()
+ "Customize Makefile group."
+ (interactive)
+ (customize-group "makefile"))
+
+(defun casual-make-about-make ()
+ "Casual Make is a Transient menu for makefiles.
+
+Learn more about using Casual Make at our discussion group on GitHub.
+Any questions or comments about it should be made there.
+URL `https://github.com/kickingvegas/casual/discussions'
+
+If you find a bug or have an enhancement request, please file an issue.
+Our best effort will be made to answer it.
+URL `https://github.com/kickingvegas/casual/issues'
+
+If you enjoy using Casual Make, consider making a modest financial
+contribution to help support its development and maintenance.
+URL `https://www.buymeacoffee.com/kickingvegas'
+
+Casual Make was conceived and crafted by Charles Choi in
+San Francisco, California.
+
+Thank you for using Casual Make.
+
+Always choose love."
+ (ignore))
+
+(defun casual-make-about ()
+ "About information for Casual Make."
+ (interactive)
+ (describe-function #'casual-make-about-make))
+
+(provide 'casual-make-settings)
+;;; casual-make-settings.el ends here
diff --git a/lisp/casual-make-utils.el b/lisp/casual-make-utils.el
new file mode 100644
index 0000000000..2134b71d6d
--- /dev/null
+++ b/lisp/casual-make-utils.el
@@ -0,0 +1,239 @@
+;;; casual-make-utils.el --- Casual Make Utils -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2025 Charles Y. Choi
+
+;; Author: Charles Choi <[email protected]>
+;; Keywords: tools
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+
+;;; Code:
+(require 'map)
+(require 'make-mode)
+(require 'casual-lib)
+
+(defconst casual-make-unicode-db
+ '((:previous . '("↑" "Previous"))
+ (:next . '("↓" "Next")))
+
+ "Unicode symbol DB to use for Make Transient menus.")
+
+(defun casual-make-unicode-get (key)
+ "Lookup Unicode symbol for KEY in DB.
+
+- KEY symbol used to lookup Unicode symbol in DB.
+
+If the value of customizable variable `casual-lib-use-unicode'
+is non-nil, then the Unicode symbol is returned, otherwise a
+plain ASCII-range string."
+ (casual-lib-unicode-db-get key casual-make-unicode-db))
+
+
+(defconst casual-make--make-mode-label-map
+ '((makefile-automake-mode . "automake")
+ (makefile-bsdmake-mode . "BSD make")
+ (makefile-gmake-mode . "GNU make")
+ (makefile-imake-mode . "imake")
+ (makefile-mode . "make")
+ (makefile-makepp-mode . "makepp"))
+ "Map of make modes to their labels.")
+
+(defun casual-make-mode-label (mode)
+ "Label for make MODE."
+ (map-elt casual-make--make-mode-label-map mode))
+
+(transient-define-prefix casual-make-mode-select-tmenu ()
+ "Configure `make-mode' for different variants of Make."
+
+ ["Select Make Mode"
+ :description (lambda ()
+ (format
+ "Select Make Mode (%s)"
+ (casual-make-mode-label major-mode)))
+ [("a" "automake" makefile-automake-mode)
+ ("b" "BSD make" makefile-bsdmake-mode)
+ ("g" "GNU make" makefile-gmake-mode)]
+ [("i" "imake" makefile-imake-mode)
+ ("m" "make" makefile-mode)
+ ("e" "makepp" makefile-makepp-mode)]]
+
+ [:class transient-row
+ (casual-lib-quit-one)
+ (casual-lib-quit-all)])
+
+(defconst casual-make--autovar-description-map
+ '(("$@" . "The target name.")
+ ("$(@D)" . "The directory part of the target.")
+ ("$(@F)" . "The file-within-directory part of the target.")
+ ("$*" . "Stem with which an implicit rule matches.")
+ ("$(*D)" . "Directory part of stem.")
+ ("$(*F)" . "File-within-directory part of stem.")
+ ("$%" . "The target member name, when the target is an archive member.")
+ ("$(%D)" . "The directory part of the target archive member name.")
+ ("$(%F)" . "The file-within-directory part of the target archive member
name.")
+ ("$<" . "The name of the first prerequisite.")
+ ("$(<D)" . "The directory part of the first prerequisite.")
+ ("$(<F)" . "The file-within-directory part of the first prerequisite.")
+ ("$?" . "The names of all the prerequisites that are newer than the
target.")
+ ("$(?D)" . "Lists of the directory parts of all prerequisites newer than
the target.")
+ ("$(?F)" . "Lists of the file-within-directory parts of all prerequisites
newer than the target.")
+ ("$^" . "The names of all prerequisites, normalized (de-duplicated).")
+ ("$(^D)" . "Lists of the directory parts of all prerequisites,
normalized.")
+ ("$(^F)" . "Lists of the file-within-directory parts of all prerequisites,
normalized.")
+ ("$+" . "The names of all prerequisites, allowing duplicates.")
+ ("$(+D)" . "Lists of the directory parts of all prerequisites, allowing
duplicates.")
+ ("$(+F)" . "Lists of the file-within-directory parts of all prerequisites,
allowing duplicates."))
+ "Map of automatic variable descriptions.")
+
+(defun casual-make--identify-autovar (value)
+ "Identify GNU Make automatic variable in VALUE."
+ (let* ((value (string-clean-whitespace value))
+ (description (map-elt casual-make--autovar-description-map value)))
+ (if description
+ (format "%s - %s" value description)
+ (format "%s is not a GNU Make automatic variable." value))))
+
+(defun casual-make-identify-autovar-region (start end)
+ "Identify GNU Make automatic variable in region from START to END."
+ (interactive "r")
+ (let ((value (buffer-substring start end)))
+ (message (casual-make--identify-autovar value))))
+
+
+;; Transients
+(transient-define-prefix casual-make-automatic-variables-tmenu ()
+ "Makefile automatic variables menu.
+
+Menu for GNU Make automatic variables.
+
+For more info, refer to info node `(make) Automatic Variables'."
+
+ ["Automatic Variables - GNU Make"
+ ["Target @"
+ :pad-keys t
+ ("$@" "Name"
+ (lambda ()
+ "The target name."
+ (interactive) (insert "$@")))
+ ("$(@D)" "Directory"
+ (lambda ()
+ "The directory part of the target."
+ (interactive) (insert "$(@D)")))
+ ("$(@F)" "File"
+ (lambda ()
+ "The file-within-directory part of the target."
+ (interactive) (insert "$(@F)")))]
+
+ ["Implicit Stem *"
+ :pad-keys t
+ ("$*" "Stem"
+ (lambda ()
+ "Stem with which an implicit rule matches."
+ (interactive) (insert "$*")))
+ ("$(*D)" "Directory"
+ (lambda ()
+ "Directory part of stem."
+ (interactive) (insert "$(*D)")))
+ ("$(*F)" "File"
+ (lambda ()
+ "File-within-directory part of stem."
+ (interactive) (insert "$(*F)")))]
+
+ ["Archive %"
+ :pad-keys t
+ ("$%" "Archive"
+ (lambda ()
+ "The target member name, when the target is an archive member."
+ (interactive) (insert "$%")))
+ ("$(%D)" "Directory"
+ (lambda ()
+ "The directory part of the target archive member name."
+ (interactive) (insert "$(%D)")))
+ ("$(%F)" "File"
+ (lambda ()
+ "The file-within-directory part of the target archive member name."
+ (interactive) (insert "$(%F)")))]]
+
+ ["Prerequisites"
+ ["First <"
+ :pad-keys t
+ ("$<" "First"
+ (lambda ()
+ "The name of the first prerequisite."
+ (interactive) (insert "$<")))
+ ("$(<D)" "Directory"
+ (lambda ()
+ "The directory part of the first prerequisite."
+ (interactive) (insert "$(<D)")))
+ ("$(<F)" "File"
+ (lambda ()
+ "The file-within-directory part of the first prerequisite."
+ (interactive) (insert "$(<F)")))]
+
+ ["Newer than Target ?"
+ :pad-keys t
+ ("$?" "All"
+ (lambda ()
+ "The names of all the prerequisites that are newer than the target."
+ (interactive) (insert "$?")))
+ ("$(?D)" "Directory"
+ (lambda ()
+ "Lists of the directory parts of all prerequisites newer than the
target."
+ (interactive) (insert "$(?D)")))
+ ("$(?F)" "File"
+ (lambda ()
+ "Lists of the file-within-dir parts of all prerequisites newer than the
target."
+ (interactive) (insert "$(?F)")))]
+
+ ["Normalized ^"
+ :pad-keys t
+ ("$^" "All"
+ (lambda ()
+ "The names of all prerequisites, normalized (de-duplicated)."
+ (interactive) (insert "$^")))
+ ("$(^D)" "Directory"
+ (lambda ()
+ "Lists of the directory parts of all prerequisites, normalized."
+ (interactive) (insert "$(^D)")))
+ ("$(^F)" "File"
+ (lambda ()
+ "Lists of the file-within-directory parts of all prerequisites,
normalized."
+ (interactive) (insert "$(^F)")))]
+
+ ["Include Duplicates +"
+ :pad-keys t
+ ("$+" "All"
+ (lambda ()
+ "The names of all prerequisites, allowing duplicates."
+ (interactive) (insert "$+")))
+ ("$(+D)" "Directory"
+ (lambda ()
+ "Lists of the directory parts of all prerequisites, allowing
duplicates."
+ (interactive) (insert "$(+D)")))
+ ("$(+F)" "File"
+ (lambda ()
+ "Lists of the file-within-dir parts of all prerequisites, allowing
duplicates."
+ (interactive) (insert "$(+F)")))]]
+
+ [:class transient-row
+ (casual-lib-quit-one)
+ (casual-lib-quit-all)
+ ("RET" "Dismiss" casual-lib-quit-all)
+ ("i" "Info" (lambda () (interactive) (info "(make) Automatic Variables")))])
+
+(provide 'casual-make-utils)
+;;; casual-make-utils.el ends here
diff --git a/lisp/casual-make.el b/lisp/casual-make.el
new file mode 100644
index 0000000000..f239b47429
--- /dev/null
+++ b/lisp/casual-make.el
@@ -0,0 +1,85 @@
+;;; casual-make.el --- Transient UI for Make -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2025 Charles Y. Choi
+
+;; Author: Charles Choi <[email protected]>
+;; Keywords: tools
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This library provides a Transient-based user interface for `make-mode'.
+
+;;
+;; INSTALLATION
+
+;; In your initialization file, bind the Transient `casual-make-tmenu' to your
+;; key binding of preference. Two suggested bindings are 'M-m' and 'C-c m'.
+
+;; (require 'casual-make) ; optional if using autoloaded menu
+;; (keymap-set makefile-mode-map "M-m" #'casual-make-tmenu)
+
+;;; Code:
+(require 'casual-make-utils)
+(require 'casual-make-settings)
+
+;;;###autoload (autoload 'casual-make-tmenu "casual-make" nil t)
+(transient-define-prefix casual-make-tmenu ()
+ "Main menu for Casual Make.
+
+This menu provides a user interface for the commands in `make-mode'."
+
+ ["Casual Make"
+ :description (lambda () (format "Casual Make (%s)" (casual-make-mode-label
major-mode)))
+ ["Edit"
+ :inapt-if (lambda () (if buffer-read-only t nil))
+ ("\\" "Backslash region" makefile-backslash-region :inapt-if-not
use-region-p)
+ (";" "Comment region" comment-region :inapt-if-not use-region-p)
+ (":" "Insert target…" makefile-insert-target-ref)
+ ("m" "Insert macro…" makefile-insert-macro-ref)
+ ("f" "Insert GNU function…" makefile-insert-gmake-function)
+ ("a" "Automatic Variables›" casual-make-automatic-variables-tmenu)]
+
+ ["Pickup as targets"
+ ("E" "Everything" makefile-pickup-everything)
+ ("F" "Filenames" makefile-pickup-filenames-as-targets)]
+
+ ["Misc"
+ ("c" "Compile…" compile)
+ ("o" "Overview" makefile-create-up-to-date-overview)
+ ("t" "Makefile Type›" casual-make-mode-select-tmenu :transient t)
+ ("." "Identify Auto Var" casual-make-identify-autovar-region
+ :inapt-if-not use-region-p)]
+
+ ["Navigate"
+ ("i" "Index Menu…" imenu :transient t)
+ ("p" "Previous" makefile-previous-dependency
+ :description (lambda ()
+ (format "%s target" (casual-make-unicode-get :previous)))
+ :transient t)
+ ("n" "Next" makefile-next-dependency
+ :description (lambda ()
+ (format "%s target"(casual-make-unicode-get :next)))
+ :transient t)]]
+
+ [:class transient-row
+ (casual-lib-quit-one)
+ ("," "Settings" casual-make-settings-tmenu)
+ ("I" "ⓘ Make" (lambda () (interactive) (info "(make) Top")))
+ (casual-lib-quit-all)
+ ("RET" "Exit Menu" transient-quit-all)])
+
+(provide 'casual-make)
+;;; casual-make.el ends here
diff --git a/lisp/casual.el b/lisp/casual.el
index abb1a66a85..c082a52912 100644
--- a/lisp/casual.el
+++ b/lisp/casual.el
@@ -74,6 +74,10 @@
;; An interface for the many commands supported by I-Search.
;; URL: `https://github.com/kickingvegas/casual/blob/main/docs/isearch.org'
+;; - Make (Elisp library: `casual-make')
+;; An interface to `make-mode'.
+;; URL: `https://github.com/kickingvegas/casual/blob/main/docs/make-mode.org'
+
;; - Re-Builder (Elisp library: `casual-re-builder')
;; An interface for the Emacs regular expression tool.
;; URL:
`https://github.com/kickingvegas/casual/blob/main/docs/re-builder.org'
diff --git a/tests/Makefile b/tests/Makefile
index 46026aad3f..c485160e9d 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -27,6 +27,7 @@ editkit-tests \
ibuffer-tests \
info-tests \
isearch-tests \
+make-mode-tests \
re-builder-tests
SRC_DIR=../lisp
@@ -41,6 +42,7 @@ editkit-tests \
ibuffer-tests \
info-tests \
isearch-tests \
+make-mode-tests \
re-builder-tests
lib-tests:
@@ -73,5 +75,8 @@ info-tests:
isearch-tests:
$(MAKE) -C $(SRC_DIR) $@
+make-mode-tests:
+ $(MAKE) -C $(SRC_DIR) $@
+
re-builder-tests:
$(MAKE) -C $(SRC_DIR) $@
diff --git a/tests/casual-make-test-utils.el b/tests/casual-make-test-utils.el
new file mode 100644
index 0000000000..2e57e95a80
--- /dev/null
+++ b/tests/casual-make-test-utils.el
@@ -0,0 +1,70 @@
+;;; casual-make-test-utils.el --- Casual Test Utils -*- lexical-binding:
t; -*-
+
+;; Copyright (C) 2024-2025 Charles Y. Choi
+
+;; Author: Charles Choi <[email protected]>
+;; Keywords: tools
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;
+
+;;; Code:
+(require 'ert)
+(require 'casual-lib)
+(require 'kmacro)
+
+
+(defun casualt-make-setup (&optional filename)
+ "Casual editkit test with FILENAME.
+
+- FILENAME file name to be stored in /tmp."
+ (when filename
+ (let ((temp-filename (concat "/tmp/" filename)))
+ (with-temp-file temp-filename
+ (insert "# Hello"))
+ (find-file temp-filename))))
+
+(defun casualt-make-breakdown (&optional filename)
+ "Casual menu test breakdown function with FILENAME.
+
+- FILENAME filename that is stored in /tmp"
+
+ (when filename
+ (let ((temp-filename (concat "/tmp/" filename)))
+ (kill-buffer)
+ (delete-file temp-filename))))
+
+
+(defun casualt-mock-active-region ()
+ "Mock an active region to test `use-region-p'."
+
+ (let ((p1 (line-beginning-position)))
+ (transient-mark-mode t)
+
+ (push-mark p1)
+ (insert "hey: mary\n")
+ (insert "jane\n")
+ (insert "bob\n")
+
+ (goto-char (line-end-position))
+ (setq mark-active t)
+ ))
+
+
+
+(provide 'casual-make-test-utils)
+;;; casual-make-test-utils.el ends here
diff --git a/tests/test-casual-make-settings.el
b/tests/test-casual-make-settings.el
new file mode 100644
index 0000000000..2e0306a7fe
--- /dev/null
+++ b/tests/test-casual-make-settings.el
@@ -0,0 +1,52 @@
+;;; test-casual-make-settings.el --- Casual Make Settings Tests -*-
lexical-binding: t; -*-
+
+;; Copyright (C) 2025 Charles Y. Choi
+
+;; Author: Charles Choi <[email protected]>
+;; Keywords: tools
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;
+
+;;; Code:
+
+(require 'ert)
+(require 'casual-make-test-utils)
+(require 'casual-make-settings)
+
+(ert-deftest test-casual-make-settings-tmenu ()
+ (let ((tmpfile "casual-make-settings-tmenu.txt"))
+ (casualt-make-setup)
+ (cl-letf (;;((symbol-function #') (lambda () t))
+ (casualt-mock #'casual-make--customize-group))
+
+ (let ((test-vectors
+ '((:binding "G" :command casual-make--customize-group)
+ (:binding "u" :command
casual-lib-customize-casual-lib-use-unicode)
+ (:binding "n" :command
casual-lib-customize-casual-lib-hide-navigation)
+ (:binding "a" :command casual-make-about))))
+
+ (casualt-suffix-testcase-runner test-vectors
+ #'casual-make-settings-tmenu
+ '(lambda () (random 5000)))))
+ (casualt-make-breakdown)))
+
+(ert-deftest test-casual-make-about ()
+ (should (stringp (casual-make-about))))
+
+(provide 'test-casual-make-settings)
+;;; test-casual-make-setttings.el ends here
diff --git a/tests/test-casual-make-utils.el b/tests/test-casual-make-utils.el
new file mode 100644
index 0000000000..e1d4e2ec17
--- /dev/null
+++ b/tests/test-casual-make-utils.el
@@ -0,0 +1,90 @@
+;;; test-casual-make-utils.el --- Casual Make Utils Tests -*-
lexical-binding: t; -*-
+
+;; Copyright (C) 2025 Charles Y. Choi
+
+;; Author: Charles Choi <[email protected]>
+;; Keywords: tools
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;
+
+;;; Code:
+(require 'ert)
+(require 'casual-make-test-utils)
+(require 'casual-make-utils)
+
+(ert-deftest test-casual-make-unicode-get ()
+ (let ((casual-lib-use-unicode nil))
+ (should (string-equal (casual-make-unicode-get :previous) "Previous"))
+ (should (string-equal (casual-make-unicode-get :next) "Next")))
+
+ (let ((casual-lib-use-unicode t))
+ (should (string-equal (casual-make-unicode-get :previous) "↑"))
+ (should (string-equal (casual-make-unicode-get :next) "↓"))))
+
+
+(ert-deftest test-casual-make-mode-select-tmenu ()
+ (let ((tmpfile "casual-make-mode-select-tmenu.txt"))
+ (casualt-make-setup)
+ (cl-letf ((casualt-mock #'makefile-automake-mode)
+ (casualt-mock #'makefile-bsdmake-mode)
+ (casualt-mock #'makefile-gmake-mode)
+ (casualt-mock #'makefile-imake-mode)
+ (casualt-mock #'makefile-mode)
+ (casualt-mock #'makefile-makepp-mode))
+
+ (let ((test-vectors
+ '((:binding "a" :command makefile-automake-mode)
+ (:binding "b" :command makefile-bsdmake-mode)
+ (:binding "g" :command makefile-gmake-mode)
+ (:binding "i" :command makefile-imake-mode)
+ (:binding "m" :command makefile-mode)
+ (:binding "e" :command makefile-makepp-mode))))
+
+ (casualt-suffix-testcase-runner test-vectors
+ #'casual-make-mode-select-tmenu
+ '(lambda () (random 5000)))))
+ (casualt-make-breakdown)))
+
+
+(ert-deftest test-casual-make--autovar-description-map ()
+ (let ((avmap casual-make--autovar-description-map))
+ (should (string= (map-elt avmap "$@") "The target name."))
+ (should (string= (map-elt avmap "$(@D)") "The directory part of the
target."))
+ (should (string= (map-elt avmap "$(@F)") "The file-within-directory part
of the target."))
+ (should (string= (map-elt avmap "$*") "Stem with which an implicit rule
matches."))
+ (should (string= (map-elt avmap "$(*D)") "Directory part of stem."))
+ (should (string= (map-elt avmap "$(*F)") "File-within-directory part of
stem."))
+ (should (string= (map-elt avmap "$%") "The target member name, when the
target is an archive member."))
+ (should (string= (map-elt avmap "$(%D)") "The directory part of the target
archive member name."))
+ (should (string= (map-elt avmap "$(%F)") "The file-within-directory part
of the target archive member name."))
+ (should (string= (map-elt avmap "$<") "The name of the first
prerequisite."))
+ (should (string= (map-elt avmap "$(<D)") "The directory part of the first
prerequisite."))
+ (should (string= (map-elt avmap "$(<F)") "The file-within-directory part
of the first prerequisite."))
+ (should (string= (map-elt avmap "$?") "The names of all the prerequisites
that are newer than the target."))
+ (should (string= (map-elt avmap "$(?D)") "Lists of the directory parts of
all prerequisites newer than the target."))
+ (should (string= (map-elt avmap "$(?F)") "Lists of the
file-within-directory parts of all prerequisites newer than the target."))
+ (should (string= (map-elt avmap "$^") "The names of all prerequisites,
normalized (de-duplicated)."))
+ (should (string= (map-elt avmap "$(^D)") "Lists of the directory parts of
all prerequisites, normalized."))
+ (should (string= (map-elt avmap "$(^F)") "Lists of the
file-within-directory parts of all prerequisites, normalized."))
+ (should (string= (map-elt avmap "$+") "The names of all prerequisites,
allowing duplicates."))
+ (should (string= (map-elt avmap "$(+D)") "Lists of the directory parts of
all prerequisites, allowing duplicates."))
+ (should (string= (map-elt avmap "$(+F)") "Lists of the
file-within-directory parts of all prerequisites, allowing duplicates."))))
+
+
+(provide 'test-casual-make-utils)
+;;; test-casual-make-utils.el ends here
diff --git a/tests/test-casual-make.el b/tests/test-casual-make.el
new file mode 100644
index 0000000000..a45310dab4
--- /dev/null
+++ b/tests/test-casual-make.el
@@ -0,0 +1,75 @@
+;;; test-casual-make.el --- Casual Make Tests -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2025 Charles Y. Choi
+
+;; Author: Charles Choi <[email protected]>
+;; Keywords: tools
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;
+
+;;; Code:
+
+(require 'ert)
+(require 'casual-make-test-utils)
+(require 'casual-lib-test-utils)
+(require 'casual-make)
+
+(ert-deftest test-casual-make-tmenu ()
+ (let ((tmpfile "Makefile"))
+ (casualt-make-setup tmpfile)
+ (cl-letf ((casualt-mock #'makefile-backslash-region)
+ (casualt-mock #'comment-region)
+ (casualt-mock #'makefile-insert-target-ref)
+ (casualt-mock #'makefile-insert-macro-ref)
+ (casualt-mock #'makefile-insert-gmake-function)
+ (casualt-mock #'makefile-pickup-everything)
+ (casualt-mock #'makefile-pickup-filenames-as-targets)
+ (casualt-mock #'makefile-create-up-to-date-overview)
+ (casualt-mock #'makefile-previous-dependency)
+ (casualt-mock #'makefile-next-dependency)
+ (casualt-mock #'compile)
+ (casualt-mock #'imenu))
+
+ (let ((test-vectors
+ '((:binding "\\" :command makefile-backslash-region)
+ (:binding ";" :command comment-region)
+ (:binding ":" :command makefile-insert-target-ref)
+ (:binding "m" :command makefile-insert-macro-ref)
+ (:binding "f" :command makefile-insert-gmake-function)
+ (:binding "a" :command casual-make-automatic-variables-tmenu)
+
+ (:binding "E" :command makefile-pickup-everything)
+ (:binding "F" :command makefile-pickup-filenames-as-targets)
+
+ (:binding "c" :command compile)
+ (:binding "o" :command makefile-create-up-to-date-overview)
+ (:binding "t" :command casual-make-mode-select-tmenu)
+ (:binding "." :command casual-make-identify-autovar-region)
+
+ ;; (:binding "i" :command imenu)
+ (:binding "p" :command makefile-previous-dependency)
+ (:binding "n" :command makefile-next-dependency))))
+
+ (casualt-mock-active-region)
+ (casualt-suffix-testcase-runner test-vectors
+ #'casual-make-tmenu
+ '(lambda () (random 5000)))))
+ (casualt-make-breakdown tmpfile)))
+
+(provide 'test-casual-make)
+;;; test-casual-make.el ends here