How to tab through markers like native snippets package


#1

I’m trying to create a package that allows users to tab through their currently selected ranges in a text editor much like the native snippets package. The idea is a user has multiple ranges selected, they run a command from this package and then when they hit tab it iterates over the previously selected ranges one by one for editing.

The apis are definitely all there from Atom to make this work but I’m running into several challenges that are already overcome by the native snippets package. I’m wondering if there’s any way to pass Atom (or some package) a collection of ranges and have it implement the same tab-through functionality as the snippets package. I’ve looked through the snippets package to see what’s going on but I can’t quite tell.

Thanks for any thoughts or ideas.


#2

You make a command that

  1. checks if the package has been activated (if not, return),
  2. checks if the latest cursor is at the end of the array of ranges (if so, return), and
  3. moves the cursor to the next range.

Then you can bind that command to tab and make sure to have your package be clear about if it’s been activated or not.


#3

Thanks for the response @DamnedScholar. I’m guessing that I’ll just need to build in all the functionality from scratch.

For example, when a user hits tab I can tab through the markers just fine but the e.preventDefault() doesn’t stop the tab key from indenting the active line. I also can’t seem to get the Disposable to unsubscribe from the keydown events. I’ll include my module below if anyone has any suggestions. The user would activate this module after selecting multiple text that they’d like to tab through. Then tab through them individually and edit each one.

var CompositeDisposable, Disposable, addEventListener, disposables, ref;

ref = require('atom'), Disposable = ref.Disposable, CompositeDisposable = ref.CompositeDisposable;

module.exports = {
    markers: [],
    disposables: new CompositeDisposable(),
    editor: null,
    start: function(){
        console.log('start');
        this.editor = atom.workspace.getActiveTextEditor();
        var selBufs = this.editor.getSelectedBufferRanges();

        if (selBufs.length > 1) {
            for (var i = 0; i < selBufs.length; i++) {
                this.markers.push(this.editor.markBufferRange(selBufs[i]));
            }
            this.subscribe();
        }

    },
    next: function(){
        console.log('next');
        if (this.markers.length) {
            var nextMarker = this.markers.shift();
            this.editor.setSelectedBufferRange(nextMarker.getBufferRange());
        } else {
            this.unsubscribe();
        }
    },
    subscribe: function(){
        console.log('subscribe');

        var editorView = atom.views.getView(this.editor);
        editorView.addEventListener('keydown', this.handler.bind(this), false);
        console.log('editorView', editorView, this);

        this.disposables.add(new Disposable(() => {
            console.log('removeEventListener', editorView, this);
            return editorView.removeEventListener('keydown', this.handler);
        }));

    },
    handler: function(e){
        console.log('handler');

        switch (e.code) {

            case 'Tab':
            console.log('this', this);
            e.preventDefault();
            this.next();
            break;

            case 'Escape':
            this.unsubscribe();
            break;

        }

    },
    unsubscribe: function(){
        console.log('unsubscribe');
        // Remove error tracking for tab
        this.markers = [];
        this.disposables.dispose();

    }

}

#4

Your code listening to keydown directly is operating on a different mechanism than the command matcher. Since e.preventDefault() isn’t working, maybe it’s a timing issue where the command matcher works first. You should consider trying it with atom.commands.add() so that you can make use of the Keybinding Resolver and the cascade properties of Atom’s keymaps.


#5

Thanks @DamnedScholar - I changed the event listener to user the regular keybindings (which is what the native snippets package does) and that solved that issue. I’ve now published the package tab-through-selections and it’s working great.