macvim-dev / macvim

Vim - the text editor - for macOS
https://macvim.org
Vim License
7.47k stars 680 forks source link

Change vim `colorscheme` based on system appearance #1306

Closed haze closed 1 year ago

haze commented 1 year ago

Is your feature request about something that is currently impossible or hard to do? Please describe the problem. I initially created sys-appearance to provide updates to constrained contexts (vim script) about the macOS system appearance. At the time, I used NeoVim within Terminal.app and was able to use the Lua event loop to have efficient communication with the sys_appearance daemon. I've done an hour or so of research into whether or not this is possible in vim and gave up on a plugin only approach. I MacVim however is a perfect candidate for said behavior.

Describe the solution you'd like Provide the user with an option to let MacVim set the colorscheme when the system appearance changes. Ideally, there should a checkbox toggle in the preferences. When a system appearance update occurs, if the checkbox is toggled, update the colorscheme with a value provided in the users vimrc.

Describe alternatives you've considered I haven't considered any alternatives but am open to discussion and other ideas.

Additional context N/A

ychin commented 1 year ago

Provide the user with an option to let MacVim set the colorscheme when the system appearance changes

Were you aware of v:os_appearance (which lets you query the light/dark modes) and the OSAppearanceChanged autocmd? You can use both to query and handle appearance changes in MacVim. Just handle the autocmd to change your colorscheme.

Ideally, there should a checkbox toggle in the preferences.

The problem with checkbox toggle is that it conflicts with Vim's builtin handling. I think it's better for color schemes to be handled in a vimrc script to be consistent with how it usually works in regular Vim. Usually, the MacVim preference pane is used for global configuration that affects all of MacVim, and mostly involve MacVim-specific functionality that Vim doesn't know about, whereas each Vim window (each window hosts a separate Vim process) can have its own configuration done in vimrc. It's totally possible for the user to want to have different color schemes in each Vim window.

I've done an hour or so of research into whether or not this is possible in vim and gave up on a plugin only approach.

As I mentioned, MacVim already has builtin support for this so you don't need to use a plugin, but why can't it be done in Vim? You can spawn background processes and use the jobs API to to communicate with your sys-appearance app. The APIs are admittedly different from NeoVim but I wasn't aware of any critical functionality missing from what you need. E.g. I think this should work if you need this to work in terminal Vim.

ychin commented 1 year ago

Made a PR (#1307) to make the documentation a little clearer, so that you can find that by scanning through :h macvim

haze commented 1 year ago

I'll admit I did browse around in the MacVim :help, but probably just had bad luck finding what I was looking for; I'm happy that a solution exists that uses autocmd, I don't have a preference for how it's implemented.

The APIs are admittedly different from NeoVim but I wasn't aware of any critical functionality missing from what you need.

My concern was that sys_appearance is a long lived TCP connection, and because Vim is single threaded blocking on reading from that socket would become troublesome. I'm not at my computer right now, but when I whip up an example of this working, I'll close this issue. Thanks for the hasty response!

ychin commented 1 year ago

I'll admit I did browse around in the MacVim :help, but probably just had bad luck finding what I was looking for; I'm happy that a solution exists that uses autocmd, I don't have a preference for how it's implemented.

Yeah it was kind of hidden. The new documentation should make it clearer if you just search for dark mode for example, by directly mentioning the autocmd and built-in variable.

My concern was that sys_appearance is a long lived TCP connection, and because Vim is single threaded blocking on reading from that socket would become troublesome. I'm not at my computer right now, but when I whip up an example of this working, I'll close this issue. Thanks for the hasty response!

If you use the jobs/channels API (:h jobs) it allows Vim to talk to jobs asynchronously. It will only call back to the main thread if your process prints out something to say stdout, or the TCP connection sends over stuff. (In Vim 8+, "jobs" are either background spawned processes using job_start or terminals spawned using term_start / :terminal. "Channels" are how you communicate with either a spawned job's stdout/etc, or a TCP connection to a server)

haze commented 1 year ago

That's awesome! I didn't get far googling "vim event loop" or "vim async io" (everything that came up was NeoVim)

ychin commented 1 year ago

Yeah, I mean, Neovim started off as a fork to add async to Vim 7 (after the initial pull requests were not accepted) and it snowballed from there. Meanwhile Vim 8 added its own sets of APIs (jobs/channels) that did have some controversies since it works a little differently from the async features in Neovim. That aside, it's totally doable to talk to background processes/servers in Vim now.

Just closing the issue unless you find issues with the current functionality.

ychin commented 1 year ago

One thing I forgot to mention is that v:os_appearance depends on the user preference under Preferences… -> Appearance. It will work the way you expect to if you just choose "Automatic", but if you set MacVim to always use dark mode for example, then v:os_appearance will always report 1 (dark mode) even though macOS is running in light mode. This is intentional.