diff --git a/doc/emacs/custom.texi b/doc/emacs/custom.texi index b2fcb3c489f..72779d32168 100644 --- a/doc/emacs/custom.texi +++ b/doc/emacs/custom.texi @@ -32,6 +32,7 @@ Reference Manual}. * Init File:: How to write common customizations in the initialization file. * Authentication:: Keeping persistent authentication information. +* Newcomers Theme:: A theme that enables options for newcomers. @end menu @@ -3230,3 +3231,22 @@ auth, Emacs auth-source}. configured backend, some of the backends offer to save it persistently. This can be changed by customizing the user option @code{auth-source-save-behavior}. + +@node Newcomers Theme +@section Newcomers Theme + + The default appearance and behaviour of Emacs is traditionally +conservative, not changing much between releases. While each release +brings more features, these are not enabled out of the box to not +disrupt existing workflows. + + As a remedy for new users who would like to enable a larger set of +features in one fell swoop, the @code{newcomers-presets} theme +(@pxref{Custom Themes}) sets a collection of users options considered to +be too invasive by default, but useful for users acquainted with +conventions from other programs trying to learn Emacs. + + Keep in mind that the functionality enabled by this theme may change +between releases, including adding new functionality that is expected to +be of value for new users or removing old functionality that is regarded +to be confusing or superseded. diff --git a/etc/NEWS b/etc/NEWS index 26a16dd0d4c..c76af281763 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -3325,6 +3325,14 @@ If non-nil, FFAP always finds remote files in buffers with remote 'default-directory'. If nil, FFAP finds local files first for absolute file names in above buffers. The default is nil. ++++ +*** New theme 'newcomers-presets' +This new theme configures user options and minor modes that might +interest new users, but would otherwise be too invasive to enable by +default. Recall that an Emacs theme is a generic collection of +settings, that doesn't have to affect the appearance. See the Info node +"(emacs) Newcomers Theme" for more information. + ** Edebug +++ diff --git a/etc/themes/newcomers-presets-theme.el b/etc/themes/newcomers-presets-theme.el new file mode 100644 index 00000000000..4d1dcf9b084 --- /dev/null +++ b/etc/themes/newcomers-presets-theme.el @@ -0,0 +1,142 @@ +;;; newcomers-presets-theme.el --- Theme of user options for newcomers -*- lexical-binding:t -*- + +;; Copyright (C) 2026 Free Software Foundation, Inc. + +;; Author: Philip Kaludercic + +;; This file is part of GNU Emacs. + +;; GNU Emacs 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. + +;; GNU Emacs 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 GNU Emacs. If not, see . + +;;; Commentary + +;; A theme that enables user options new users might be interested in. +;; The guideline to enabling a feature is "would this interest someone +;; who wouldn't even know that this option exists?". Please avoid +;; opinionated cosmetic changes, that is the job of regular/color-scheme +;; themes. + +;;; Code: + +;; We define a `newcomers-presets-mode' that we can use to execute custom code +;; that we cannot express by setting existing users options. + +(defvar newcomers-presets-mode-enabled-local-modes + '((prog-mode-hook . display-line-numbers-mode) + (prog-mode-hook . flymake-mode) + (prog-mode-hook . flyspell-prog-mode) + + (text-mode-hook . display-line-numbers-mode) + (text-mode-hook . flyspell-mode) + ) + "Alist mapping hooks to functions. +The functions are added to the corresponding hooks when enabling +`newcomers-presets-mode', and removed when disabling the mode.") + +(defconst newcomers-presets--dnt-prop + (make-symbol "newcomers-presets-no-not-touch") + "A fresh constant to use as a symbol property. +The symbol property is used by `newcomers-presets-mode'") + +;;;###autoload +(define-minor-mode newcomers-presets-mode + "A minor mode associated with the `newcomers-presets' theme. +This minor mode will enable and disable the theme on startup." + :global t + (cl-letf (((symbol-function 'newcomers-presets-mode) + ;; As the theme enables/disables this mode we have to + ;; prevent in indefinite mutual recursion to allow + ;; `newcomers-presets-mode' to enable the `newcomers-presets' theme and + ;; vice versa. + #'ignore)) + (if newcomers-presets-mode + (enable-theme 'newcomers-presets) + (disable-theme 'newcomers-presets))) + ;; TODO: extend `custom-theme-set-variables' to support function local + ;; hooks. + (pcase-dolist (`(,hook . ,fn) newcomers-presets-mode-enabled-local-modes) + (cond + (newcomers-presets-mode + ;; We check if a function is already in the hook, to avoid + ;; removing it later if the user disables the theme. + (when (run-hook-wrapped hook (lambda (ent &rest _) (eq fn ent))) + (push fn (get hook newcomers-presets--dnt-prop))) + (add-hook hook fn)) + (t + (unless (memq fn (get hook newcomers-presets--dnt-prop)) + (remove-hook hook fn)) + (put hook newcomers-presets--dnt-prop '()))))) + +;;;###theme-autoload +(deftheme newcomers-presets + "Theme of user options settings interesting for newcomers." + :kind 'user-options) + +(custom-theme-set-variables + 'newcomers-presets + + '(newcomers-presets-mode t) + +;;;; Appearance-related options + '(font-use-system-font t) + '(frame-resize-pixelwise t) + '(window-resize-pixelwise t) + +;;;; Mouse-related options + '(context-menu-mode t) + '(save-interprogram-paste-before-kill t) + '(mouse-yank-at-point t) + '(pixel-scroll-mode t) + ;; '(pixel-scroll-precision-mode t) ;; see bug#69972 + '(mouse-drag-and-drop-region t) + '(mouse-drag-and-drop-region-cross-program t) + '(mouse-drag-mode-line-buffer t) + +;;;; Persistence-related options + '(savehist-mode t) + '(save-place-mode t) + '(recentf-mode t) + +;;;; Editing-related options + '(electric-pair-mode t) + '(repeat-mode t) + '(delete-selection-mode t) + '(editorconfig-mode t) + +;;;; Directory managment-related options + '(dired-auto-revert-buffer t) + '(dired-mouse-drag-files t) + +;;;; File-related options + '(vc-auto-revert-mode t) + +;;;; Completion-related options + '(minibuffer-visible-completions t) + '(completions-detailed t) + '(completions-group t) + '(completion-auto-select 'second-tab) + '(completion-eager-update t) + '(completion-styles '(basic emacs22 flex)) + '(global-completion-preview-mode t) + '(tab-always-indent 'complete) + '(which-key-mode t) + +;;;; Package-related options + '(package-autosuggest-mode t) + '(package-menu-use-current-if-no-marks nil) + + ) + +(provide-theme 'newcomers-presets) +;;; newcomers-presets-theme.el ends here diff --git a/lisp/startup.el b/lisp/startup.el index 6fa42026ca0..8de2af3def3 100644 --- a/lisp/startup.el +++ b/lisp/startup.el @@ -2158,6 +2158,38 @@ a face or button specification." :face 'variable-pitch "To quit a partially entered command, type " :face 'default "Control-g" :face 'variable-pitch ".\n") + + (fancy-splash-insert + :face '(variable-pitch (:height 0.8)) + "Enable " + :link `("newcomer-friendly" + ,(lambda (_button) (info "(emacs) Newcomers Theme"))) + " options: ") + + (let ((checked (create-image "checked.xpm" + nil nil :ascent 'center)) + (unchecked (create-image "unchecked.xpm" + nil nil :ascent 'center)) + (enabled (custom-theme-enabled-p 'newcomers-presets))) + (insert-button + " " + :on-glyph checked + :off-glyph unchecked + 'checked enabled + 'display (if enabled checked unchecked) + 'follow-link t + 'action (lambda (button) + (if (overlay-get button 'checked) + (progn (overlay-put button 'checked nil) + (overlay-put button 'display + (overlay-get button :off-glyph)) + (disable-theme 'newcomers-presets)) + (overlay-put button 'checked t) + (overlay-put button 'display + (overlay-get button :on-glyph)) + (load-theme 'newcomers-presets))))) + (fancy-splash-insert :face 'variable-pitch "\n") + (save-restriction (narrow-to-region (point) (point)) (fancy-splash-insert :face '(variable-pitch font-lock-builtin-face)