branch: elpa/casual
commit dd68c1189122d7aa01c52173c487625ebfdfbd2e
Merge: 0bd77c868b 10e4b3eca1
Author: Charles Choi <[email protected]>
Commit: GitHub <[email protected]>

    Merge pull request #316 from 
kickingvegas/merge-development-to-main-20251029_125723
    
    Merge development to main 20251029_125723
---
 docs/agenda.org                                    |  10 +-
 docs/bookmarks.org                                 |   2 +-
 docs/calc.org                                      |   2 +-
 docs/casual.info                                   | Bin 132785 -> 139795 bytes
 docs/casual.org                                    |  16 +-
 docs/casual.texi                                   | 250 ++++++++++++++---
 docs/compile.org                                   |   2 +-
 docs/dired.org                                     |   4 +-
 docs/ediff.org                                     | 113 ++++++++
 docs/editkit.org                                   |  22 +-
 docs/elisp.org                                     |   4 +-
 docs/eshell.org                                    |   2 +-
 docs/help.org                                      |   2 +-
 docs/htmlxref.cnf                                  |   1 +
 docs/image.org                                     |   2 +-
 docs/images/casual-ediff-basic-screenshot.png      | Bin 0 -> 283120 bytes
 docs/images/casual-ediff-merge-conflict.png        | Bin 0 -> 313757 bytes
 docs/images/casual-ediff-screenshot.png            | Bin 0 -> 362160 bytes
 docs/info.org                                      |   2 +-
 docs/man.org                                       |   6 +-
 docs/re-builder.org                                |   8 +-
 lisp/Makefile                                      |   5 +
 lisp/Makefile-agenda.make                          |   1 +
 lisp/Makefile-bibtex.make                          |   1 +
 lisp/Makefile-bookmarks.make                       |   1 +
 lisp/Makefile-calc.make                            |   1 +
 lisp/Makefile-calendar.make                        |   1 +
 lisp/Makefile-compile.make                         |   1 +
 lisp/{Makefile-eshell.make => Makefile-ediff.make} |   9 +-
 lisp/Makefile-editkit.make                         |   1 +
 lisp/Makefile-elisp.make                           |   1 +
 lisp/Makefile-eshell.make                          |   1 +
 lisp/Makefile-help.make                            |   1 +
 lisp/Makefile-ibuffer.make                         |   1 +
 lisp/Makefile-image.make                           |   1 +
 lisp/Makefile-isearch.make                         |   1 +
 lisp/Makefile-make-mode.make                       |   1 +
 lisp/Makefile-man.make                             |   1 +
 lisp/Makefile-re-builder.make                      |   1 +
 lisp/Makefile-timezone.make                        |   1 +
 lisp/casual-ediff-settings.el                      | 100 +++++++
 lisp/casual-ediff-utils.el                         | 242 +++++++++++++++++
 lisp/casual-ediff.el                               | 295 +++++++++++++++++++++
 tests/Makefile                                     |   5 +
 tests/casual-ediff-test-utils.el                   |  39 +++
 tests/test-casual-ediff-settings.el                |  60 +++++
 tests/test-casual-ediff-utils.el                   |  98 +++++++
 tests/test-casual-ediff.el                         |  56 ++++
 tests/test-casual-eshell-settings.el               |   1 +
 tests/test-casual-eshell-utils.el                  |   4 -
 tests/test-casual-timezone-utils.el                |   9 +-
 51 files changed, 1296 insertions(+), 92 deletions(-)

diff --git a/docs/agenda.org b/docs/agenda.org
index faa7271e0b..bf00099adb 100644
--- a/docs/agenda.org
+++ b/docs/agenda.org
@@ -40,29 +40,29 @@ The main menu for Casual Agenda is ~casual-agenda-tmenu~. 
It is divided into fiv
 - Utils :: Set a timer, get almanac info.
 
 
-#+TEXINFO: @majorheading Operating on Headlines
+#+TEXINFO: @subheading Operating on Headlines
 Use “{{{kbd(o)}}} Operations›” from ~casual-agenda-tmenu~ to change a 
headline's attributes such as TODO state, scheduling, tags, and priority. The 
following menu will be displayed.
 
 [[file:images/casual-agenda-operations-screenshot.png]]
 
 
-#+TEXINFO: @majorheading Marking Headlines
+#+TEXINFO: @subheading Marking Headlines
 
 Use “{{{kbd(M)}}} Mark›” menu from ~casual-agenda-tmenu~ to mark different 
headlines and perform a bulk action on them. 
 
 [[file:images/casual-agenda-mark-screenshot.png]]
 
-#+TEXINFO: @majorheading Almanac
+#+TEXINFO: @subheading Almanac
 Get sunrise/sunset times, lunar cycle dates, and holidays with respect to a 
date via the “{{{kbd(l)}}} Almanac›” menu from ~casual-agenda-tmenu~.
 
 [[file:images/casual-agenda-almanac-screenshot.png]]
 
-#+TEXINFO: @majorheading Changing Modes and Settings
+#+TEXINFO: @subheading Changing Modes and Settings
 Agenda views have different display modes and behavior that can be modified 
from the “{{{kbd(\,)}}} Settings›” menu from ~casual-agenda-tmenu~.
 
 [[file:images/casual-agenda-settings-screenshot.png]]
 
-#+TEXINFO: @majorheading Agenda Unicode Symbol Support
+#+TEXINFO: @subheading Agenda Unicode Symbol Support
 By enabling “{{{kbd(u)}}} Use Unicode Settings” from the Settings menu, Casual 
Agenda will use Unicode symbols as appropriate in its menus.
 
 [[file:images/casual-agenda-unicode-screenshot.png]]
diff --git a/docs/bookmarks.org b/docs/bookmarks.org
index 61f13265d5..24e32827ab 100644
--- a/docs/bookmarks.org
+++ b/docs/bookmarks.org
@@ -70,7 +70,7 @@ Casual Bookmarks organizes its main menu into the following 
sections:
 - Column :: Commands to navigate and control the display of the table layout 
for bookmarks.
 
 
-#+TEXINFO: @majorheading Unicode Symbol Support
+#+TEXINFO: @subheading Unicode Symbol Support
 
 [[file:images/casual-bookmarks-unicode-screenshot.png]]
 
diff --git a/docs/calc.org b/docs/calc.org
index cdb22f30f0..149d88b97f 100644
--- a/docs/calc.org
+++ b/docs/calc.org
@@ -39,7 +39,7 @@ Casual Calc organizes its main menu into the following 
sections:
 - Functions :: Entry point for sub-menus of commands organized into different 
classes of functionality.
 - Settings :: Entry point for sub-menus of commands to configure Calc settings.
 
-#+TEXINFO: @majorheading Calc Basics
+#+TEXINFO: @subheading Calc Basics
 
 It helps to know some basics about Calc.
 
diff --git a/docs/casual.info b/docs/casual.info
index 751861b6d7..533f4adc0b 100644
Binary files a/docs/casual.info and b/docs/casual.info differ
diff --git a/docs/casual.org b/docs/casual.org
index 778a29c2e3..9ca40c0c44 100644
--- a/docs/casual.org
+++ b/docs/casual.org
@@ -5,7 +5,7 @@
 #+EMAIL: [email protected]
 #+OPTIONS: ':t toc:t author:t email:t H:4 f:t
 #+LANGUAGE: en
-#+MACRO: version 2.9.1
+#+MACRO: version 2.10.0
 #+MACRO: kbd (eval (org-texinfo-kbd-macro $1))
 #+TEXINFO_FILENAME: casual.info
 #+TEXINFO_CLASS: casual
@@ -117,7 +117,7 @@ For Transient menus that offer setting different values, 
the following bindings
 
   Clears the set and saved values of the active transient.
 
-#+TEXINFO: @majorheading References
+#+TEXINFO: @subheading References
 
 - [[info:transient#Aborting and Resuming Transients]]
   
@@ -148,6 +148,7 @@ Configuration of a particular Casual user interface is 
performed per mode. Go to
 - [[#calendar-install][Calendar]]
 - [[#compile-install][Compile (Grep)]]
 - [[#dired-install][Dired]]
+- [[#ediff-install][Ediff]]
 - [[#editkit-install][EditKit]]
 - [[#elisp-install][Elisp]]
 - [[#eshell-install][Eshell]]
@@ -193,25 +194,25 @@ That said, if you have used ~:ensure t~ to install a 
superseded package, you /mu
 
 The Casual menus share common user experience (UX) conventions to facilitate 
usability. This section details this.
 
-#+TEXINFO: @majorheading Common Menu Navigation
+#+TEXINFO: @subheading Common Menu Navigation
 
 Casual organizes a mode's command set into a menu hierarchy. As the user 
descends down different menu levels, the user is given the option to dismiss 
the current menu or to dismiss completely all menu levels descended. Transient 
provides a standard convention for menu dismissal via the {{{kbd(C-g)}}} 
(~transient-quit-one~) and {{{kbd(C-q)}}} (~transient-quit-all~) commands. By 
default Transient does not display these commands in the menu though, rather 
they make the assumption that the u [...]
 
 #+VINDEX: casual-lib-hide-navigation
 In contrast, Casual makes these bindings explicitly known to user by 
displaying them at the bottom of the menu. Users who wish to have them hidden 
can set the customizable variable ~casual-lib-hide-navigation~ to a non-nil 
value. This can be changed from a Casual mode-specific settings menu.
 
-#+TEXINFO: @majorheading Settings Menu
+#+TEXINFO: @subheading Settings Menu
 
 Most all Casual main menus support invoking a mode-specific settings menu via 
the binding {{{kbd(\,)}}}.
 
-#+TEXINFO: @majorheading Unicode Symbol Support
+#+TEXINFO: @subheading Unicode Symbol Support
 #+VINDEX: casual-lib-use-unicode
 
 The customizable variable named ~casual-lib-use-unicode~ which when non-nil 
will inform Casual menus to use Unicode symbol labels whenever supported. This 
can be changed from Casual mode-specific settings menu.
 
 If ~casual-lib-use-unicode~ is set to ~t~, it is also recommended that the 
variable ~transient-align-variable-pitch~ also be set to ~t~.
 
-#+TEXINFO: @majorheading Common Key Bindings
+#+TEXINFO: @subheading Common Key Bindings
 
 Listed below are keybindings which are common among Casual menus. 
 
@@ -236,7 +237,7 @@ Listed below are keybindings which are common among Casual 
menus.
   For modes that organize items into sections, navigation to a next or 
previous section is bound to {{{kbd([)}}} and {{{kbd(])}}} respectively.
 
 
-#+TEXINFO: @majorheading Label Conventions
+#+TEXINFO: @subheading Label Conventions
 
 Casual annotates menu labels with the following conventions:
 
@@ -260,6 +261,7 @@ The following modes are supported by Casual:
 #+INCLUDE: "./calendar.org" :minlevel 2
 #+INCLUDE: "./compile.org" :minlevel 2
 #+INCLUDE: "./dired.org" :minlevel 2
+#+INCLUDE: "./ediff.org" :minlevel 2
 #+INCLUDE: "./editkit.org" :minlevel 2
 #+INCLUDE: "./elisp.org" :minlevel 2
 #+INCLUDE: "./eshell.org" :minlevel 2
diff --git a/docs/casual.texi b/docs/casual.texi
index 678806b0cb..9ce648d77f 100644
--- a/docs/casual.texi
+++ b/docs/casual.texi
@@ -20,7 +20,7 @@ Copyright © 2024-2025 Charles Y@. Choi
 @finalout
 @titlepage
 @title Casual User Guide
-@subtitle for version 2.9.1
+@subtitle for version 2.10.0
 @author Charles Y@. Choi (@email{kickingvegas@@gmail.com})
 @page
 @vskip 0pt plus 1filll
@@ -33,7 +33,7 @@ Copyright © 2024-2025 Charles Y@. Choi
 @node Top
 @top Casual User Guide
 
-Version: 2.9.1
+Version: 2.10.0
 
 Casual is a project to re-imagine the primary user interface for Emacs using 
keyboard-driven menus.
 
@@ -106,6 +106,7 @@ Casual Modes
 * Calendar::
 * Compile::
 * Dired::
+* Ediff::
 * EditKit::
 * Elisp::
 * Eshell::
@@ -181,6 +182,18 @@ Dired Usage
 * Dired Link::
 * Dired Unicode Symbol Support::
 
+Ediff
+
+* Ediff Install::
+* Ediff Usage::
+
+Ediff Usage
+
+* Ediff Basic Operation::
+* Comparing a Version-Controlled File::
+* Resolving a Merge Conflict::
+* Ediff Unicode Symbol Support::
+
 EditKit
 
 * EditKit Install::
@@ -384,7 +397,7 @@ Saves the value of the active transient persistently across 
Emacs sessions.
 Clears the set and saved values of the active transient.
 @end itemize
 
-@majorheading References
+@subheading References
 
 @itemize
 @item
@@ -435,6 +448,8 @@ Configuration of a particular Casual user interface is 
performed per mode. Go to
 @item
 @ref{Dired Install, , Dired}
 @item
+@ref{Ediff Install, , Ediff}
+@item
 @ref{EditKit Install, , EditKit}
 @item
 @ref{Elisp Install, , Elisp}
@@ -512,25 +527,25 @@ That said, if you have used @code{:ensure t} to install a 
superseded package, yo
 
 The Casual menus share common user experience (UX) conventions to facilitate 
usability. This section details this.
 
-@majorheading Common Menu Navigation
+@subheading Common Menu Navigation
 
 Casual organizes a mode's command set into a menu hierarchy. As the user 
descends down different menu levels, the user is given the option to dismiss 
the current menu or to dismiss completely all menu levels descended. Transient 
provides a standard convention for menu dismissal via the @kbd{C-g} 
(@code{transient-quit-one}) and @kbd{C-q} (@code{transient-quit-all}) commands. 
By default Transient does not display these commands in the menu though, rather 
they make the assumption that the u [...]
 
 @vindex casual-lib-hide-navigation
 In contrast, Casual makes these bindings explicitly known to user by 
displaying them at the bottom of the menu. Users who wish to have them hidden 
can set the customizable variable @code{casual-lib-hide-navigation} to a 
non-nil value. This can be changed from a Casual mode-specific settings menu.
 
-@majorheading Settings Menu
+@subheading Settings Menu
 
 Most all Casual main menus support invoking a mode-specific settings menu via 
the binding @kbd{,}.
 
-@majorheading Unicode Symbol Support
+@subheading Unicode Symbol Support
 @vindex casual-lib-use-unicode
 
 The customizable variable named @code{casual-lib-use-unicode} which when 
non-nil will inform Casual menus to use Unicode symbol labels whenever 
supported. This can be changed from Casual mode-specific settings menu.
 
 If @code{casual-lib-use-unicode} is set to @code{t}, it is also recommended 
that the variable @code{transient-align-variable-pitch} also be set to @code{t}.
 
-@majorheading Common Key Bindings
+@subheading Common Key Bindings
 
 Listed below are keybindings which are common among Casual menus. 
 
@@ -562,7 +577,7 @@ For modes that organize items into sections, navigation to 
a next or previous se
 @end itemize
 
 
-@majorheading Label Conventions
+@subheading Label Conventions
 
 Casual annotates menu labels with the following conventions:
 
@@ -594,6 +609,7 @@ The following modes are supported by Casual:
 * Calendar::
 * Compile::
 * Dired::
+* Ediff::
 * EditKit::
 * Elisp::
 * Eshell::
@@ -665,29 +681,29 @@ Set a timer, get almanac info.
 @end table
 
 
-@majorheading Operating on Headlines
+@subheading Operating on Headlines
 Use “@kbd{o} Operations›” from @code{casual-agenda-tmenu} to change a 
headline's attributes such as TODO state, scheduling, tags, and priority. The 
following menu will be displayed.
 
 @image{images/casual-agenda-operations-screenshot,,,,png}
 
 
-@majorheading Marking Headlines
+@subheading Marking Headlines
 
 Use “@kbd{M} Mark›” menu from @code{casual-agenda-tmenu} to mark different 
headlines and perform a bulk action on them. 
 
 @image{images/casual-agenda-mark-screenshot,,,,png}
 
-@majorheading Almanac
+@subheading Almanac
 Get sunrise/sunset times, lunar cycle dates, and holidays with respect to a 
date via the “@kbd{l} Almanac›” menu from @code{casual-agenda-tmenu}.
 
 @image{images/casual-agenda-almanac-screenshot,,,,png}
 
-@majorheading Changing Modes and Settings
+@subheading Changing Modes and Settings
 Agenda views have different display modes and behavior that can be modified 
from the “@kbd{,} Settings›” menu from @code{casual-agenda-tmenu}.
 
 @image{images/casual-agenda-settings-screenshot,,,,png}
 
-@majorheading Agenda Unicode Symbol Support
+@subheading Agenda Unicode Symbol Support
 By enabling “@kbd{u} Use Unicode Settings” from the Settings menu, Casual 
Agenda will use Unicode symbols as appropriate in its menus.
 
 @image{images/casual-agenda-unicode-screenshot,,,,png}
@@ -1006,7 +1022,7 @@ Commands to navigate and control the display of the table 
layout for bookmarks.
 @end table
 
 
-@majorheading Unicode Symbol Support
+@subheading Unicode Symbol Support
 
 @image{images/casual-bookmarks-unicode-screenshot,,,,png}
 
@@ -1101,7 +1117,7 @@ Entry point for sub-menus of commands organized into 
different classes of functi
 Entry point for sub-menus of commands to configure Calc settings.
 @end table
 
-@majorheading Calc Basics
+@subheading Calc Basics
 
 It helps to know some basics about Calc.
 
@@ -1398,7 +1414,7 @@ If the output window is from a Grep command, 
@code{casual-compile-tmenu} will ad
 @image{images/casual-compile-grep-screenshot,,,,png}
 
 
-@majorheading Compile Mode Unicode Symbol Support
+@subheading Compile Mode Unicode Symbol Support
 
 By enabling “@kbd{u} Use Unicode Symbols” from the Settings menu, Casual 
Compile will use Unicode symbols as appropriate in its menus.
 
@@ -1486,7 +1502,7 @@ Included is a standard keymap for Dired sorting commands 
(@code{casual-dired-sor
 @end lisp
 
 
-@majorheading Configuration
+@subheading Configuration
 
 As Dired has been around for a long time, the different ways of configuring it 
are myriad. Described below is a configuration used by the author that is 
consistent with the bindings used in Casual Dired.
 
@@ -1528,7 +1544,7 @@ As Dired has been around for a long time, the different 
ways of configuring it a
 (keymap-set image-dired-thumbnail-mode-map "p" #'image-dired-display-previous)
 @end lisp
 
-@majorheading Dired Variables
+@subheading Dired Variables
 
 The Casual Dired main menu offers “@kbd{,} Settings›” to customize a set of 
commonly used Dired variables. 
 
@@ -1636,6 +1652,156 @@ By enabling “@kbd{u} Use Unicode Symbols” from the 
Settings menu, Casual Dir
 
 To ensure proper layout and spacing, your default typeface should be 
fixed-width or monospace and @strong{must} support the Unicode symbols used.
 
+@node Ediff
+@section Ediff
+
+@cindex Ediff
+@vindex casual-ediff-tmenu
+
+Casual Ediff is a user interface for Ediff (@ref{Top,Ediff,,ediff,}), a visual 
interface for the Unix diff and patch utilities. Casual Ediff strives to 
improve the usability of Ediff by simplifying the following workflows:
+
+@itemize
+@item
+Comparing a modified and uncommitted version-controlled file with its most 
recent commit.
+@item
+Resolving a merge conflicted file.
+@end itemize
+
+Notable features of Casual Ediff include:
+
+@itemize
+@item
+Context aware menu items.
+@item
+Menu design tuned for side-by-side comparison.
+@item
+For a merge conflict, the ability to resolve using both the conflicting diff 
versions.
+@end itemize
+
+Shown below is screenshot of the Casual Ediff in action for a 
version-controlled file.
+
+@image{images/casual-ediff-screenshot,,,,png}
+
+@menu
+* Ediff Install::
+* Ediff Usage::
+@end menu
+
+@node Ediff Install
+@subsection Ediff Install
+
+@cindex Ediff Install
+
+In your initialization file, bind the Transient @code{casual-ediff-tmenu} to 
your key binding of preference.
+
+@lisp
+(casual-ediff-install) ; run this to enable Casual Ediff
+(add-hook 'ediff-keymap-setup-hook
+          (lambda ()
+            (keymap-set ediff-mode-map "C-o" #'casual-ediff-tmenu)))
+@end lisp
+
+If the current buffer is loaded with a version-controlled file, then the 
difference between that buffer's content with its most recent commit can be 
seen with the command @code{casual-ediff-revision}. It is often convenient to 
bind this command. Shown below is an example of such a binding.
+
+@lisp
+(keymap-global-set "<f15>" #'casual-ediff-revision)
+@end lisp
+
+Users who wish to call @code{casual-ediff-revision} via mouse can use the 
command @code{casual-ediff-revision-from-menu}. An example of its use is shown 
in the source example below (note this is a code fragment). 
+
+@lisp
+(easy-menu-add-item
+     menu nil
+     ["Ediff revision…"
+      casual-ediff-revision-from-menu
+      :visible (and (bound-and-true-p buffer-file-name)
+                    (vc-registered (buffer-file-name)))
+      :help "Ediff this file with revision"])
+@end lisp
+
+
+@subsubheading Ediff Variables
+
+Casual Ediff recommends the following variables be set as follows:
+
+@multitable {aaaaaaaaaaaaaaaaaaaaaaaaaaa} {aaaaaaaaaaaaaaaaaaaaaaaaa}
+@headitem Variable
+@tab Value
+@item ediff-keep-variants
+@tab nil
+@item ediff-window-setup-function
+@tab ediff-setup-windows-plain
+@item ediff-split-window-function
+@tab split-window-horizontally
+@end multitable
+
+@node Ediff Usage
+@subsection Ediff Usage
+
+@cindex Ediff Usage
+
+@image{images/casual-ediff-basic-screenshot,,,,png}
+
+Casual Ediff (@code{casual-ediff-tmenu}) is invoked from the Ediff control 
window, typically using the binding @kbd{C-o} (or your binding of preference).
+
+The menu comprises of sections laid out horizontally that correspond to source 
of the diffs. The number of sections and their contents are context-dependent 
on the type of content to be compared.
+
+The following sections are offered in the menu:
+
+@table @asis
+@item A
+Context-dependent commands for Ediff buffer A@.
+@item B
+Context-dependent commands for Ediff buffer B@.
+@item C
+Context-dependent commands for Ediff buffer C, if available.
+@item Diff
+Ediff commands not related to a specific buffer.
+@end table
+
+Navigation between diffs are bound to the @kbd{p} and @kbd{n} keys for 
previous and next diff respectively. 
+
+@menu
+* Ediff Basic Operation::
+* Comparing a Version-Controlled File::
+* Resolving a Merge Conflict::
+* Ediff Unicode Symbol Support::
+@end menu
+
+@node Ediff Basic Operation
+@subsubsection Ediff Basic Operation
+
+@image{images/casual-ediff-basic-screenshot,,,,png}
+
+If two writeable files are compared, Casual Ediff provides the ability to 
write diffs from either side A or B to each other. Unwanted changes to a side 
can be restored.
+
+@node Comparing a Version-Controlled File
+@subsubsection Comparing a Version-Controlled File
+
+@image{images/casual-ediff-screenshot,,,,png}
+
+Casual Ediff provides a dedicated command to compare a version-controlled file 
with its latest commit: @code{casual-ediff-revision}. This command 
short-circuits the prompts asked for by the command  @code{ediff-revision} 
which asks for the source file and the revision to compare it to. Call 
@code{casual-ediff-revision} from the window of a version-controlled file, 
typically by key binding of preference.
+
+The A side will hold the last committed revision of a file. The B side will 
hold current source file.
+
+Users desiring a mouse-driven approach to calling @code{casual-ediff-revision} 
should use the command @code{casual-ediff-revision-from-menu} invoked from 
either the top-level or context menu.
+
+Note that both @code{casual-ediff-revision} and 
@code{casual-ediff-revision-from-menu} will checkpoint the window configuration 
before invoking the Ediff session and restore it after the session is complete.
+
+@node Resolving a Merge Conflict
+@subsubsection Resolving a Merge Conflict
+
+@image{images/casual-ediff-merge-conflict,,,,png}
+
+Magit offers a means of invoking Ediff to resolve a merge conflict via the 
@kbd{e} binding from the @code{magit-status} (@ref{Status Buffer,(magit) Status 
Buffer,,magit,}) window.
+
+Calling @code{casual-ediff-tmenu} will show the menu shown above. Note that in 
the C section, there are commands to merge both the variant A and variant B 
diffs using the bindings @kbd{mab} or @kbd{mba}, depending on the desired order 
the diffs are to be merged.
+
+@node Ediff Unicode Symbol Support
+@subsubsection Ediff Unicode Symbol Support
+
+By enabling “@kbd{u} Use Unicode Symbols” from the Settings menu, Casual Ediff 
will use Unicode symbols as appropriate in its menus.
+
 @node EditKit
 @section EditKit
 
@@ -1735,21 +1901,21 @@ This menu contains commands and sub-menus related to 
editing text.
 
 Depending on the buffer mode, text can be operated on with different 
granularity as words, sentences, paragraphs, balanced expressions, functions 
(defuns). The following sub-menus illustrate what operations can be done on the 
different text granularity.
 
-@majorheading Mark›
+@subheading Mark›
 @cindex Mark commands
 @vindex casual-editkit-mark-tmenu
 Text can be marked with different granularity with this menu. Note that 
marking functions (Defun) is only supported for modes derived from 
@code{prog-mode}. 
 
 @image{images/casual-editkit-mark-screenshot,,,,png}
 
-@majorheading Copy›
+@subheading Copy›
 @cindex Copy commands
 @vindex casual-editkit-copy-tmenu
 Text can be copied with different granularity with this menu.
 
 @image{images/casual-editkit-copy-screenshot,,,,png}
 
-@majorheading Kill (Cut)›
+@subheading Kill (Cut)›
 @cindex Kill commands
 @cindex Cut commands
 @vindex casual-editkit-kill-tmenu
@@ -1757,42 +1923,42 @@ Text can be cut (killed) with different granularity 
with this menu.
 
 @image{images/casual-editkit-kill-screenshot,,,,png}
 
-@majorheading Move›
+@subheading Move›
 @cindex Move commands
 @vindex casual-editkit-move-tmenu
 Text can be moved forwards or backwards with different granularity with this 
menu. Note that selecting a granularity will raise another menu to allow 
selection of direction (forward, backward) the text is to be moved. To enable 
repeat operation, that menu is persisted and must be dismissed either with 
either @code{C-q} (dismiss all) or  @code{C-g} (dismiss to previous menu).
 
 @image{images/casual-editkit-move-screenshot,,,,png}
 
-@majorheading Transpose›
+@subheading Transpose›
 @cindex Transpose commands
 @vindex casual-editkit-transpose-tmenu
 Text can be transposed with different granularity with this menu. 
 
 @image{images/casual-editkit-transpose-screenshot,,,,png}
 
-@majorheading Transform›
+@subheading Transform›
 @cindex Transform commands
 @vindex casual-editkit-transform-tmenu
 Text can be transformed with different granularity with this menu. Supported 
transformations are capitalization, lower and upper casing of text.
 
 @image{images/casual-editkit-transform-screenshot,,,,png}
 
-@majorheading Delete›
+@subheading Delete›
 @cindex Delete commands
 @vindex casual-editkit-delete-tmenu
 Operations involving text deletion are included in this menu, including 
joining lines and zapping to a character.
 
 @image{images/casual-editkit-delete-screenshot,,,,png}
 
-@majorheading Sort›
+@subheading Sort›
 @cindex Sort commands
 @vindex casual-editkit-sort-tmenu
 Sorting operations on different sections of text are supported, as well as 
support for sorting off a field. Press @code{?} or @code{C-h} to get help for a 
specific command.
 
 @image{images/casual-editkit-sort-screenshot,,,,png}
 
-@majorheading Reformat›
+@subheading Reformat›
 @cindex Reformat commands
 @vindex casual-editkit-reformat-tmenu
 Commands to reformat text such as filling, centering, and repunctuating 
sentences are provided here. Press @code{?} or @code{C-h} to get help for a 
specific command.
@@ -1800,7 +1966,7 @@ Commands to reformat text such as filling, centering, and 
repunctuating sentence
 @image{images/casual-editkit-reformat-screenshot,,,,png}
 
 
-@majorheading Rectangle›
+@subheading Rectangle›
 @cindex Rectangle commands
 @vindex casual-editkit-rectangle-tmenu
 
@@ -1821,7 +1987,7 @@ If the variable @code{casual-lib-use-unicode} is set to 
@code{t}, then Unicode s
 
 @image{images/casual-editkit-window-unicode-screenshot,,,,png}
 
-@majorheading Window Deletion
+@subheading Window Deletion
 @cindex Window deletion
 @vindex casual-editkit-window-delete-tmenu
 This menu provides support for deleting windows.
@@ -1990,14 +2156,14 @@ Commands for Sexp (balanced expression) navigation.
 @end table
 
 
-@majorheading Edebug Support
+@subheading Edebug Support
 @cindex Edebug
 
 Using the prefix @kbd{C-u} when @code{casual-elisp-tmenu} is raised will 
change the “@kbd{d} Defunⁿ” menu item to “@kbd{d} Edebugⁿ” to support 
instrumenting a function for Edebug (@ref{Edebug,,,elisp,}).
 
 @image{images/casual-elisp-edebug-screenshot,,,,png}
 
-@majorheading Elisp Unicode Symbol Support
+@subheading Elisp Unicode Symbol Support
 
 By enabling “@kbd{u} Use Unicode Symbols” from the Settings menu, Casual Elisp 
will use Unicode symbols as appropriate in its menus.
 
@@ -2055,7 +2221,7 @@ Signal commands to send to the process. This section is 
only visible when a proc
 @image{images/casual-eshell-process-screenshot,,,,png}
 
 
-@majorheading Eshell Unicode Symbol Support
+@subheading Eshell Unicode Symbol Support
 
 By enabling “@kbd{u} Use Unicode Symbols” from the Settings menu, Casual 
Eshell will use Unicode symbols as appropriate in its menus.
 
@@ -2155,7 +2321,7 @@ If available, then open this help topic in 
@uref{info.org, Info}.
 Show the Elisp source. If the help displayed is for a customizable variable, 
then show a customize menu item.
 @end table
 
-@majorheading Help Mode Unicode Symbol Support
+@subheading Help Mode Unicode Symbol Support
 
 By enabling “@kbd{u} Use Unicode Symbols” from the Settings menu, Casual Man 
will use Unicode symbols as appropriate in its menus.
 @uref{../README.org, ❮ Back to Casual}
@@ -2518,7 +2684,7 @@ Note that if the image file has been modified, the resize 
interface will be disa
 
 @image{images/casual-image-resize-screenshot,,,,png}
 
-@majorheading Image Resize Options
+@subheading Image Resize Options
 @itemize
 @item
 (g) Geometry - 
@uref{https://imagemagick.org/script/command-line-processing.php#geometry, 
ImageMagick specifier} for the resize geometry.
@@ -2708,7 +2874,7 @@ Command related to structurally navigating an Info 
document. Note that these com
 Miscellaneous commands for working with an Info document. Included are 
commands for bookmarks, copying the current node name, and cloning the buffer.
 @end table
 
-@majorheading Info Unicode Symbol Support
+@subheading Info Unicode Symbol Support
 By enabling “@kbd{u} Use Unicode Symbols” from the Settings menu, Casual Info 
will use Unicode symbols as appropriate in its menus.
 
 @node I-Search
@@ -2956,11 +3122,11 @@ Jump to other Man pages referenced in the current Man 
page.
 If the Man page reader is configured to display all manual pages for a given 
search key, navigation commands for multiple pages is provided.
 @end table
 
-@majorheading Options Navigation
+@subheading Options Navigation
 
 @code{casual-man-tmenu} provides the menu item @kbd{o} which runs the command 
@code{casual-man-occur-options}. This will invoke @code{occur} with a regexp 
that searches for command line options (for example, ``--foo'', ``-a'') that 
can be navigated via the @code{occur} interface.
 
-@majorheading Man Settings
+@subheading Man Settings
 
 By default, the Man page reader will @emph{not} display all manual pages for 
given search key. This can be changed in the Settings menu 
@code{casual-man-settings-tmenu} that can be invoked by pressing @kbd{,} in 
@code{casual-man-tmenu}. 
 
@@ -2970,7 +3136,7 @@ Press ‘s’ and configure @code{Man-switches} to have the 
value ``-a'' to get
 @image{images/casual-man-settings,,,,png}
 
 
-@majorheading Man Unicode Symbol Support
+@subheading Man Unicode Symbol Support
 By enabling “@kbd{u} Use Unicode Symbols” from the Settings menu, Casual Man 
will use Unicode symbols as appropriate in its menus.
 
 @node RE-Builder
@@ -3017,7 +3183,7 @@ If multiple sub-expressions are in the regexp pattern, 
then they can be observed
 
 If the regexp pattern entered in ``✳︎RE-Builder✳︎'' finds multiple matches, a 
match can be navigated to via the  “@kbd{p} Previous” and “@kbd{n} Next” menu 
items.
 
-@majorheading Exporting the Regexp Pattern
+@subheading Exporting the Regexp Pattern
 Once a desired regexp pattern is defined, there are two menu items that can be 
used to export (copy) it to the kill-ring for further use.
 
 @itemize
@@ -3045,13 +3211,13 @@ This can only be used when the regexp syntax is set to 
@code{string}.
 @end itemize
 @end itemize
 
-@majorheading Regexp Syntax Help
+@subheading Regexp Syntax Help
 The menu item @kbd{i} will invoke the Info page for regexp syntax with respect 
to the current syntax type.
 
-@majorheading Quitting RE-Builder
+@subheading Quitting RE-Builder
 Select “@kbd{q} Quit” to exit the RE-Builder tool. 
 
-@majorheading RE-Builder Unicode Symbol Support
+@subheading RE-Builder Unicode Symbol Support
 By enabling “@kbd{u} Use Unicode Symbols” from the Settings menu, Casual 
RE-Builder will use Unicode symbols as appropriate in its menus. The following 
mapping is shown in the table below:
 
 @multitable {aaaaaaaaa} {aaaaaaaa} {aaaaaaa}
diff --git a/docs/compile.org b/docs/compile.org
index 167cc2a505..44cfce36bb 100644
--- a/docs/compile.org
+++ b/docs/compile.org
@@ -79,7 +79,7 @@ If the output window is from a Grep command, 
~casual-compile-tmenu~ will adjust
 [[file:images/casual-compile-grep-screenshot.png]]
 
   
-#+TEXINFO: @majorheading Compile Mode Unicode Symbol Support
+#+TEXINFO: @subheading Compile Mode Unicode Symbol Support
 
 By enabling “{{{kbd(u)}}} Use Unicode Symbols” from the Settings menu, Casual 
Compile will use Unicode symbols as appropriate in its menus.
 
diff --git a/docs/dired.org b/docs/dired.org
index cc0042c0df..37254e9b1d 100644
--- a/docs/dired.org
+++ b/docs/dired.org
@@ -56,7 +56,7 @@ Included is a standard keymap for Dired sorting commands 
(~casual-dired-sort-men
 #+end_src
 
 
-#+TEXINFO: @majorheading Configuration
+#+TEXINFO: @subheading Configuration
 
 As Dired has been around for a long time, the different ways of configuring it 
are myriad. Described below is a configuration used by the author that is 
consistent with the bindings used in Casual Dired.
 
@@ -98,7 +98,7 @@ As Dired has been around for a long time, the different ways 
of configuring it a
   (keymap-set image-dired-thumbnail-mode-map "p" 
#'image-dired-display-previous)
 #+end_src
 
-#+TEXINFO: @majorheading Dired Variables
+#+TEXINFO: @subheading Dired Variables
 
 The Casual Dired main menu offers “{{{kbd(\,)}}} Settings›” to customize a set 
of commonly used Dired variables. 
 
diff --git a/docs/ediff.org b/docs/ediff.org
new file mode 100644
index 0000000000..19b9ae9c24
--- /dev/null
+++ b/docs/ediff.org
@@ -0,0 +1,113 @@
+* Ediff
+#+CINDEX: Ediff
+#+VINDEX: casual-ediff-tmenu
+
+Casual Ediff is a user interface for Ediff ([[info:ediff#Top][Ediff]]), a 
visual interface for the Unix diff and patch utilities. Casual Ediff strives to 
improve the usability of Ediff by simplifying the following workflows:
+
+- Comparing a modified and uncommitted version-controlled file with its most 
recent commit.
+- Resolving a merge conflicted file.
+
+Notable features of Casual Ediff include:
+
+- Context aware menu items.
+- Menu design tuned for side-by-side comparison.
+- For a merge conflict, the ability to resolve using both the conflicting diff 
versions.
+
+Shown below is screenshot of the Casual Ediff in action for a 
version-controlled file.
+
+[[file:images/casual-ediff-screenshot.png]]
+
+
+** Ediff Install
+:PROPERTIES:
+:CUSTOM_ID: ediff-install
+:END:
+
+#+CINDEX: Ediff Install
+
+In your initialization file, bind the Transient ~casual-ediff-tmenu~ to your 
key binding of preference.
+
+#+begin_src elisp :lexical no
+  (casual-ediff-install) ; run this to enable Casual Ediff
+  (add-hook 'ediff-keymap-setup-hook
+            (lambda ()
+              (keymap-set ediff-mode-map "C-o" #'casual-ediff-tmenu)))
+#+end_src
+
+If the current buffer is loaded with a version-controlled file, then the 
difference between that buffer's content with its most recent commit can be 
seen with the command ~casual-ediff-revision~. It is often convenient to bind 
this command. Shown below is an example of such a binding.
+
+#+begin_src elisp :lexical no
+  (keymap-global-set "<f15>" #'casual-ediff-revision)
+#+end_src
+
+Users who wish to call ~casual-ediff-revision~ via mouse can use the command 
~casual-ediff-revision-from-menu~. An example of its use is shown in the source 
example below (note this is a code fragment). 
+
+#+begin_src elisp :lexical no
+  (easy-menu-add-item
+       menu nil
+       ["Ediff revision…"
+        casual-ediff-revision-from-menu
+        :visible (and (bound-and-true-p buffer-file-name)
+                      (vc-registered (buffer-file-name)))
+        :help "Ediff this file with revision"])
+#+end_src
+
+
+#+TEXINFO: @subsubheading Ediff Variables
+
+Casual Ediff recommends the following variables be set as follows:
+
+| Variable                    | Value                     |
+|-----------------------------+---------------------------|
+| ediff-keep-variants         | nil                       |
+| ediff-window-setup-function | ediff-setup-windows-plain |
+| ediff-split-window-function | split-window-horizontally |
+
+** Ediff Usage
+#+CINDEX: Ediff Usage
+
+[[file:images/casual-ediff-basic-screenshot.png]]
+
+Casual Ediff (~casual-ediff-tmenu~) is invoked from the Ediff control window, 
typically using the binding {{{kbd(C-o)}}} (or your binding of preference).
+
+The menu comprises of sections laid out horizontally that correspond to source 
of the diffs. The number of sections and their contents are context-dependent 
on the type of content to be compared.
+
+The following sections are offered in the menu:
+
+- A :: Context-dependent commands for Ediff buffer A.
+- B :: Context-dependent commands for Ediff buffer B.
+- C :: Context-dependent commands for Ediff buffer C, if available.
+- Diff :: Ediff commands not related to a specific buffer.
+  
+Navigation between diffs are bound to the {{{kbd(p)}}} and {{{kbd(n)}}} keys 
for previous and next diff respectively. 
+
+*** Ediff Basic Operation
+
+[[file:images/casual-ediff-basic-screenshot.png]]
+
+If two writeable files are compared, Casual Ediff provides the ability to 
write diffs from either side A or B to each other. Unwanted changes to a side 
can be restored. 
+
+*** Comparing a Version-Controlled File
+
+[[file:images/casual-ediff-screenshot.png]]
+
+Casual Ediff provides a dedicated command to compare a version-controlled file 
with its latest commit: ~casual-ediff-revision~. This command short-circuits 
the prompts asked for by the command  ~ediff-revision~ which asks for the 
source file and the revision to compare it to. Call ~casual-ediff-revision~ 
from the window of a version-controlled file, typically by key binding of 
preference.
+
+The A side will hold the last committed revision of a file. The B side will 
hold current source file.
+
+Users desiring a mouse-driven approach to calling ~casual-ediff-revision~ 
should use the command ~casual-ediff-revision-from-menu~ invoked from either 
the top-level or context menu.
+
+Note that both ~casual-ediff-revision~ and ~casual-ediff-revision-from-menu~ 
will checkpoint the window configuration before invoking the Ediff session and 
restore it after the session is complete.
+
+*** Resolving a Merge Conflict
+
+[[file:images/casual-ediff-merge-conflict.png]]
+
+Magit offers a means of invoking Ediff to resolve a merge conflict via the 
{{{kbd(e)}}} binding from the ~magit-status~ ([[info:magit#Status 
Buffer][(magit) Status Buffer]]) window.
+
+Calling ~casual-ediff-tmenu~ will show the menu shown above. Note that in the 
C section, there are commands to merge both the variant A and variant B diffs 
using the bindings {{{kbd(mab)}}} or {{{kbd(mba)}}}, depending on the desired 
order the diffs are to be merged.
+
+
+*** Ediff Unicode Symbol Support
+By enabling “{{{kbd(u)}}} Use Unicode Symbols” from the Settings menu, Casual 
Ediff will use Unicode symbols as appropriate in its menus.
+
diff --git a/docs/editkit.org b/docs/editkit.org
index 2021c86a51..c5335465a5 100644
--- a/docs/editkit.org
+++ b/docs/editkit.org
@@ -68,21 +68,21 @@ This menu contains commands and sub-menus related to 
editing text.
 
 Depending on the buffer mode, text can be operated on with different 
granularity as words, sentences, paragraphs, balanced expressions, functions 
(defuns). The following sub-menus illustrate what operations can be done on the 
different text granularity.
 
-#+TEXINFO: @majorheading Mark›
+#+TEXINFO: @subheading Mark›
 #+CINDEX: Mark commands
 #+VINDEX: casual-editkit-mark-tmenu
 Text can be marked with different granularity with this menu. Note that 
marking functions (Defun) is only supported for modes derived from ~prog-mode~. 
 
 [[file:images/casual-editkit-mark-screenshot.png]]
 
-#+TEXINFO: @majorheading Copy›
+#+TEXINFO: @subheading Copy›
 #+CINDEX: Copy commands
 #+VINDEX: casual-editkit-copy-tmenu
 Text can be copied with different granularity with this menu.
 
 [[file:images/casual-editkit-copy-screenshot.png]]
 
-#+TEXINFO: @majorheading Kill (Cut)›
+#+TEXINFO: @subheading Kill (Cut)›
 #+CINDEX: Kill commands
 #+CINDEX: Cut commands
 #+VINDEX: casual-editkit-kill-tmenu
@@ -90,42 +90,42 @@ Text can be cut (killed) with different granularity with 
this menu.
 
 [[file:images/casual-editkit-kill-screenshot.png]]
 
-#+TEXINFO: @majorheading Move›
+#+TEXINFO: @subheading Move›
 #+CINDEX: Move commands
 #+VINDEX: casual-editkit-move-tmenu
 Text can be moved forwards or backwards with different granularity with this 
menu. Note that selecting a granularity will raise another menu to allow 
selection of direction (forward, backward) the text is to be moved. To enable 
repeat operation, that menu is persisted and must be dismissed either with 
either ~C-q~ (dismiss all) or  ~C-g~ (dismiss to previous menu).
 
 [[file:images/casual-editkit-move-screenshot.png]]
 
-#+TEXINFO: @majorheading Transpose›
+#+TEXINFO: @subheading Transpose›
 #+CINDEX: Transpose commands
 #+VINDEX: casual-editkit-transpose-tmenu
 Text can be transposed with different granularity with this menu. 
 
 [[file:images/casual-editkit-transpose-screenshot.png]]
 
-#+TEXINFO: @majorheading Transform›
+#+TEXINFO: @subheading Transform›
 #+CINDEX: Transform commands
 #+VINDEX: casual-editkit-transform-tmenu
 Text can be transformed with different granularity with this menu. Supported 
transformations are capitalization, lower and upper casing of text.
 
 [[file:images/casual-editkit-transform-screenshot.png]]
 
-#+TEXINFO: @majorheading Delete›
+#+TEXINFO: @subheading Delete›
 #+CINDEX: Delete commands
 #+VINDEX: casual-editkit-delete-tmenu
 Operations involving text deletion are included in this menu, including 
joining lines and zapping to a character.
 
 [[file:images/casual-editkit-delete-screenshot.png]]
 
-#+TEXINFO: @majorheading Sort›
+#+TEXINFO: @subheading Sort›
 #+CINDEX: Sort commands
 #+VINDEX: casual-editkit-sort-tmenu
 Sorting operations on different sections of text are supported, as well as 
support for sorting off a field. Press ~?~ or ~C-h~ to get help for a specific 
command.
 
 [[file:images/casual-editkit-sort-screenshot.png]]
 
-#+TEXINFO: @majorheading Reformat›
+#+TEXINFO: @subheading Reformat›
 #+CINDEX: Reformat commands
 #+VINDEX: casual-editkit-reformat-tmenu
 Commands to reformat text such as filling, centering, and repunctuating 
sentences are provided here. Press ~?~ or ~C-h~ to get help for a specific 
command.
@@ -133,7 +133,7 @@ Commands to reformat text such as filling, centering, and 
repunctuating sentence
 [[file:images/casual-editkit-reformat-screenshot.png]]
 
 
-#+TEXINFO: @majorheading Rectangle›
+#+TEXINFO: @subheading Rectangle›
 #+CINDEX: Rectangle commands
 #+VINDEX: casual-editkit-rectangle-tmenu
 
@@ -153,7 +153,7 @@ If the variable ~casual-lib-use-unicode~ is set to ~t~, 
then Unicode symbols are
 
 [[file:images/casual-editkit-window-unicode-screenshot.png]]
 
-#+TEXINFO: @majorheading Window Deletion
+#+TEXINFO: @subheading Window Deletion
 #+CINDEX: Window deletion
 #+VINDEX: casual-editkit-window-delete-tmenu
 This menu provides support for deleting windows.
diff --git a/docs/elisp.org b/docs/elisp.org
index 0c8beed86a..7fbce42eb6 100644
--- a/docs/elisp.org
+++ b/docs/elisp.org
@@ -42,14 +42,14 @@ The following sections are offered in the menu:
 - Navigate :: Commands for Sexp (balanced expression) navigation.
 
 
-#+TEXINFO: @majorheading Edebug Support
+#+TEXINFO: @subheading Edebug Support
 #+CINDEX: Edebug
 
 Using the prefix {{{kbd(C-u)}}} when ~casual-elisp-tmenu~ is raised will 
change the “{{{kbd(d)}}} Defunⁿ” menu item to “{{{kbd(d)}}} Edebugⁿ” to support 
instrumenting a function for Edebug ([[info:elisp#Edebug]]).
 
 [[file:images/casual-elisp-edebug-screenshot.png]]
 
-#+TEXINFO: @majorheading Elisp Unicode Symbol Support
+#+TEXINFO: @subheading Elisp Unicode Symbol Support
 
 By enabling “{{{kbd(u)}}} Use Unicode Symbols” from the Settings menu, Casual 
Elisp will use Unicode symbols as appropriate in its menus.
 
diff --git a/docs/eshell.org b/docs/eshell.org
index 4d948c51c1..7e9cfff49c 100644
--- a/docs/eshell.org
+++ b/docs/eshell.org
@@ -38,7 +38,7 @@ The following sections are offered in the menu:
   [[file:images/casual-eshell-process-screenshot.png]]
 
   
-#+TEXINFO: @majorheading Eshell Unicode Symbol Support
+#+TEXINFO: @subheading Eshell Unicode Symbol Support
 
 By enabling “{{{kbd(u)}}} Use Unicode Symbols” from the Settings menu, Casual 
Eshell will use Unicode symbols as appropriate in its menus.
 
diff --git a/docs/help.org b/docs/help.org
index 47c067c5e1..e950ba7206 100644
--- a/docs/help.org
+++ b/docs/help.org
@@ -57,7 +57,7 @@ The following sections are offered in the menu:
 - Info :: If available, then open this help topic in [[file:info.org][Info]].
 - Source :: Show the Elisp source. If the help displayed is for a customizable 
variable, then show a customize menu item.
   
-#+TEXINFO: @majorheading Help Mode Unicode Symbol Support
+#+TEXINFO: @subheading Help Mode Unicode Symbol Support
 
 By enabling “{{{kbd(u)}}} Use Unicode Symbols” from the Settings menu, Casual 
Man will use Unicode symbols as appropriate in its menus.
 
diff --git a/docs/htmlxref.cnf b/docs/htmlxref.cnf
index 7cc14067ba..e858e82814 100644
--- a/docs/htmlxref.cnf
+++ b/docs/htmlxref.cnf
@@ -2,3 +2,4 @@ calc            mono    
https://www.gnu.org/software/emacs/manual/html_mono/calc.html
 calc           node    
https://www.gnu.org/software/emacs/manual/html_node/calc/
 transient       node    
https://www.gnu.org/software/emacs/manual/html_node/transient/
 use-package     node    
https://www.gnu.org/software/emacs/manual/html_node/use-package/
+magit           node    https://magit.vc/manual/magit/
diff --git a/docs/image.org b/docs/image.org
index 7a550f7cc6..5e8d405c91 100644
--- a/docs/image.org
+++ b/docs/image.org
@@ -52,7 +52,7 @@ Note that if the image file has been modified, the resize 
interface will be disa
 
 [[file:images/casual-image-resize-screenshot.png]]
 
-#+TEXINFO: @majorheading Image Resize Options
+#+TEXINFO: @subheading Image Resize Options
 - (g) Geometry - 
[[https://imagemagick.org/script/command-line-processing.php#geometry][ImageMagick
 specifier]] for the resize geometry.
 - (o) Output to another file - If enabled, then the user will be prompted for 
a different output file, else it will /irreversibly/ update the current image 
file.
 - (t) Type - Specify if /adaptive/ or /interpolative/ resizing should be used. 
If nothing is specified then /standard/ resizing is used.
diff --git a/docs/images/casual-ediff-basic-screenshot.png 
b/docs/images/casual-ediff-basic-screenshot.png
new file mode 100644
index 0000000000..1d70b4834d
Binary files /dev/null and b/docs/images/casual-ediff-basic-screenshot.png 
differ
diff --git a/docs/images/casual-ediff-merge-conflict.png 
b/docs/images/casual-ediff-merge-conflict.png
new file mode 100644
index 0000000000..d0220f71ed
Binary files /dev/null and b/docs/images/casual-ediff-merge-conflict.png differ
diff --git a/docs/images/casual-ediff-screenshot.png 
b/docs/images/casual-ediff-screenshot.png
new file mode 100644
index 0000000000..45902acc31
Binary files /dev/null and b/docs/images/casual-ediff-screenshot.png differ
diff --git a/docs/info.org b/docs/info.org
index 750b96d8dc..71580bc1f6 100644
--- a/docs/info.org
+++ b/docs/info.org
@@ -67,6 +67,6 @@ The main menu for Casual Info is organized into the following 
sections:
 
 - Quick :: Miscellaneous commands for working with an Info document. Included 
are commands for bookmarks, copying the current node name, and cloning the 
buffer.
 
-#+TEXINFO: @majorheading Info Unicode Symbol Support
+#+TEXINFO: @subheading Info Unicode Symbol Support
 By enabling “{{{kbd(u)}}} Use Unicode Symbols” from the Settings menu, Casual 
Info will use Unicode symbols as appropriate in its menus. 
 
diff --git a/docs/man.org b/docs/man.org
index 4f8f0beadd..08fc4583b0 100644
--- a/docs/man.org
+++ b/docs/man.org
@@ -59,11 +59,11 @@ The following sections are offered in the menu:
 - Link :: Jump to other Man pages referenced in the current Man page.
 - Page :: If the Man page reader is configured to display all manual pages for 
a given search key, navigation commands for multiple pages is provided.
 
-#+TEXINFO: @majorheading Options Navigation
+#+TEXINFO: @subheading Options Navigation
 
 ~casual-man-tmenu~ provides the menu item {{{kbd(o)}}} which runs the command 
~casual-man-occur-options~. This will invoke ~occur~ with a regexp that 
searches for command line options (for example, "--foo", "-a") that can be 
navigated via the ~occur~ interface.
 
-#+TEXINFO: @majorheading Man Settings
+#+TEXINFO: @subheading Man Settings
 
 By default, the Man page reader will /not/ display all manual pages for given 
search key. This can be changed in the Settings menu 
~casual-man-settings-tmenu~ that can be invoked by pressing {{{kbd(\,)}}} in 
~casual-man-tmenu~. 
 
@@ -73,5 +73,5 @@ Press ‘s’ and configure ~Man-switches~ to have the value "-a" 
to get all man
 [[file:images/casual-man-settings.png]]
 
 
-#+TEXINFO: @majorheading Man Unicode Symbol Support
+#+TEXINFO: @subheading Man Unicode Symbol Support
 By enabling “{{{kbd(u)}}} Use Unicode Symbols” from the Settings menu, Casual 
Man will use Unicode symbols as appropriate in its menus.
diff --git a/docs/re-builder.org b/docs/re-builder.org
index 7bcebf0a9e..558b72c878 100644
--- a/docs/re-builder.org
+++ b/docs/re-builder.org
@@ -35,7 +35,7 @@ If multiple sub-expressions are in the regexp pattern, then 
they can be observed
 
 If the regexp pattern entered in "✳︎RE-Builder✳︎" finds multiple matches, a 
match can be navigated to via the  “{{{kbd(p)}}} Previous” and “{{{kbd(n)}}} 
Next” menu items.
 
-#+TEXINFO: @majorheading Exporting the Regexp Pattern
+#+TEXINFO: @subheading Exporting the Regexp Pattern
 Once a desired regexp pattern is defined, there are two menu items that can be 
used to export (copy) it to the kill-ring for further use.
 
 - “{{{kbd(w)}}} Interactive” will copy the regexp to the kill-ring so that it 
can be yanked in an interactive command that requires a regexp (e.g. 
~query-replace-regexp~).
@@ -48,13 +48,13 @@ Once a desired regexp pattern is defined, there are two 
menu items that can be u
   - ❗️At current this is an experimental feature. The regexp exported from 
RE-Builder may not work. If so please report an 
[[https://github.com/kickingvegas/casual-re-builder/issues][issue]] describing 
the desired regexp and the target text.
   - This can only be used when the regexp syntax is set to ~string~.    
 
-#+TEXINFO: @majorheading Regexp Syntax Help
+#+TEXINFO: @subheading Regexp Syntax Help
 The menu item {{{kbd(i)}}} will invoke the Info page for regexp syntax with 
respect to the current syntax type.
 
-#+TEXINFO: @majorheading Quitting RE-Builder
+#+TEXINFO: @subheading Quitting RE-Builder
 Select “{{{kbd(q)}}} Quit” to exit the RE-Builder tool. 
 
-#+TEXINFO: @majorheading RE-Builder Unicode Symbol Support
+#+TEXINFO: @subheading RE-Builder Unicode Symbol Support
 By enabling “{{{kbd(u)}}} Use Unicode Symbols” from the Settings menu, Casual 
RE-Builder will use Unicode symbols as appropriate in its menus. The following 
mapping is shown in the table below:
 
 | Name      | Plain    | Unicode |
diff --git a/lisp/Makefile b/lisp/Makefile
index b26f8b2433..b65da829cc 100644
--- a/lisp/Makefile
+++ b/lisp/Makefile
@@ -26,6 +26,7 @@ calc-tests                                    \
 calendar-tests                                 \
 compile-tests                                  \
 dired-tests                                    \
+ediff-tests                                    \
 editkit-tests                                  \
 elisp-tests                                    \
 eshell-tests                                   \
@@ -70,6 +71,10 @@ compile-tests:
 dired-tests:
        $(MAKE) -C $(SRC_DIR) -f Makefile-dired.make tests
 
+.PHONY: ediff-tests
+ediff-tests:
+       $(MAKE) -C $(SRC_DIR) -f Makefile-ediff.make tests
+
 .PHONY: editkit-tests
 editkit-tests:
        $(MAKE) -C $(SRC_DIR) -f Makefile-editkit.make tests
diff --git a/lisp/Makefile-agenda.make b/lisp/Makefile-agenda.make
index ad3896dd6f..8f990993d0 100644
--- a/lisp/Makefile-agenda.make
+++ b/lisp/Makefile-agenda.make
@@ -25,6 +25,7 @@ PACKAGE_PATHS=                                        \
 -L $(EMACS_ELPA_DIR)/compat-current            \
 -L $(EMACS_ELPA_DIR)/seq-current               \
 -L $(EMACS_ELPA_DIR)/transient-current         \
+-L $(EMACS_ELPA_DIR)/cond-let-current          \
 -L $(CASUAL_LIB_LISP_DIR)
 
 include Makefile--rules.make
diff --git a/lisp/Makefile-bibtex.make b/lisp/Makefile-bibtex.make
index ffc38630ca..4960bfb80a 100644
--- a/lisp/Makefile-bibtex.make
+++ b/lisp/Makefile-bibtex.make
@@ -25,6 +25,7 @@ PACKAGE_PATHS=                                        \
 -L $(EMACS_ELPA_DIR)/compat-current            \
 -L $(EMACS_ELPA_DIR)/seq-current               \
 -L $(EMACS_ELPA_DIR)/transient-current         \
+-L $(EMACS_ELPA_DIR)/cond-let-current          \
 -L $(CASUAL_LIB_LISP_DIR)
 
 include Makefile--rules.make
diff --git a/lisp/Makefile-bookmarks.make b/lisp/Makefile-bookmarks.make
index 5783f1d17c..e1716f617f 100644
--- a/lisp/Makefile-bookmarks.make
+++ b/lisp/Makefile-bookmarks.make
@@ -25,6 +25,7 @@ PACKAGE_PATHS=                                        \
 -L $(EMACS_ELPA_DIR)/compat-current            \
 -L $(EMACS_ELPA_DIR)/seq-current               \
 -L $(EMACS_ELPA_DIR)/transient-current         \
+-L $(EMACS_ELPA_DIR)/cond-let-current          \
 -L $(CASUAL_LIB_LISP_DIR)
 
 include Makefile--rules.make
diff --git a/lisp/Makefile-calc.make b/lisp/Makefile-calc.make
index 2021e916e4..8b9bb41b8e 100644
--- a/lisp/Makefile-calc.make
+++ b/lisp/Makefile-calc.make
@@ -53,6 +53,7 @@ PACKAGE_PATHS=                                        \
 -L $(EMACS_ELPA_DIR)/seq-current               \
 -L $(EMACS_ELPA_DIR)/transpose-frame-current   \
 -L $(EMACS_ELPA_DIR)/transient-current         \
+-L $(EMACS_ELPA_DIR)/cond-let-current          \
 -L $(EMACS_ELPA_DIR)/magit-current             \
 -L $(EMACS_ELPA_DIR)/magit-section-current     \
 -L $(EMACS_ELPA_DIR)/dash-current              \
diff --git a/lisp/Makefile-calendar.make b/lisp/Makefile-calendar.make
index 01caf35f33..1d80048aa2 100644
--- a/lisp/Makefile-calendar.make
+++ b/lisp/Makefile-calendar.make
@@ -27,6 +27,7 @@ PACKAGE_PATHS=                                        \
 -L $(EMACS_ELPA_DIR)/seq-current               \
 -L $(EMACS_ELPA_DIR)/transpose-frame-current   \
 -L $(EMACS_ELPA_DIR)/transient-current         \
+-L $(EMACS_ELPA_DIR)/cond-let-current          \
 -L $(EMACS_ELPA_DIR)/magit-current             \
 -L $(EMACS_ELPA_DIR)/magit-section-current     \
 -L $(EMACS_ELPA_DIR)/dash-current              \
diff --git a/lisp/Makefile-compile.make b/lisp/Makefile-compile.make
index ab09c7f592..6cf74554a0 100644
--- a/lisp/Makefile-compile.make
+++ b/lisp/Makefile-compile.make
@@ -25,6 +25,7 @@ PACKAGE_PATHS=                                        \
 -L $(EMACS_ELPA_DIR)/compat-current            \
 -L $(EMACS_ELPA_DIR)/seq-current               \
 -L $(EMACS_ELPA_DIR)/transient-current         \
+-L $(EMACS_ELPA_DIR)/cond-let-current          \
 -L $(CASUAL_LIB_LISP_DIR)
 
 include Makefile--rules.make
diff --git a/lisp/Makefile-eshell.make b/lisp/Makefile-ediff.make
similarity index 86%
copy from lisp/Makefile-eshell.make
copy to lisp/Makefile-ediff.make
index 349f653096..faa6772349 100644
--- a/lisp/Makefile-eshell.make
+++ b/lisp/Makefile-ediff.make
@@ -16,15 +16,16 @@
 
 include Makefile--defines.make
 
-PACKAGE_NAME=casual-eshell
-ELISP_INCLUDES=casual-eshell-utils.el  \
-casual-eshell-settings.el
+PACKAGE_NAME=casual-ediff
+ELISP_INCLUDES=casual-ediff-utils.el   \
+casual-ediff-settings.el
 ELISP_PACKAGES=
-ELISP_TEST_INCLUDES=casual-eshell-test-utils.el
+ELISP_TEST_INCLUDES=casual-ediff-test-utils.el
 PACKAGE_PATHS=                                 \
 -L $(EMACS_ELPA_DIR)/compat-current            \
 -L $(EMACS_ELPA_DIR)/seq-current               \
 -L $(EMACS_ELPA_DIR)/transient-current         \
+-L $(EMACS_ELPA_DIR)/cond-let-current          \
 -L $(EMACS_ELPA_DIR)/magit-current             \
 -L $(EMACS_ELPA_DIR)/magit-section-current     \
 -L $(EMACS_ELPA_DIR)/dash-current              \
diff --git a/lisp/Makefile-editkit.make b/lisp/Makefile-editkit.make
index d75e1e9805..ab3350e96a 100644
--- a/lisp/Makefile-editkit.make
+++ b/lisp/Makefile-editkit.make
@@ -26,6 +26,7 @@ PACKAGE_PATHS=                                        \
 -L $(EMACS_ELPA_DIR)/seq-current               \
 -L $(EMACS_ELPA_DIR)/transpose-frame-current   \
 -L $(EMACS_ELPA_DIR)/transient-current         \
+-L $(EMACS_ELPA_DIR)/cond-let-current          \
 -L $(EMACS_ELPA_DIR)/magit-current             \
 -L $(EMACS_ELPA_DIR)/magit-section-current     \
 -L $(EMACS_ELPA_DIR)/dash-current              \
diff --git a/lisp/Makefile-elisp.make b/lisp/Makefile-elisp.make
index f9d2fb8466..45052935a4 100644
--- a/lisp/Makefile-elisp.make
+++ b/lisp/Makefile-elisp.make
@@ -25,6 +25,7 @@ PACKAGE_PATHS=                                        \
 -L $(EMACS_ELPA_DIR)/compat-current            \
 -L $(EMACS_ELPA_DIR)/seq-current               \
 -L $(EMACS_ELPA_DIR)/transient-current         \
+-L $(EMACS_ELPA_DIR)/cond-let-current          \
 -L $(CASUAL_LIB_LISP_DIR)
 
 include Makefile--rules.make
diff --git a/lisp/Makefile-eshell.make b/lisp/Makefile-eshell.make
index 349f653096..d3e178ea2f 100644
--- a/lisp/Makefile-eshell.make
+++ b/lisp/Makefile-eshell.make
@@ -25,6 +25,7 @@ PACKAGE_PATHS=                                        \
 -L $(EMACS_ELPA_DIR)/compat-current            \
 -L $(EMACS_ELPA_DIR)/seq-current               \
 -L $(EMACS_ELPA_DIR)/transient-current         \
+-L $(EMACS_ELPA_DIR)/cond-let-current          \
 -L $(EMACS_ELPA_DIR)/magit-current             \
 -L $(EMACS_ELPA_DIR)/magit-section-current     \
 -L $(EMACS_ELPA_DIR)/dash-current              \
diff --git a/lisp/Makefile-help.make b/lisp/Makefile-help.make
index ae3368698c..8de0cca5ea 100644
--- a/lisp/Makefile-help.make
+++ b/lisp/Makefile-help.make
@@ -25,6 +25,7 @@ PACKAGE_PATHS=                                        \
 -L $(EMACS_ELPA_DIR)/compat-current            \
 -L $(EMACS_ELPA_DIR)/seq-current               \
 -L $(EMACS_ELPA_DIR)/transient-current         \
+-L $(EMACS_ELPA_DIR)/cond-let-current          \
 -L $(CASUAL_LIB_LISP_DIR)
 
 include Makefile--rules.make
diff --git a/lisp/Makefile-ibuffer.make b/lisp/Makefile-ibuffer.make
index 2521701ea9..d524770e6f 100644
--- a/lisp/Makefile-ibuffer.make
+++ b/lisp/Makefile-ibuffer.make
@@ -24,6 +24,7 @@ PACKAGE_PATHS=                                        \
 -L $(EMACS_ELPA_DIR)/compat-current            \
 -L $(EMACS_ELPA_DIR)/seq-current               \
 -L $(EMACS_ELPA_DIR)/transient-current         \
+-L $(EMACS_ELPA_DIR)/cond-let-current          \
 -L $(CASUAL_LIB_LISP_DIR)
 
 include Makefile--rules.make
diff --git a/lisp/Makefile-image.make b/lisp/Makefile-image.make
index 7cb0c1f018..1f000821e9 100644
--- a/lisp/Makefile-image.make
+++ b/lisp/Makefile-image.make
@@ -24,6 +24,7 @@ PACKAGE_PATHS=                                        \
 -L $(EMACS_ELPA_DIR)/compat-current            \
 -L $(EMACS_ELPA_DIR)/seq-current               \
 -L $(EMACS_ELPA_DIR)/transient-current         \
+-L $(EMACS_ELPA_DIR)/cond-let-current          \
 -L $(CASUAL_LIB_LISP_DIR)
 
 include Makefile--rules.make
diff --git a/lisp/Makefile-isearch.make b/lisp/Makefile-isearch.make
index bc761cbeb9..01ece3c994 100644
--- a/lisp/Makefile-isearch.make
+++ b/lisp/Makefile-isearch.make
@@ -25,6 +25,7 @@ PACKAGE_PATHS=                                        \
 -L $(EMACS_ELPA_DIR)/compat-current            \
 -L $(EMACS_ELPA_DIR)/seq-current               \
 -L $(EMACS_ELPA_DIR)/transient-current         \
+-L $(EMACS_ELPA_DIR)/cond-let-current          \
 -L $(CASUAL_LIB_LISP_DIR)
 
 .PHONY: tests compile regression
diff --git a/lisp/Makefile-make-mode.make b/lisp/Makefile-make-mode.make
index f567e868f0..26d3697949 100644
--- a/lisp/Makefile-make-mode.make
+++ b/lisp/Makefile-make-mode.make
@@ -25,6 +25,7 @@ PACKAGE_PATHS=                                        \
 -L $(EMACS_ELPA_DIR)/compat-current            \
 -L $(EMACS_ELPA_DIR)/seq-current               \
 -L $(EMACS_ELPA_DIR)/transient-current         \
+-L $(EMACS_ELPA_DIR)/cond-let-current          \
 -L $(CASUAL_LIB_LISP_DIR)
 
 include Makefile--rules.make
diff --git a/lisp/Makefile-man.make b/lisp/Makefile-man.make
index 76dd12ead4..5bb4f4a1d6 100644
--- a/lisp/Makefile-man.make
+++ b/lisp/Makefile-man.make
@@ -25,6 +25,7 @@ PACKAGE_PATHS=                                        \
 -L $(EMACS_ELPA_DIR)/compat-current            \
 -L $(EMACS_ELPA_DIR)/seq-current               \
 -L $(EMACS_ELPA_DIR)/transient-current         \
+-L $(EMACS_ELPA_DIR)/cond-let-current          \
 -L $(CASUAL_LIB_LISP_DIR)
 
 include Makefile--rules.make
diff --git a/lisp/Makefile-re-builder.make b/lisp/Makefile-re-builder.make
index 52ddc528df..ff2000bcee 100644
--- a/lisp/Makefile-re-builder.make
+++ b/lisp/Makefile-re-builder.make
@@ -25,6 +25,7 @@ PACKAGE_PATHS=                                        \
 -L $(EMACS_ELPA_DIR)/compat-current            \
 -L $(EMACS_ELPA_DIR)/seq-current               \
 -L $(EMACS_ELPA_DIR)/transient-current         \
+-L $(EMACS_ELPA_DIR)/cond-let-current          \
 -L $(CASUAL_LIB_LISP_DIR)
 
 include Makefile--rules.make
diff --git a/lisp/Makefile-timezone.make b/lisp/Makefile-timezone.make
index b293c6824e..d8b2fb1c04 100644
--- a/lisp/Makefile-timezone.make
+++ b/lisp/Makefile-timezone.make
@@ -25,6 +25,7 @@ PACKAGE_PATHS=                                        \
 -L $(EMACS_ELPA_DIR)/compat-current            \
 -L $(EMACS_ELPA_DIR)/seq-current               \
 -L $(EMACS_ELPA_DIR)/transient-current         \
+-L $(EMACS_ELPA_DIR)/cond-let-current          \
 -L $(CASUAL_LIB_LISP_DIR)
 
 include Makefile--rules.make
diff --git a/lisp/casual-ediff-settings.el b/lisp/casual-ediff-settings.el
new file mode 100644
index 0000000000..f017da6633
--- /dev/null
+++ b/lisp/casual-ediff-settings.el
@@ -0,0 +1,100 @@
+;;; casual-ediff-settings.el --- Casual Ediff 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 'ediff)
+(require 'casual-lib)
+
+(transient-define-prefix casual-ediff-settings-tmenu ()
+  "Casual Ediff settings menu."
+  ["Casual Ediff: Settings"
+   [("k" "Keep Variants"
+     casual-ediff-customize-ediff-keep-variants
+     :description (lambda () (casual-lib-checkbox-label ediff-keep-variants
+                                                   "Keep Variants")))
+
+    ("w" "Window Setup Function"
+     casual-ediff-customize-ediff-window-setup-function)
+
+    ("s" "Split Window Function"
+     casual-ediff-customize-ediff-split-window-function)]
+
+   [("G" "Ediff Group" casual-ediff-customize-group)
+    (casual-lib-customize-unicode)
+    (casual-lib-customize-hide-navigation)]]
+
+  [:class transient-row
+   (casual-lib-quit-one)
+   ("a" "About" casual-ediff-about :transient nil)
+   (casual-lib-quit-all)])
+
+(defun casual-ediff-customize-group ()
+  "Customize Ediff group."
+  (interactive)
+  (customize-group "ediff"))
+
+(defun casual-ediff-customize-ediff-keep-variants ()
+  "Customize `ediff-keep-variants'."
+  (interactive)
+  (customize-variable 'ediff-keep-variants))
+
+(defun casual-ediff-customize-ediff-window-setup-function ()
+  "Customize `ediff-window-setup-function'."
+  (interactive)
+  (customize-variable 'ediff-window-setup-function))
+
+(defun casual-ediff-customize-ediff-split-window-function ()
+  "Customize `ediff-split-window-function'."
+  (interactive)
+  (customize-variable 'ediff-split-window-function))
+
+(defun casual-ediff-about-ediff ()
+  "Casual Ediff is a Transient menu for Ediff.
+
+Learn more about using Casual Ediff 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 Ediff, consider making a modest financial
+contribution to help support its development and maintenance.
+URL `https://www.buymeacoffee.com/kickingvegas'
+
+Casual Ediff was conceived and crafted by Charles Choi in San Francisco,
+California.
+
+Thank you for using Casual Ediff.
+
+Always choose love."
+  (ignore))
+
+(defun casual-ediff-about ()
+  "About information for Casual Ediff."
+  (interactive)
+  (describe-function #'casual-ediff-about-ediff))
+
+(provide 'casual-ediff-settings)
+;;; casual-ediff-settings.el ends here
diff --git a/lisp/casual-ediff-utils.el b/lisp/casual-ediff-utils.el
new file mode 100644
index 0000000000..543a3712e4
--- /dev/null
+++ b/lisp/casual-ediff-utils.el
@@ -0,0 +1,242 @@
+;;; casual-ediff-utils.el --- Casual Eshell 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 'ediff)
+(require 'casual-lib)
+
+;; these defvars are here to let cc-ediff-mode.el compile clean
+(defvar ediff-buffer-A)
+(defvar ediff-buffer-B)
+(defvar ediff-buffer-C)
+(defvar ediff-merge-job)
+(defvar ediff-ancestor-buffer)
+
+;; CC: I set my Ediff variables in `custom-set-variables'
+;; Use your own preference.
+;; '(ediff-keep-variants nil)
+;; '(ediff-split-window-function 'split-window-horizontally)
+;; '(ediff-window-setup-function 'ediff-setup-windows-plain)
+
+(defconst casual-ediff-unicode-db
+  '((:previous . '("↑" "Previous"))
+    (:next . '("↓" "Next"))
+    (:scroll-to-right . '("↤" "Scroll to right"))
+    (:scroll-to-left . '("↦" "Scroll to left"))
+    (:refresh . '("⟲" "Refresh")))
+
+  "Unicode symbol DB to use for Ediff Transient menus.")
+
+(defun casual-ediff-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-ediff-unicode-db))
+
+
+;; -------------------------------------------------------------------
+;;; Format
+
+(defun casual-ediff--buffer-description (ebuf slot &optional extend)
+  "Format name of buffer EBUF in SLOT with option to EXTEND."
+  (let ((bufname (buffer-name ebuf))
+        (fwidth (frame-width)))
+    (casual-ediff--display-filename bufname slot extend fwidth)))
+
+(defun casual-ediff--display-filename (filename slot extend fwidth)
+  "Reformat FILENAME for Ediff display using SLOT, EXTEND, and FWIDTH."
+  (if (and (string-match "~\\([[:xdigit:]]*\\)~$" filename) (< fwidth 95))
+      (let* ((commit-hash (match-string 1 filename))
+             (short-hash (truncate-string-to-width commit-hash 7 0 nil t))
+             (filename (string-replace commit-hash short-hash filename)))
+        (format "%s: %s" slot filename))
+    (if extend
+        (let* ((bwidth (- (/ fwidth 2) 6))
+               (ext-fmt (concat "%s: " (format "%%-%ss" bwidth))))
+          (format ext-fmt slot filename))
+      (format "%s: %s" slot filename))))
+
+
+;; -------------------------------------------------------------------
+;;; Ediff Functions
+(defun casual-ediff-info ()
+  "Open Info for Ediff."
+  (interactive) (info "(ediff) Top"))
+
+(defvar casual-ediff--revision-session-p nil
+  "If t then `casual-ediff--internal-last-revision' has been called.
+This state variable is used to insert added behavior to the advised
+function `ediff-janitor'.")
+
+(defvar casual-ediff--installed-p nil
+  "If t then Casual Ediff is initialized.")
+
+(defun casual-ediff-revision-from-menu (e)
+  "Invoke `casual-ediff-revision' on E with variable `buffer-file-name'."
+  (interactive "e")
+  (ignore e)
+  (casual-ediff-revision))
+
+(defun casual-ediff-revision ()
+  "Run Ediff comparing current file with last committed version.
+
+This function handles the interactive concerns found in
+`ediff-revision'. This function will also test if a diff should apply to
+the current buffer."
+  (interactive)
+  (when (and (bound-and-true-p buffer-file-name)
+             (vc-registered (buffer-file-name)))
+    (if (and (buffer-modified-p)
+            (y-or-n-p (format "Buffer %s is modified.  Save buffer? "
+                               (buffer-name))))
+      (save-buffer (current-buffer)))
+    (message buffer-file-name)
+    (casual-ediff--internal-last-revision))
+
+  (cond ((not (bound-and-true-p buffer-file-name))
+         (message (concat (buffer-name) " is not a file that can be diffed.")))
+        ((not (vc-registered buffer-file-name))
+         (message (concat buffer-file-name " is not under version 
control.")))))
+
+(defun casual-ediff--internal-last-revision ()
+  "Implementation of Ediff comparing current file with last committed version.
+
+This function handles the actual diff behavior called by `ediff-revision'."
+  (let ((rev1 "")
+        (rev2 ""))
+    (setq casual-ediff--revision-session-p t)
+    (ediff-load-version-control)
+    (funcall
+     (intern (format "ediff-%S-internal" ediff-version-control-package))
+     rev1 rev2 nil)))
+
+(defun casual-ediff-janitor (ask keep-variants)
+  "Kill buffers A, B, and, possibly, C, if these buffers aren't modified.
+In merge jobs, buffer C is not deleted here, but rather according to
+`ediff-quit-merge-hook'.
+ASK non-nil means ask the user whether to keep each unmodified buffer, unless
+KEEP-VARIANTS is non-nil, in which case buffers are never killed.
+A side effect of cleaning up may be that you should be careful when comparing
+the same buffer in two separate Ediff sessions: quitting one of them might
+delete this buffer in another session as well.
+
+!!!: This method overrides the original Ediff function."
+  (let ((ask (if (and (boundp 'casual-ediff--revision-session-p)
+                      casual-ediff--revision-session-p)
+                 nil
+               ask)))
+    (ediff-dispose-of-variant-according-to-user
+     ediff-buffer-A 'A ask keep-variants)
+    ;; !!!: Test global state variable `casual-ediff--revision-session-p' to
+    ;; determine if the modified repo file should be kept.
+    ;; Guarding in place to hopefully avoid side-effects when `ediff-janitor' 
is
+    ;; called from other Ediff functions. Informal testing has not revealed any
+    ;; side-effects but YOLO.
+    (if (and (boundp 'casual-ediff--revision-session-p)
+             casual-ediff--revision-session-p)
+        (ediff-dispose-of-variant-according-to-user
+         ;; CC Note: keep-variants argument is hard-coded to t to keep
+         ;; buffer holding modified repo file around.
+         ediff-buffer-B 'B t t)
+      (ediff-dispose-of-variant-according-to-user
+       ediff-buffer-B 'B ask keep-variants))
+    (if ediff-merge-job  ; don't del buf C if merging--del ancestor buf instead
+        (ediff-dispose-of-variant-according-to-user
+         ediff-ancestor-buffer 'Ancestor ask keep-variants)
+      (ediff-dispose-of-variant-according-to-user
+       ediff-buffer-C 'C ask keep-variants))
+    ;; CC Note: Reset global state variable `casual-ediff--revision-session-p'.
+    (if (and (boundp 'casual-ediff--revision-session-p)
+             casual-ediff--revision-session-p)
+        (setq casual-ediff--revision-session-p nil))))
+
+
+(defun casual-ediff--stash-window-configuration-for-ediff ()
+  "Store window configuration to register 🧊.
+
+Use of emoji is to avoid potential use of keyboard character to reference
+the register."
+  (window-configuration-to-register ?🧊))
+
+(defun casual-ediff--restore-window-configuration-for-ediff ()
+  "Restore window configuration from register 🧊.
+
+Use of emoji is to avoid potential use of keyboard character to reference
+the register."
+  (jump-to-register ?🧊))
+
+(defun casual-ediff--restore-and-save-diff (key)
+  "Restore diff and save Ediff buffer referenced by KEY."
+  (ediff-restore-diff nil key)
+  (casual-ediff--save-buffer key))
+
+(defun casual-ediff--save-buffer (key)
+  "Save Ediff buffer referenced by KEY."
+  (setq last-command-event key)
+  (ediff-save-buffer nil))
+
+(defun casual-ediff--buffer-read-only-p (buf)
+  "Predicate to test if BUF is read-only."
+  (with-current-buffer buf
+    (if buffer-read-only t nil)))
+
+(defun casual-ediff--split-window-vertically-description ()
+  "Provide string label for state of `split-window-vertically'."
+  (if (eq ediff-split-window-function 'split-window-vertically)
+      "Side by side"
+    "On top"))
+
+;; The implementations of `casual-ediff-copy-AB-to-C' and
+;; `casual-ediff-copy-BA-to-C' are adapted from code written by killdash9 from
+;; the following Stack Overflow post.
+;; 
https://stackoverflow.com/questions/9656311/conflict-resolution-with-emacs-ediff-how-can-i-take-the-changes-of-both-version/29757750#29757750
+
+(defun casual-ediff-copy-AB-to-C ()
+  "Resolve merge conflict by inserting difference from buffer A then buffer B."
+  (interactive)
+  (ediff-copy-diff
+   ediff-current-difference nil 'C nil
+   (concat
+    (ediff-get-region-contents
+     ediff-current-difference 'A ediff-control-buffer)
+    (ediff-get-region-contents
+     ediff-current-difference 'B ediff-control-buffer))))
+
+(defun casual-ediff-copy-BA-to-C ()
+  "Resolve merge conflict by inserting difference from buffer B then buffer A."
+  (interactive)
+  (ediff-copy-diff
+   ediff-current-difference nil 'C nil
+   (concat
+    (ediff-get-region-contents
+     ediff-current-difference 'B ediff-control-buffer)
+    (ediff-get-region-contents
+     ediff-current-difference 'A ediff-control-buffer))))
+
+(provide 'casual-ediff-utils)
+;;; casual-ediff-utils.el ends here
diff --git a/lisp/casual-ediff.el b/lisp/casual-ediff.el
new file mode 100644
index 0000000000..2dd6ff965d
--- /dev/null
+++ b/lisp/casual-ediff.el
@@ -0,0 +1,295 @@
+;;; casual-ediff.el --- Transient UI for Eshell -*- 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 Ediff.
+
+;; INSTALLATION
+
+;; To install Casual Ediff, the function `casual-ediff-install' should be 
called
+;; from your Emacs initialization file. You will also need to bind
+;; `casual-ediff-tmenu' to your key binding of preference.
+
+;; (require 'casual-ediff) ; optional if using autoloaded menu
+;; (casual-ediff-install) ; run this to enable Casual Ediff
+;; (add-hook 'ediff-keymap-setup-hook
+;;           (lambda ()
+;;             (keymap-set ediff-mode-map "C-o" #'casual-ediff-tmenu)))
+
+;; Notes
+;; - `casual-ediff-install' will:
+;;   * override advise the function `ediff-janitor'.
+;;   * in-memory set the variable `ediff-window-setup-function' to plain.
+;; - The way Ediff handles keymaps necessitates the configuration of
+;;   `ediff-keymap-setup-hook' as shown above.
+
+;;; Code:
+(require 'casual-ediff-settings)
+(require 'casual-ediff-utils)
+
+;;;###autoload (autoload 'casual-ediff-tmenu "casual-ediff" nil t)
+(transient-define-prefix casual-ediff-tmenu ()
+  :refresh-suffixes t
+  [["A"
+    :pad-keys t
+    :description (lambda () (casual-ediff--buffer-description
+                        ediff-buffer-A
+                        "A"
+                        (not ediff-buffer-C)))
+    ("ab" "A→B" ediff-copy-A-to-B
+     :transient t
+     :if (lambda () (and
+                (not ediff-buffer-C)
+                ediff-buffer-B
+                (not (casual-ediff--buffer-read-only-p ediff-buffer-B)))))
+
+    ("ac" "A→C" ediff-copy-A-to-C
+     :transient t
+     :if (lambda () (and
+                ediff-buffer-C
+                (not (casual-ediff--buffer-read-only-p ediff-buffer-C)))))
+
+    ("ra" "Restore A"
+     (lambda ()
+       "Restore and save prior state of buffer A."
+       (interactive)
+       (casual-ediff--restore-and-save-diff ?a))
+     :transient t
+     :if (lambda () (and
+                (or ediff-buffer-B ediff-buffer-C)
+                (not (casual-ediff--buffer-read-only-p ediff-buffer-A)))))
+
+    ("wa" "Save A"
+     (lambda ()
+       "Save buffer A."
+       (interactive)
+       (casual-ediff--save-buffer ?a))
+     :transient t
+     :if (lambda () (and
+                (or ediff-buffer-B ediff-buffer-C)
+                (not (casual-ediff--buffer-read-only-p ediff-buffer-A))))
+     :inapt-if-not (lambda () (buffer-modified-p ediff-buffer-A)))]
+
+   ;; !!!: A diff B
+   ["Diff"
+    :pad-keys t
+    :if (lambda () (not ediff-buffer-C))
+    ("p" "↑" ediff-previous-difference
+     :description (lambda () (casual-ediff-unicode-get :previous))
+     :transient t)
+    ("n" "↓" ediff-next-difference
+     :description (lambda () (casual-ediff-unicode-get :next))
+     :transient t)
+
+    ("<" "↤" ediff-scroll-horizontally
+     :description (lambda () (casual-ediff-unicode-get :scroll-to-right))
+     :transient t)
+    (">" "↦" ediff-scroll-horizontally
+     :description (lambda () (casual-ediff-unicode-get :scroll-to-left))
+     :transient t)
+
+    ("!" "⟲" ediff-update-diffs
+     :description (lambda () (casual-ediff-unicode-get :refresh))
+     :transient t)
+
+    ("|" "H/V" ediff-toggle-split
+     :transient t
+     :description casual-ediff--split-window-vertically-description)
+    ("#" "Skip 𝑤𝑠" ediff-toggle-skip-similar
+     :transient t
+     :description (lambda ()
+                    (casual-lib-checkbox-label ediff-ignore-similar-regions
+                                               "Skip Space")))]
+
+   ["B"
+    :pad-keys t
+    :description (lambda () (casual-ediff--buffer-description ediff-buffer-B 
"B"))
+    ("ba" "A←B" ediff-copy-B-to-A
+     :transient t
+     :if (lambda () (and
+                (not (casual-ediff--buffer-read-only-p ediff-buffer-A)))))
+
+    ("bc" "B→C" ediff-copy-B-to-C
+     :transient t
+     :if (lambda () (and
+                ediff-buffer-C
+                (not (casual-ediff--buffer-read-only-p ediff-buffer-C)))))
+
+    ("rb" "Restore B"
+     (lambda ()
+       "Restore and save prior state of buffer B."
+       (interactive)
+       (casual-ediff--restore-and-save-diff ?b))
+     :transient t
+     :if (lambda () (and
+                ediff-buffer-B
+                (not (casual-ediff--buffer-read-only-p ediff-buffer-B))
+                (not (string= (file-name-extension (buffer-name 
ediff-buffer-B))
+                              "~{index}")))))
+
+    ("wb" "Save B"
+     (lambda ()
+       "Save buffer B."
+       (interactive)
+       (casual-ediff--save-buffer ?b))
+     :transient t
+     :if (lambda () (and
+                (not (casual-ediff--buffer-read-only-p ediff-buffer-B))
+                (not (string= (file-name-extension (buffer-name 
ediff-buffer-B))
+                              "~{index}"))))
+     :inapt-if-not (lambda () (buffer-modified-p ediff-buffer-B)))]
+
+   ;; !!!: A B diff C
+   ["Diff"
+    :pad-keys t
+    :if (lambda () (and ediff-buffer-C t))
+    ("p" "↑" ediff-previous-difference
+     :description (lambda () (casual-ediff-unicode-get :previous))
+     :transient t)
+    ("n" "↓" ediff-next-difference
+     :description (lambda () (casual-ediff-unicode-get :next))
+     :transient t)
+
+    (">" "↤" ediff-scroll-horizontally
+     :description (lambda () (casual-ediff-unicode-get :scroll-to-right))
+     :transient t)
+    ("<" "↦" ediff-scroll-horizontally
+     :description (lambda () (casual-ediff-unicode-get :scroll-to-left))
+     :transient t)
+
+    ("!" "⟲" ediff-update-diffs
+     :description (lambda () (casual-ediff-unicode-get :refresh))
+     :transient t)
+    ("|" "H/V" ediff-toggle-split
+     :description casual-ediff--split-window-vertically-description
+     :transient t)
+    ("#" "Skip 𝑤𝑠" ediff-toggle-skip-similar
+     :transient t
+     :description (lambda ()
+                    (casual-lib-checkbox-label ediff-ignore-similar-regions
+                                               "Skip Space")))]
+
+   ["C"
+    :pad-keys t
+    :if (lambda () (if ediff-buffer-C t nil))
+    :description (lambda () (casual-ediff--buffer-description ediff-buffer-C 
"C"))
+    ("cb" "B←C" ediff-copy-C-to-B
+     :transient t
+     :if (lambda () (and
+                (not (casual-ediff--buffer-read-only-p ediff-buffer-B))
+                (not (string= (file-name-extension (buffer-name 
ediff-buffer-B))
+                              "~{index}")))))
+
+    ("ca" "A←C" ediff-copy-C-to-A
+     :transient t
+     :if (lambda () (and
+                (not (casual-ediff--buffer-read-only-p ediff-buffer-A)))))
+
+    ("mab" "Merge A,B to C" casual-ediff-copy-AB-to-C
+     :transient t
+     :if (lambda () (string-equal (buffer-name ediff-buffer-C) 
"*ediff-merge*")))
+
+    ("mba" "Merge B,A to C" casual-ediff-copy-BA-to-C
+     :transient t
+     :if (lambda () (string-equal (buffer-name ediff-buffer-C) 
"*ediff-merge*")))
+
+    ("rc" "Restore C"
+     (lambda ()
+       "Restore and save prior state of buffer C."
+       (interactive)
+       (casual-ediff--restore-and-save-diff ?c))
+     :transient t
+     :if (lambda ()
+           (if (not (casual-ediff--buffer-read-only-p ediff-buffer-C))
+               (if (not (string= (buffer-name ediff-buffer-C) "*ediff-merge*"))
+                   t
+                 nil)
+             nil))
+     :inapt-if-not (lambda () (buffer-modified-p ediff-buffer-C)))
+
+    ("rm" "Restore Prior to Last Merge"
+     (lambda ()
+       "Call `ediff-restore-diff-in-merge-buffer' using current diff.
+
+Note that this command will restore only the state of the *ediff-merge*
+buffer prior to the previous merge. To avoid any changes to the
+conflicted file, exit Ediff and when prompted to save the merge file,
+reply with no."
+       (interactive)
+       (ediff-restore-diff-in-merge-buffer nil))
+     :transient t
+     :if (lambda ()
+           (if (not (casual-ediff--buffer-read-only-p ediff-buffer-C))
+               (if (string= (buffer-name ediff-buffer-C) "*ediff-merge*")
+                   t
+                 nil)
+             nil)))]]
+
+  [:class transient-row
+   (casual-lib-quit-one)
+   ("i" "Status" ediff-status-info)
+   ("I" "ⓘ" ediff-documentation)
+   ("," "Settings" casual-ediff-settings-tmenu)
+   ("q" "Quit Ediff" ediff-quit)])
+
+;;;###autoload (autoload 'casual-ediff-install "casual-ediff" nil t)
+(defun casual-ediff-install ()
+  "Install Casual Ediff."
+  (interactive)
+  (setq casual-ediff--installed-p t)
+
+  ;; CC: I set my Ediff variables in `custom-set-variables'
+  ;; Use your own preference.
+  ;; '(ediff-keep-variants nil)
+  ;; '(ediff-split-window-function 'split-window-horizontally)
+  ;; '(ediff-window-setup-function 'ediff-setup-windows-plain)
+
+  (unless (eq ediff-window-setup-function #'ediff-setup-windows-plain)
+    (message
+     "Overriding ediff-window-setup-function to ediff-setup-windows-plain. \
+Consider customizing to always set this variable to plain.")
+    (setq ediff-window-setup-function #'ediff-setup-windows-plain))
+
+  (add-hook
+   'ediff-before-setup-hook
+   #'casual-ediff--stash-window-configuration-for-ediff)
+  (add-hook
+   'ediff-after-quit-hook-internal
+   #'casual-ediff--restore-window-configuration-for-ediff)
+  (advice-add 'ediff-janitor :override #'casual-ediff-janitor))
+
+;;;###autoload (autoload 'casual-ediff-uninstall "casual-ediff" nil t)
+(defun casual-ediff-uninstall ()
+  "Uninstall Casual Ediff."
+  (interactive)
+  (advice-remove 'ediff-janitor #'casual-ediff-janitor)
+
+  (remove-hook
+   'ediff-before-setup-hook
+   #'casual-ediff--stash-window-configuration-for-ediff)
+  (remove-hook
+   'ediff-after-quit-hook-internal
+   #'casual-ediff--restore-window-configuration-for-ediff)
+
+  (setq casual-ediff--installed-p nil))
+
+(provide 'casual-ediff)
+;;; casual-ediff.el ends here
diff --git a/tests/Makefile b/tests/Makefile
index fa1ef9c71a..82b98bed4e 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -25,6 +25,7 @@ calc-tests                                    \
 calendar-tests                                 \
 compile-tests                                  \
 dired-tests                                    \
+ediff-tests                                    \
 editkit-tests                                  \
 elisp-tests                                    \
 eshell-tests                                   \
@@ -47,6 +48,7 @@ calc-tests                                    \
 calendar-tests                                 \
 compile-tests                                  \
 dired-tests                                    \
+ediff-tests                                    \
 editkit-tests                                  \
 elisp-tests                                    \
 eshell-tests                                   \
@@ -83,6 +85,9 @@ compile-tests:
 dired-tests:
        $(MAKE) -C $(SRC_DIR) $@
 
+ediff-tests:
+       $(MAKE) -C $(SRC_DIR) $@
+
 editkit-tests:
        $(MAKE) -C $(SRC_DIR) $@
 
diff --git a/tests/casual-ediff-test-utils.el b/tests/casual-ediff-test-utils.el
new file mode 100644
index 0000000000..e7ec8e9a9f
--- /dev/null
+++ b/tests/casual-ediff-test-utils.el
@@ -0,0 +1,39 @@
+;;; casual-ediff-test-utils.el --- Casual Test 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 'ert)
+(require 'casual-lib)
+(require 'kmacro)
+
+(defun casualt-ediff-setup ()
+  "Casual Ediff setup."
+    )
+
+(defun casualt-ediff-breakdown ()
+  "Casual man breakdown."
+  )
+
+(provide 'casual-ediff-test-utils)
+;;; casual-ediff-test-utils.el ends here
diff --git a/tests/test-casual-ediff-settings.el 
b/tests/test-casual-ediff-settings.el
new file mode 100644
index 0000000000..6c9f3dee6f
--- /dev/null
+++ b/tests/test-casual-ediff-settings.el
@@ -0,0 +1,60 @@
+;;; test-casual-ediff-settings.el --- Casual Elisp 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-ediff-test-utils)
+(require 'casual-lib-test-utils)
+(require 'casual-ediff-settings)
+
+(ert-deftest test-casual-ediff-settings-tmenu ()
+  (let ()
+    (cl-letf ((casualt-mock #'casual-ediff-customize-group)
+              (casualt-mock #'casual-ediff-customize-ediff-keep-variants)
+              (casualt-mock 
#'casual-ediff-customize-ediff-split-window-function)
+              (casualt-mock 
#'casual-ediff-customize-ediff-window-setup-function)
+              (casualt-mock #'casual-lib-customize-casual-lib-hide-navigation)
+              (casualt-mock #'casual-lib-customize-casual-lib-use-unicode)
+              (casualt-mock #'casual-ediff-about))
+
+      (let ((test-vectors
+             '(
+               (:binding "k" :command 
casual-ediff-customize-ediff-keep-variants)
+               (:binding "w" :command 
casual-ediff-customize-ediff-window-setup-function)
+               (:binding "s" :command 
casual-ediff-customize-ediff-split-window-function)
+               (:binding "G" :command casual-ediff-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-ediff-about))))
+
+        (casualt-suffix-testcase-runner test-vectors
+                                        #'casual-ediff-settings-tmenu
+                                        '(lambda () (random 5000)))))))
+
+(ert-deftest test-casual-ediff-about ()
+  (should (stringp (casual-ediff-about))))
+
+(provide 'test-casual-ediff-settings)
+;;; test-casual-ediff-setttings.el ends here
diff --git a/tests/test-casual-ediff-utils.el b/tests/test-casual-ediff-utils.el
new file mode 100644
index 0000000000..32570958d9
--- /dev/null
+++ b/tests/test-casual-ediff-utils.el
@@ -0,0 +1,98 @@
+;;; test-casual-ediff-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-ediff-test-utils)
+(require 'casual-ediff-utils)
+
+(ert-deftest test-casual-ediff-unicode-get ()
+  (let ((casual-lib-use-unicode nil))
+    (should (string-equal (casual-ediff-unicode-get :previous) "Previous"))
+    (should (string-equal (casual-ediff-unicode-get :next) "Next"))
+    (should (string-equal (casual-ediff-unicode-get :scroll-to-right) "Scroll 
to right"))
+    (should (string-equal (casual-ediff-unicode-get :scroll-to-left) "Scroll 
to left"))
+    (should (string-equal (casual-ediff-unicode-get :refresh) "Refresh")))
+
+  (let ((casual-lib-use-unicode t))
+    (should (string-equal (casual-ediff-unicode-get :previous) "↑"))
+    (should (string-equal (casual-ediff-unicode-get :next) "↓"))
+    (should (string-equal (casual-ediff-unicode-get :scroll-to-right) "↤"))
+    (should (string-equal (casual-ediff-unicode-get :scroll-to-left) "↦"))
+    (should (string-equal (casual-ediff-unicode-get :refresh) "⟲"))))
+
+
+(ert-deftest test-casual-ediff--display-filename ()
+  (let* ((control "A: fred.foo")
+         (filename "fred.foo")
+         (slot "A")
+         (extend nil)
+         (fwidth 120)
+         (result (casual-ediff--display-filename filename slot extend fwidth)))
+    (should (string-equal control result)))
+
+  (let* ((control "B: fred.foo")
+         (filename "fred.foo")
+         (slot "B")
+         (extend nil)
+         (fwidth 120)
+         (result (casual-ediff--display-filename filename slot extend fwidth)))
+    (should (string-equal control result)))
+
+  (let* ((control "B: fred.foo                                              ")
+         (filename "fred.foo")
+         (slot "B")
+         (extend t)
+         (fwidth 120)
+         (result (casual-ediff--display-filename filename slot extend fwidth)))
+    (should (string-equal control result)))
+
+  (let* ((control "B: fred.foo.~ABCDEF…~")
+         (filename "fred.foo.~ABCDEF0123456~")
+         (slot "B")
+         (extend t)
+         (fwidth 80)
+         (result (casual-ediff--display-filename filename slot extend fwidth)))
+    (should (string-equal control result)))
+
+  (let* ((control
+          "B: fred.foo.~ABCDEF0123456~                                         
                   ")
+         (filename "fred.foo.~ABCDEF0123456~")
+         (slot "B")
+         (extend t)
+         (fwidth 180)
+         (result (casual-ediff--display-filename filename slot extend fwidth)))
+    (should (string-equal control result)))
+
+  (let* ((control
+          "B: fred.foo.~ABCDEF0123456~")
+         (filename "fred.foo.~ABCDEF0123456~")
+         (slot "B")
+         (extend nil)
+         (fwidth 180)
+         (result (casual-ediff--display-filename filename slot extend fwidth)))
+    (should (string-equal control result))))
+
+(provide 'test-casual-ediff-utils)
+;;; test-casual-ediff-utils.el ends here
diff --git a/tests/test-casual-ediff.el b/tests/test-casual-ediff.el
new file mode 100644
index 0000000000..dd5c1e1af8
--- /dev/null
+++ b/tests/test-casual-ediff.el
@@ -0,0 +1,56 @@
+;;; test-casual-ediff.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-ediff-test-utils)
+(require 'casual-lib-test-utils)
+(require 'casual-ediff)
+
+;; Gah, testing casual-ediff-tmenu might be too hard.
+;; Resorting to manual testing.
+
+;; (ert-deftest test-casual-ediff-tmenu ()
+;;   (let ((tmpfile "casual-ediff-tmenu.txt"))
+;;     (casualt-ediff-setup)
+;;     (cl-letf (;;((symbol-function #') (lambda () t))
+;;               (casualt-mock #'ediff-previous-difference)
+;;               (casualt-mock #'ediff-next-difference)
+;;               ;;(casualt-mock #')
+;;               )
+
+;;       (let ((test-vectors
+;;              '((:binding "p" :command ediff-previous-difference)
+;;                (:binding "n" :command ediff-next-difference)
+;;                ;;(:binding "" :command ediff-main-)
+;;                )))
+
+;;         (casualt-suffix-testcase-runner test-vectors
+;;                                         #'casual-ediff-tmenu
+;;                                         '(lambda () (random 5000)))))
+;;     (casualt-ediff-breakdown)))
+
+(provide 'test-casual-ediff)
+;;; test-casual-ediff.el ends here
diff --git a/tests/test-casual-eshell-settings.el 
b/tests/test-casual-eshell-settings.el
index 267873ee41..33e9240e78 100644
--- a/tests/test-casual-eshell-settings.el
+++ b/tests/test-casual-eshell-settings.el
@@ -26,6 +26,7 @@
 
 (require 'ert)
 (require 'casual-eshell-test-utils)
+(require 'casual-lib-test-utils)
 (require 'casual-eshell-settings)
 
 (ert-deftest test-casual-eshell-settings-tmenu ()
diff --git a/tests/test-casual-eshell-utils.el 
b/tests/test-casual-eshell-utils.el
index f54d492bdb..b1b2e5a75d 100644
--- a/tests/test-casual-eshell-utils.el
+++ b/tests/test-casual-eshell-utils.el
@@ -46,9 +46,5 @@
     (should (string-equal (casual-eshell-unicode-get :clear) "⌫"))
     ))
 
-
-
-
-
 (provide 'test-casual-eshell-utils)
 ;;; test-casual-eshell-utils.el ends here
diff --git a/tests/test-casual-timezone-utils.el 
b/tests/test-casual-timezone-utils.el
index ee06be4998..5420381fb3 100644
--- a/tests/test-casual-timezone-utils.el
+++ b/tests/test-casual-timezone-utils.el
@@ -146,8 +146,15 @@
 
     (should (string-equal control result))))
 
+(defun in-daylight-saving-time-p ()
+  "Return t if the current local time is in Daylight Saving Time (DST), nil 
otherwise."
+  (let ((dst (nth 8 (decode-time (current-time)))))
+    (and dst (not (eq dst 0)))))
+
 (ert-deftest test-casual-timezone-remote-time-to-local ()
-  (let* ((read-date "2025-05-23 12:00")
+  (let* ((read-date (if (in-daylight-saving-time-p)
+                        "2025-05-23 11:00"
+                      "2025-05-23 12:00"))
          (remote-tz "Europe/Berlin")
          (control "2025-05-23 03:00:00 PDT")
          (result (casual-timezone-remote-time-to-local read-date remote-tz)))


Reply via email to