-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Rework filetype
change, reload
command and autosave
#3343
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
filetype
change
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
reload
will 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
changefiletype
change and reload
command
Ah... you checked that save without prompt on exit gets disabled after |
master:
current:
Ok, you're right...saw it now. Totally weird. |
filetype
change and reload
commandfiletype
change, reload
command and autosave
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
@@ -20,7 +20,19 @@ func (b *Buffer) SetOptionNative(option string, nativeValue interface{}) error { | |||
} else if option == "statusline" { | |||
screen.Redraw() | |||
} 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
filetype
must 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
@@ -49,6 +50,10 @@ func (b *Buffer) SetOptionNative(option string, nativeValue interface{}) error { | |||
b.UpdateRules() | |||
} | |||
} 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.json
is read, optionfoo
is set to true. - The user edits
settings.json
, changingfoo
to false (but doesn't runreload
). - The user runs
set foo true
-> micro doesn't updatesettings.json
(so the permanentfoo
setting remains false, i.e. theset
command 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
parsedSettings
in this function?
Argh, yes. Your example shows, that it's the wrong approach anyway.
So we shouldn't assume that
settings.json
hasn'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
reload
case?
This definitely.
faf9c58
to
1940ef8
Compare
- reload runtime files - reset globals - parse the settings again since this isn't the task of a filetype change.
Additionally keep volatile settings as they have been set by the user.
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
true
it 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
elapsed
will not contain more than 1 element (i.e. there is no difference betweenif len(elapsed) > 0
andfor len(elapsed) > 0
in 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
filetype
could cause unexpected behavior depending on global options being reset, while a volatile setting has been used.reload
command could cause the same unexpected behavior and additionally doesn't really reload the user settingsautosave
is now aborted in case his value has changed to something smaller or equal to 0.Fixes #3342