Bugginess Associated with Changing Atom Settings Via Code


#1

I received some help in another thread here for setting an Atom editor setting via code, however, I’m experiencing some pretty erratic bugginess now. I’ve been over everything numerous times and can’t seem to find what it could be. I’m not sure of this is my fault (something I’m not doing correctly), or that quickly changing preferences through code on the fly is something Atom can’t handle well. I’m betting on the first. Basically, I’m changing the editors preferred line length. The user is able to set an array of values for their preferred line length and then perform a key command to cycle through the values in the array (they just get set to the editor’s preferred line length setting). The bugginess is eally easy to tell if you are using wrap-guide as a visual. When switching settings through the package, the wrap guide will often flicker between two settings before fully taking on the new setting. Once in a blue moon, it will get endlessly stuck in a state of flickering between two states, and in time with the cursor blink rate (weird!). Other times, atom will ask to restart. I really don’t feel as im writing any dangerous code or asking too much of Atom, but I’m not certain.

Would anyone be kind enough to take a look and see if they spot anything super wrong here? The package is very stripped and only has a tiny bit of code that actually executes (around 10 lines of code in changePreferredLineLength()):

This is a package I’ve been really wanting to make for myself, really hoping to find the culprit to the bugginess of changing editor settings.


#2

I can get this by quickly toggling the setting… I think. When it happens, it seems like the config.cson file is wiped; I also get a bunch of notifications from all the packages as if they were just installed, and I caught the config file largely empty, but with a mix of core and community settings.

I’ll try and look further. Chances are it’s something async.

Edit: The config file corruption being really difficult to reproduce… the wrap flicker is easier to get, but I’m having trouble pinpointing where it goes wrong (it gets into the non-render process JS). However, a call to config.set is ultimately an async operation that edits a file (the config file). That’s always going to be slow, and not well suited for quick changes on the fly. I’ll get back to this tomorrow.

  • Note; the following seems to fix the flicker issue, but does not update the wrap guide
let editor = atom.workspace.getActiveTextEditor();
editor.update({ ["preferredLineLength"]: preferredLineLengthValues[newIndex] })

#3

Thank you so very much for having a look at this for me.

Yep, this is one of the bugs I am experiencing, the one with Atom asking to restart, then I have to deal with a bunch of packages asking for permissions for various things.

Yes, that was one of my hunches, that these file operations were slow operations, but still wasn’t sure why that would result in the bugginess. I even thought about introducing some sort of delay / latency when executing the command so that it couldn’t be toggled quickly back and forth, but I hoped that there would be a better way to solve this issue other than that.


#4

You’re going to like this. I did some digging and found out how the soft wrap is calculated.

getSoftWrapColumn () {
    if (this.isSoftWrapped() && !this.mini) {
      if (this.softWrapAtPreferredLineLength) {
        return Math.min(this.getEditorWidthInChars(), this.preferredLineLength)
      } else {
        return this.getEditorWidthInChars()
      }
    } else {
      return this.maxScreenLineLength
    }
  }

Whenever it is called, which is not constantly, but should be whenever the display changes or the editor:toggle-soft-wrap gets used. The question is how to trigger it yourself and how to have it give you the value you want. Well, it does rely on preferredLineLength, so you can do what you were doing initially, but preferredLineLength could also update itself from the config setting without your say-so.

I think the best approach will be to use the lax security of JavaScript and the openness of Atom to do a little hacking. Since everything is an object, you can replace any function on the TextEditor like you would with any other variable. Open a new editor window and paste this into the console:

editor = atom.workspace.getActiveTextEditor()
editor.getSoftWrapColumn = () => { return 40 }
editor.setSoftWrapped(false)
editor.setSoftWrapped(true)

You will see the display change immediately. If you close and reopen the tab, the change will vanish because the new TextEditor will have all of its default methods. If you set the soft wrap to only true or false, it won’t always update, so you need to flick it back and forth. One of the things that getSoftWrapColumn() reveals to us is that Atom always soft wraps. Sometimes it wraps at the maximum screen length and sometimes it wraps at the preferred line length (or the editor width, if it’s smaller). If you override the function to always return the wrap value you want, then you can take control of the editor display until you’re done with it. Since you aren’t changing anything important to Atom’s functioning, the hack will evaporate when you’re done with it.


#5

That doesn’t seem to update the wrap-guide though, which is also why

editor.update({ ["preferredLineLength"]: preferredLineLengthValues[newIndex] })

isn’t ideal.

@joe_04_04 Question: Do you want this to apply to all open panes, or just the active one? Because the config set version is applied to everything.


#6

Thanks a ton DS, again, I will sift through this reply more when I get to my computer at home and reply again.


#7

Ideally, it would ideally affect all instances, that’s sort of what I’m looking for, but if there are no stable options for that goal, I could deal with the alternative.


#8

Hmm, I’m not seeing a change on my end here.

Edit: Ahhh, I see the soft-wrapping adjustment kick in.

But yeah, as Aerijo mentioned, I’m really trying to change the preferredLineLength so that the wrap-guide itself will move. I didn’t expect to run into so many issues with this honestly.


#9

This doesn’t seem to change the preferredLineLength for me, at least its not changing in the editor settings pane.


#10

No, that’s the point: it is a workaround that avoids calling config.set each time. I’m still working on why the proper method changes the config back to the original sometimes.


#11

Oh ok, I understand now. Yeah, I just got done watching the config.cson file continuously change the value of my currentIndex variable, back and forth between two settings, which is what is causing the preferred line length to get stuck in a state of perpetually flickering back and forth.


#12

From my top quality investigation (heaps of console.logs), I am confident that
(1) It’s very difficult to debug events when you can’t access half the process (main vs render), and
(2) the bug is caused by two consecutive config.set calls. It seems that method has a lot of baggage, which can be seen by logging when it’s called. I could consistently reproduce the issue when normal, but commenting out the first set (your package’s one), I couldn’t reproduce it.

Scratch that, here’s a fix

atom.config.set ('change-line-length.currentIndex', newIndex,  { save: false });
atom.config.set ('editor.preferredLineLength', preferredLineLengthValues[newIndex], { save: false });

I thought point 2 was onto something, before I realised I’d already put save: false in the second command. Anyway, that option will prevent it from writing to the config file, which prevents … whatever the bug was. Still fuzzy on that.

If you need to store the index value between sessions, I’d recommend doing it after a delay, or in the package deactivate method.

This probably prevents the config file wipe too, because it doesn’t write to it anymore, but I can’t confirm because that one is a pain to reproduce.

I’ve got to say though, it’s an interesting package. I’ve wanted similar (but far more complicated) behaviour where it would change the wrap length based on scope. E.g., so tables (in LaTeX) would not be wrapped, but regular text would.


#13

Thank you very. I will play around with this fix when I get back to the comp.

I do have a question though. If I’m not storing the index value after each call, how can I store it so that the index is updated after each execution? Obviously I can save it on package deactivation, but how can I temporarily store it before then? I’m not sure how to declare a persistent variable in the context of JavaScript and Atom.

Also, if I get this working, I’d like to give credit to you and @DamnedScholar for the help, unless either of you are opposed. How would you prefer me do that? I could link your GitHubs in the README.md, or however you prefer. Or if you wanted direct credit for the lines you wrote I’d accept pull requests. Anyway you prefer.

PS: I do plan to add a bit more information. I’m hoping to figure out how to add a status bar item that has the current PLL, such as “PLL: 80”, so I do plan to make it a little more “clean.”


#14

You are, it’s just not being written to the config file. The difference, besides working around the bug, is just that other Atom windows will not update the line width, and closing that window will (if you don’t write to the file beforehand) discard any changes. This is still technically a bug in the package, because if you do update the config file on close it will update other open windows, but the impact is relatively minor.

As for persistence in general, you can just declare a variable outside of the function scope. E.g.,

let currentIndex = atom.config.get ('change-line-length.currentIndex');

function changePreferredLineLength (adjustIndexBy)
{
    let preferredLineLengthValues = atom.config.get ('change-line-length.preferredLineLengthValues');
    let newIndex = currentIndex + adjustIndexBy;

    if (newIndex < 0)
        return;

    else if (newIndex > preferredLineLengthValues.length - 1)
        return;
   
    currentIndex = newIndex;
    atom.config.set ('editor.preferredLineLength', preferredLineLengthValues[newIndex], { save: false });
}

There are better ways of doing it, but at this scale that suffices. Similarly, you can get the preferredLineLengthValues values once, and keep them around.

The downside is that changes to the config won’t be recognised, but (1) you could set an observe event for config changes, or (2) just say restart Atom to take effect. Users like (1), devs & maintainers like (2).

Finally, if the values in the lengths array are just simple integers, you could make the setting take a string, split it by commas / spaces (e.g., /(?:\s|,)+/), and parseInt to convert to numbers.

let lengths = atom.config.get ('change-line-length.preferredLineLengthValues');
lengths = lengths.split(/(?:\s|,)+/).map(num => parseInt(num));

#16

Deleted my last post, I was wrong, bugginess still persists on my end, even with only calling config.set once on just the PLL. Wishing I could better understand why. I’ll keep poking around, but it might just be a project that will have to be set aside for the time being, really a bummer.


#17

Did you add the save: false option?


#18

Yeah I am. I did some refactoring to some of the code when I realized I should be checking the validity of the index before and after adjusting it, but the code is still the same. I’m still getting the flickering and sometimes triggering the package doesn’t actually work.


#19

I’m seeing the same flickering bugs when trying to insert settings in package settings. I recall this happening long before I ever considered making this package, but have since forgot about it I suppose. I would insert a value in the package settings and it would flicker before “sticking,” so I don’t think there’s really anything I can do. Even with the save: false flag, the same issues persist for me.


#20

I don’t personally care. I just hang around here and throw out ideas for fun. I will, however, point you to this GitHub repo, which contains guidelines for a standardized system for recognizing contributions. There’s even an Atom package to make it easier.


#21

@Aerijo, were your code suggestions working completely bug-free on your end?