SystemCrafters / crafted-emacs

A sensible base Emacs configuration.
MIT License
745 stars 117 forks source link

Discussion: When do our own `defcustom` variables make sense? When are they best avoided? #337

Open sthesing opened 1 year ago

sthesing commented 1 year ago

In #334 , in the context of providing customization variables, @jeffbowman wrote:

While we have some defcustom variables around, adding more of them adds friction when someone is interested in moving away from Crafted Emacs (ie, when they outgrow it - assuming they do).

In general, I agree with this point: If we hide away a lot of the config behind customization variables, we fail our stated goals of empowering users to learn Emacs Lisp themselves and of making it easy to move away from Crafted Emacs.

For example, if a user likes part of a given crafted-*-config module, we'd rather not provide customization variables as a way to switch parts of that module off. Instead, we encourage the user to look at the source code of that module and recreate the parts they want in their config. This a) is an opportunity to learn Emacs Lisp, b) makes it easy for the user to outgrow Crafted Emacs.

So when does it make sense to provide customization variables for Crafted Emacs?

I propose the following distinction:

(when crafted-org-use-org-indent ;; Visually indent org-mode files to a given header level (add-hook 'org-mode-hook #'org-indent-mode))



- **Category 2**: customization variables that modify the **behaviour of Crafted Emacs** itself

  **Example:** In `crafted-updates-config`, we have `crafted-updates-fetch-interval`

  This is clearly something for users who want to use Crafted Emacs, but want to customize the experience. It also won't interfere when moving away from Crafted Emacs. In fact, the whole setting doesn't make sense outside of Crafted Emacs.

I think in regard to the goals and philosophy of the project, it would make sense to avoid category 1, while category 2 is OK.

#### Current customization variables

I tried to sort the customization variables we already have into these categories:

| Module                    | Variable                                   | Cat |
|---|---|---|
| `crafted-defaults-config` | `crafted-windows-prefix-key`               |   ? |
| `crafted-init-config`     | `crafted-init-auto-save-customized`        |   2 |
| `crafted-init-config`     | `crafted-init-auto-save-selected-packages` |   2 |
| `crafted-startup-config`  | `crafted-startup-inhibit-splash`           |   2 |
| `crafted-startup-config`  | `crafted-startup-recentf-count`            |   2 |
| `crafted-ui-config`       | `crafted-ui-line-numbers-enabled-modes`    |   ? |
| `crafted-ui-config`       | `crafted-ui-line-numbers-disabled-modes`   |   ? |
| `crafted-ui-config`       | `crafted-ui-display-line-numbers`          |   1 |
| `crafted-updates-config`  | `crafted-updates-fetch-interval`           |   2 |

The variables marked with "?" don't quite fit my distinction, which shows that we have to distinguish further.

- `crafted-windows-prefix-key`:
  I'm not quite sure, here. While this pertains to a built-in functionality (`winner-mode`), which argues for Category 1, one could also argue that this doesn't really change the behaviour, just the keybindings. Are variables that define keybindings a special case of Category 2?
- `crafted-ui-line-numbers-enabled-modes` and `crafted-ui-line-numbers-disabled-modes`:
  Here also, I'm not quite sure. These two don't turn parts of Crafted Emacs on or off, but - like keybindings - let the user customize _how_ they want to use what Crafted Emacs offers. Is this a special case, too?

#### Mode specific line numbers in `crafted-ui` - a package?

Come to think of it, maybe the cleanest way would be to make the (imho very useful) functionality for mode-dependent line number settings a package in its own right. That package could provide its own customizations and that users could take with them if the move away from Crafted Emacs. 
jeffbowman commented 1 year ago

Good ideas here. Category 1 could be handled with defun forms describing the configuration.

For the line numbers thing, there is also the possibility to not use any Crafted Emacs configuration and just turn on the global minor mode. Not sure it needs to be it's own package as the forms provided are just helpers in the context that more configuration is better than less and it automates that configuration. The Crafted Windows Prefix Key is a bit more of a challenge to handle without a defcustom form. Easily replaced by a user's configuration, or possibly just use the form provided by windmove.el to setup defaults (see windmove-install-defaults).

ajxn commented 1 year ago

But isn't this an example for users that want to learn Emacs Lisp and how Emacs customization works?

And if one has outgrown Crafted Emacs, want to make a configuration from scratch, etc. Will one not just remove things like that from the code one rip out of Crafted Emacs for own use? Personally I don't see them as a problem if you want to go your own way, starting from Crafted Emacs. You probably need to rewrite the code anyways.

Isn't it usually what you do when you read configurations that someone else has written? Read and then rip the good parts out? I might missed something though.

jeffbowman commented 1 year ago

Isn't it usually what you do when you read configurations that someone else has written? Read and then rip the good parts out?

Absolutely! We are just trying to be conscious of the effort to do that. The more "custom" it is, more effort it takes to decouple. Imagine starting with something like Doom and then deciding at some point you'd prefer to use leaf for a package manager and want to simplify some of the other customizations. That's a bit more difficult because of the macros, customization system, etc that comes with Doom. Nothing wrong with that, the Doom project has its own goals which aren't likely to fully align with ours. But in the same kind of way, it might be nice if all a user had to do was to do was stop using a Crafted Emacs -config module because they had ported what they wanted to their own config in some way - thus eventually they just delete the loading of the crafted-init-config file and nothing breaks. The idea is they have been migrating piece by piece over time rather than a wholesale declaration of bankruptcy and starting from scratch by copying bits and pieces they liked from before.

Of course, if the person does start completely from scratch after using Crafted Emacs, then the effort to migrate probably can't be measured in that sense.

ajxn commented 1 year ago

Isn't it usually what you do when you read configurations that someone else has written? Read and then rip the good parts out?

Absolutely! We are just trying to be conscious of the effort to do that.

I can see the benefits of doing that. ;-)

That's a bit more difficult because of the macros, customization system, etc that comes with Doom. Nothing wrong with that, the Doom project has its own goals which aren't likely to fully align with ours.

Yes, but as you wrote, they have another goal all together. So that is why they can get away with a configuration system of their own. But here we are using Emacs configuration system, which make it a learning experience to make it your own configuration. I think documentation about those defcustom in those packages that uses them will bring it much closer to just reuse a crafted module with removing those variables or recreate them yourself in your own configuration.

But in the same kind of way, it might be nice if all a user had to do was to do was stop using a Crafted Emacs -config module because they had ported what they wanted to their own config in some way - thus eventually they just delete the loading of the crafted-init-config file and nothing breaks. The idea is they have been migrating piece by piece over time rather than a wholesale declaration of bankruptcy and starting from scratch by copying bits and pieces they liked from before.

Hm, but is it even feasible to make that work? Is it reasonable or even possible to decouple dependencies so much between crafted packages? It is a nice idea though.

Of course, if the person does start completely from scratch after using Crafted Emacs, then the effort to migrate probably can't be measured in that sense.

I only saw this as an option when make your own configuration. Probably stuck in the old ways. :-)

Anyway, it was just my 5 öre (or 1 krona, as we don't have any öre coins any more)

jvdydev commented 1 year ago

I think the defun approach for category 1 is quite elegant. As in, provide bare-minimum configuration with callable extra configuration (to be documented, of course). Also it still retains the "easy to move into personal config" by replacing the function calls with the code. We also already have a few modules that use this approach (crafted-evil, crafted-writing and crafted-osx, probably others).

As for the line-number thing, maybe it's just me but the entire block seems a bit much for essentially adding/removing hooks to enable/disable line-numbers? Is there really a need to customize it with direct feedback (as in, without restarting Emacs)? It seems to me like a set-and-forget thing, something like:

(defun crafted-emacs-enable-line-numbers-for-modes (modes)
  "Enable line numbers for every mode in MODES."
  (mapc (lambda (mode)
          (add-hook mode #'display-line-number-mode)) ; probably prefer a wrapper explicitly turning it on
        modes))

;; in user config
(crafted-emacs-enable-line-numbers-for-modes '(conf-mode prog-mode))

It would mean derived modes of e.g. prog-mode in the example would have to be disabled manually, although that could be a similar function.

However, I might be missing something on that one as I'm using line-numbers in most modes.

sthesing commented 1 year ago

Isn't it usually what you do when you read configurations that someone else has written? Read and then rip the good parts out? I might missed something though.

Speaking as someone who is learning Emacs Lisp by exactly this process, I still see the benefits of being cautious with more variables. Every defcustom makes it easier for someone to use Crafted Emacs without looking at the code. Read the docs, set the variable, done. But also, every defcustom brings with it an if, when or unless, thus making the code more complex. So when you finally do get around to read the code, it's much more of a challenge than to understand a simple, opinionated setting.

So I guess - in accordance with the stated goals of this project - in the end it comes down to code simplicity, which doesn't mean to avoid customization variables altogether. The line numbers thing is actually a good example for relatively simple code that taught me (for one) a lot about the use of customization variables in my own config and in packages I use.

In general, I agree with the defun-approach to circumnavigate Cat. 1. In the particular case of the line numbers in crafted-ui, I don't think it makes the code simpler or more easy to read and understand.

I propose to leave the current customization variables as they are, document them properly and be mindful of this going on.