-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Rework filetype change, reload command and autosave
#3343
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
So it seems that instead of hacking
After such 2 changes, |
BTW maybe we actually do want |
I saw something different, otherwise I wouldn't have created the PR.
Hm, depends on what the community and or us really expects from that command to do. |
479f0d9 to
eee2f0c
Compare
Are you sure? I've checked again that with Moreover, I see it's even worse: in the basic case when I change an option value in |
runtime/help/commands.md
Outdated
| In case `micro` was started with a `-<option>` being set to something other | ||
| than the default value and other than set within the `settings.json` | ||
| then this volatile option will be reverted to the default or to the value | ||
| defined in `settings.json`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reload description is expanded also in PR #3240 (still not merged, but IMHO it is ready for merge).
This change would make it even more lengthy and confusing.
...Now it actually seems a bit more reasonable to me if reload will not change volatile settings. Its job is to update those settings that were loaded from settings.json (since settings.json changed), arguably it should not update those settings that were not loaded from settings.json in the first place.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change would make it even more lengthy and confusing.
Ok, drop it (locally) already.
...Now it actually seems a bit more reasonable to me if
reloadwill not change volatile settings.
Yes, that's an other way around. Sounds feasible and leaves the changed volatiles alone.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds feasible and leaves the changed volatiles alone.
At least as long as they aren't changed by the settings.json too.
Yep, tried it now once again with the current state (set current: master: |
eee2f0c to
5216507
Compare
filetype change
Ah... you checked that save without prompt on exit gets disabled after |
|
master: current: Ok, you're right...saw it now. Totally weird. |
e6beb6e to
9d84b25
Compare
9d84b25 to
a60ab16
Compare
|
Hopefully I didn't miss something after my absence. |
internal/buffer/settings.go
Outdated
| screen.TermMessage(err) | ||
| } | ||
| b.Settings = config.DefaultCommonSettings() | ||
| b.Settings[option] = nativeValue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why so tricky? The option will be set in b.Settings by b.SetOptionNative() anyway, so why not just:
for k := range config.DefaultCommonSettings() {
...
}
Also it would be nice to do this in a separate commit: one commit to remove the undesired stuff for globals and unneeded parsing, and another commit to add the missing stuff for locals.
internal/config/autosave.go
Outdated
| select { | ||
| case a = <-autotime: | ||
| if a > 0 { | ||
| elapsed = time.After(time.Duration(a * float64(time.Second))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to work correctly from the autosave perspective, however it seems a bit unclean: it doesn't stop the previous elapsed timer, so it will still fire after some time. Although it won't cause an unwanted autosave (thanks to the fact that the elapsed variable is now assigned to a different channel, so we will not wait on the old channel in next iterations of the loop), this also probably means that, since no one will read from the old channel when the old timer fires, the old timer will block forever on sending to the old channel, so it will not be garbage-collected even after it fires.
a60ab16 to
5bcc352
Compare
|
Just this one left for |
internal/action/command.go
Outdated
| SetGlobalOptionNative(k, parsedSettings[k]) | ||
| continue | ||
| } | ||
| if _, ok := oldParsedSettings[k]; ok { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need oldParsedSettings? If the setting is not present in the new parsed settings, we should set it to the default in any case?
If it is to avoid unneeded setting it to the default if it's already default, then it looks inconsistent with the above case when it is present in parsedSettings: in that case we don't check if the new parsed setting is different from the old one or not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was sure, that it was needed in a case, but can't remember and reproduce it so far.
So long store short:
I will remove it and directly convert the second path into } else {.
internal/buffer/settings.go
Outdated
| } else if option == "filetype" { | ||
| config.InitLocalSettings(b.Settings, b.Path) | ||
| parsedSettings := config.ParsedSettings() | ||
| parsedSettings[option] = nativeValue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems unneeded: we already ensure skipping filetype below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I confirm, that it looks hacky, but didn't find a better solution so far.
We need to start with clean parsed settings (local within this function) and call InitLocalSettings(), which then operates at this given map. But filetype must be set to parse the local settings (filetype dependent) correctly within this given map, otherwise it would ignore the ft: XYZ settings.
Since we don't want to clear it again to unknown or cause recursive calls it's ignored in the following loop.
Please correct me, in case I oversee something.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But
filetypemust be set to parse the local settings
Ah, indeed. Looks good then.
I'd maybe just use explicit parsedSettings["filetype"] instead of parsedSettings[option], to make it a bit more clear.
Maybe also rename parsedSettings to just settings?
5bcc352 to
66ace26
Compare
internal/buffer/settings.go
Outdated
| } else if option == "encoding" { | ||
| _, err := htmlindex.Get(b.Settings["encoding"].(string)) | ||
| if err != nil { | ||
| b.Settings["encoding"] = "utf-8" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe we should just return error in this case, not override previously set (valid) encoding.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So why not just return err in this case?
66ace26 to
faf9c58
Compare
internal/config/settings.go
Outdated
| if equal { | ||
| return nil | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of all this we can just use a local boolean variable which we set to true if we modify or delete anything in parsedSettings in this function?
...But I actually have doubts whether it's a good idea to prevent unnecessary writes so generically. Think of such a scenario, for example:
- The user starts micro ->
settings.jsonis read, optionfoois set to true. - The user edits
settings.json, changingfooto false (but doesn't runreload). - The user runs
set foo true-> micro doesn't updatesettings.json(so the permanentfoosetting remains false, i.e. thesetcommand doesn't behave as it should).
Since micro unfortunately encourages both ways of setting settings (manual editing of settings.json and the set command), we should aim to ensure that both work well, together? So we shouldn't assume that settings.json hasn't changed since the last time micro read it, in the general case? We can only assume that in the special reload case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of all this we can just use a local boolean variable which we set to true if we modify or delete anything in
parsedSettingsin this function?
Argh, yes. Your example shows, that it's the wrong approach anyway.
So we shouldn't assume that
settings.jsonhasn't changed since the last time micro read it, in the general case?
Most probably we've then still some use cases left, where settings.json is overwritten without needs, but we should address this then in a different issue/PR.
We can only assume that in the special
reloadcase?
This definitely.
faf9c58 to
1940ef8
Compare
Validate the parsed options directly after read and inform about errors.
Additionally pricise the documentation of the remaining DefaultCommonSettings() and DefaultAllSettings() functions.
- on `reload` - on `filetype` change
It doesn't need to loop over the DefaultCommonSettings() again, since they're already included in the default settings and set via SetGlobalOptionNative().
...and use `SetOptionNative()` instead.
48e1b96 to
fff3b18
Compare
internal/config/autosave.go
Outdated
| break | ||
| select { | ||
| case a = <-autotime: | ||
| if t != nil && !t.Stop() && len(elapsed) > 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we check elapsed only if t.Stop() returned false?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to the docs...
Stop prevents the Timer from firing. It returns true if the call stops the timer, false if the timer has already expired or been stopped.
...so in case it returns true it shouldn't lead to any elapsed timer yet.
For sanity we could decouple it from the return of Stop() and just clear elapsed in case it isn't empty till it is empty.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...so in case it returns
trueit shouldn't lead to any elapsed timer yet.
Aha, I see. If t.Stop() returned true, it is guaranteed that the timer has not fired, so the channel is guaranteed to be empty.
BTW I've noticed the doc also says this:
For a chan-based timer created with NewTimer(d), as of Go 1.23, any receive from t.C after Stop has returned is guaranteed to block rather than receive a stale time value from before the Stop; if the program has not received from t.C already and the timer is running, Stop is guaranteed to return true. Before Go 1.23, the only safe way to use Stop was insert an extra <-t.C if Stop returned false to drain a potential stale value.
BTW, I see we are using 1.19, maybe it is a bit too old? (1.23, on the other hand, could be a bit too new, it was only released a week ago.)
in case it isn't emptytill it is empty.
For the record, it seems guaranteed that elapsed will not contain more than 1 element (i.e. there is no difference between if len(elapsed) > 0 and for len(elapsed) > 0 in this case)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW, I see we are using 1.19, maybe it is a bit too old? (1.23, on the other hand, could be a bit too new, it was only released a week ago.)
Yes, 1.23 or even 1.22 could lead to problems in the moment deprecated functions have been removed from the standard library.
For the record, it seems guaranteed that
elapsedwill not contain more than 1 element (i.e. there is no difference betweenif len(elapsed) > 0andfor len(elapsed) > 0in this case)?
Ok, but since it needs to be checked anyway it doesn't hurt to do it in a loop.
fff3b18 to
b80ea93
Compare
Setting options directly in (h.)Buf.Settings without calling SetOption() or SetOptionNative() is generally not the best idea, since it may not trigger the needed side effects. In particular, after zyedidia#3343, directly setting `diffgutter` and `ruler` causes them not being tracked as locally overridden per buffer, so if we run the `reload` command, it unexpectedly replaces them with the default ones.
Setting options directly in (h.)Buf.Settings without calling SetOption() or SetOptionNative() is generally not the best idea, since it may not trigger the needed side effects. In particular, after zyedidia#3343, directly setting `diffgutter` and `ruler` causes them not being tracked as locally overridden per buffer, so if we run the `reload` command, it unexpectedly replaces them with the default ones.
Setting options directly in (h.)Buf.Settings without calling SetOption() or SetOptionNative() is generally not the best idea, since it may not trigger the needed side effects. In particular, after zyedidia#3343, directly setting `diffgutter` and `ruler` causes them not being tracked as locally overridden per buffer, so if we run the `reload` command, it unexpectedly replaces them with the default ones.
Setting options directly in (h.)Buf.Settings without calling SetOption() or SetOptionNative() is generally not the best idea, since it may not trigger the needed side effects. In particular, after zyedidia#3343, directly setting `diffgutter` and `ruler` causes them not being tracked as locally overridden per buffer, so if we run the `reload` command, it unexpectedly replaces them with the default ones.
filetypecould cause unexpected behavior depending on global options being reset, while a volatile setting has been used.reloadcommand could cause the same unexpected behavior and additionally doesn't really reload the user settingsautosaveis now aborted in case his value has changed to something smaller or equal to 0.Fixes #3342