Automatically Wrap Lines During Typing


#1

I’ve been using Atom now as both a code editor and a plaintext editor. I manually break my lines at X chars (80 or 100, depending on what setting I’m in), or I use autoflow package to do it incrementally. I’ve been looking for a package that does this, a found one called Wraptor, however, it seems to only work half as I would expect. I’m not sure if there are others or not, but I can’t seem to find much.

Ideally, autobreak will:

  1. Automatically break lines at the current preferred line length. If a word is too long and crossing the PLL, it will insert the break before that word.
  2. It could intelligently re-break lines if you are inserting words into lines that were previously written. Say you just wrote a paragraph of text, and the package has been breaking the lines all along, but you realize you need to fix a line and it causes a new break to occur, Atom should remove and add breaks on the remaining lines until it is formatted properly again.
  3. Should insert the typist’s / coder’s preferred newline character.
  4. Be efficient at doing all of this.
  5. Have an option in the settings to choose which file types it would automatically apply to.

I think I’m going to take a crack at this, but I’m not entirely confident in myself for this sort of a project, as I only us JS in the context of making simple Atom packages. Any helpful hints or tips for the algorithm or structure of this package would be helpful and if anyone else is interested in helping out, I would gladly take PRs or step aside for someone else to make it.


#2

If you want a package to be able to rebreak lines for you, Atom will have to be able to tell the difference between a newline inserted based on wrapping and one that you put there on purpose.


#3

I think the package could differentiate between wanted and unwanted newlines based on their column. For instance, a newline at the 81st column (if the preferred line length is 80) is probably wanted. Obviously there would be more rules than that, but that’s just an example.

I’m fairly certain I could implement the rules for this, however, I’m not familiar enough with Atom to understand how to register listeners and such. I see the onDidChange() function. I can write the function that this function calls whenever the buffer changes, but I’m not sure how to have Atom call onDidChange()


#4

When you call the onDidChange() method on a TextEditor, you are preparing it to listen for when Atom says that the editor has changed, and your code will run after the event happens.


#5

So unfortunately I’m having trouble with getting the bare minimum code needed to start the package. As I said, I’m fairly certain I can write up code to define rules for when to break and how to break, but I’m struggling with the Atom boilerplate code. I’m trying to set it up so that when the package is loaded, the onDidChange() function is called automatically. I’m not sure about Javascript and Atom, but in C++, this would be placed into some sort of constructor function. It looks like activate(state) or serialize may be the place for this, but it isn’t currently working for me. Would you be willing to show me what I would need to change in order to get going? I don’t need a toggle function at all, this should not be manually turned on and off, it should happen automatically per every keystroke:

'use babel';

import { CompositeDisposable } from 'atom';

let editor = atom.workspace.getActiveTextEditor();

export default {

    subscriptions: null,

    activate(_state) {
        // Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
        this.subscriptions = new CompositeDisposable();
        editor.onDidChange(breakText());
    },

    deactivate() {
        this.subscriptions.dispose();
    },

    serialize() {
        editor.onDidChange(breakText()); // Neither this or the one in activate is being called upon the package being loaded
    },
};

function breakText() {
    console.log("Breaking text now"); // Just testing to see if the code works
}

As I’ve said before, developing in Atom is way out of my comfort zone and I don’t understand a lot of the boilerplate, this is what is tripping me up mostly. I’ve removed most of I think can be safely removed, stuff I don’t think this package will need, but I could be wrong.


#6

Don’t run that code in serialize(). If it does what I think it would do, it would end up crashing Atom from overwork. Code in activate() will run every time Atom is launched, and that’s all you require. You also can’t get the active TextEditor before there are any TextEditors in existence (and you wouldn’t want your package to only work on the first one). Fortunately, Atom gives us a way to get every TextEditor as it is created: atom.workspace.observeTextEditors(). Observe:

activate() {
  atom.workspace.observeTextEditors( (editor) => { editor.onDidChange( breakText() ) } )
},

Using a standard arrow notation, we pass an anonymous function to the method on atom.workspace that says, “Every time an editor is created, attach this conditional and then run the function I’ve left with it.”

Edited to fix syntax.


#7

Thank you very much for the code and for the explanation over this. It is greatly appreciated. I am, however, getting a syntax error on the anonymous function pass. I’m looking into the syntax for anonymous functions in JS to try to see what went wrong, but figured I would ask back since you wrote it.


#8

You may be mixing up JS with CS; lambda functions in JS are done with =>


#9

Yes, that’s exactly what happened. It doesn’t help that the keys are right next to one another on Qwerty. :confused:


#10

Yep, that fixed the syntax errors. This is the final result after the suggested base code. I’m not getting print statements to the console log on key presses. Really sorry for the lame questions. It is quite embarrassing to have received a bachelors degree in computer science and struggle with these small things in a different language under a different API than I usually use. I really need to take a solid 3-6 months and just study atom API.

'use babel';

export default {

    subscriptions: null,

    activate() {
        atom.workspace.observeTextEditors((editor) => { editor.onDidChange(breakText()) });
    },

    deactivate() {
        this.subscriptions.dispose();
    },
};

function breakText() {
    console.log("Breaking text now"); // Just testing to see if the code works
}

#11

Oh, I know what the problem probably is. You most likely created the package with the package generator, which spawns a package.json file that includes a key that often trips up newbies:

"activationCommands": {
    "atom-workspace": "break-text:toggle"
},

This line tells Atom to not load any package code until the toggle command is called. Unfortunately, this rule is enforced even if you have no such command. You can and should just delete that.