Ace: Move breakpoints as code changes.

Created on 6 Mar 2013  ·  4Comments  ·  Source: ajaxorg/ace

Telling long story short: it would be great if breakpoints move as the code changes.

In more details: it would be great to have line handles which persist for the lines. This feature seems to be somewhat core for editing experience, and both breakpoints and error messages should be bound to the line handles.

Most helpful comment

I found a solution but it works only in my case because I allow only one breakpoint at once. With some improvement can fit also for the general case.
I used the on change listener on the editor instance and checked the number of lines that are modified by the action that fired the event. If these are more than one we are in the case when one or more lines are added or removed. The action parameter of the event help us to understand in which case we are.
To see if I have to move the breakpoint(s) I check if is between the starting and ending row of the modification. If it is in this range I have to move.

The code (quickly written) is the following:

aceEditor.on("change", function (e) {
                var breakpointsArray = aceEditor.session.getBreakpoints();
                if(Object.keys(aceEditor.session.getBreakpoints()).length>0){
                    if(e.lines.length>1){
                        var breakpoint = parseInt(Object.keys(breakpointsArray)[0]);
                        var lines = e.lines.length -1;
                        var start = e.start.row;
                        var end = e.end.row;
                        if(e.action==='insert'){
                            console.log('new lines',breakpoint, start , end );
                            if(breakpoint>start ){
                                console.log('breakpoint forward');
                                aceEditor.session.clearBreakpoint(breakpoint);
                                aceEditor.session.setBreakpoint(breakpoint + lines);
                            }
                        } else if(e.action==='remove'){
                            console.log('removed lines',breakpoint, start , end);
                            if(breakpoint>start && breakpoint<end ){
                                console.log('breakpoint remove');
                                aceEditor.session.clearBreakpoint(breakpoint);
                            }
                            if(breakpoint>=end ){
                                console.log('breakpoint behind');
                                aceEditor.session.clearBreakpoint(breakpoint);
                                aceEditor.session.setBreakpoint(breakpoint - lines);
                            }
                        }
                    }
                }
            });

Hope to have helped someone!

All 4 comments

In cloud9 we used change listener to update breakpoints in a separate array https://github.com/ajaxorg/cloud9/blob/master/plugins-client/ext.debugger/breakpoints.js#L170

Since ace uses array of strings, i am not sure how to best implement line handles, either full array of objects kept in sync with lines, or use ranges similar to handling of foldlines in edit_session. Tricky part is handling of lines that are being split or merged, since different types of line annotations need different behavior.

I believe that your solution for breakpoints would work well for annotations.

Tricky part is handling of lines that are being split or merged, since different types of line annotations need different >behavior.

Hmm.. I can't make up a single example. I would appreciate it if you shared some :)

I found a solution but it works only in my case because I allow only one breakpoint at once. With some improvement can fit also for the general case.
I used the on change listener on the editor instance and checked the number of lines that are modified by the action that fired the event. If these are more than one we are in the case when one or more lines are added or removed. The action parameter of the event help us to understand in which case we are.
To see if I have to move the breakpoint(s) I check if is between the starting and ending row of the modification. If it is in this range I have to move.

The code (quickly written) is the following:

aceEditor.on("change", function (e) {
                var breakpointsArray = aceEditor.session.getBreakpoints();
                if(Object.keys(aceEditor.session.getBreakpoints()).length>0){
                    if(e.lines.length>1){
                        var breakpoint = parseInt(Object.keys(breakpointsArray)[0]);
                        var lines = e.lines.length -1;
                        var start = e.start.row;
                        var end = e.end.row;
                        if(e.action==='insert'){
                            console.log('new lines',breakpoint, start , end );
                            if(breakpoint>start ){
                                console.log('breakpoint forward');
                                aceEditor.session.clearBreakpoint(breakpoint);
                                aceEditor.session.setBreakpoint(breakpoint + lines);
                            }
                        } else if(e.action==='remove'){
                            console.log('removed lines',breakpoint, start , end);
                            if(breakpoint>start && breakpoint<end ){
                                console.log('breakpoint remove');
                                aceEditor.session.clearBreakpoint(breakpoint);
                            }
                            if(breakpoint>=end ){
                                console.log('breakpoint behind');
                                aceEditor.session.clearBreakpoint(breakpoint);
                                aceEditor.session.setBreakpoint(breakpoint - lines);
                            }
                        }
                    }
                }
            });

Hope to have helped someone!

I modified alessandrocaprarelli's solution above to work for multiple breakpoints, may be helpful for others:

editor.on("change", function (e) {
    if (e.lines.length > 1 && (e.action==='insert' || e.action==='remove')){
        const breakpointsArrayOld = editor.session.getBreakpoints();
        let breakpointsArrayNew = [];

        const amountOfLinesAffected = e.lines.length - 1;
        const startRow = e.start.row;
        const endRow = e.end.row;

        for (const key of Object.keys(breakpointsArrayOld)) {
            let breakpointRow = parseInt(key)

            if (e.action==='insert') {  // new lines
                if (breakpointRow > startRow ){
                    // breakpoint forward
                    breakpointsArrayNew[breakpointRow + amountOfLinesAffected] = "ace_breakpoint"
                }
                else {
                    // unaffected by insert
                    breakpointsArrayNew[breakpointRow] = "ace_breakpoint"
                }
            }
            else if (e.action==='remove') {  // removed lines
                if (breakpointRow > startRow && breakpointRow <= endRow ){
                    // breakpoint removed
                }
                else if (breakpointRow >= endRow ){
                    // breakpoint behind
                    breakpointsArrayNew[breakpointRow - amountOfLinesAffected] = "ace_breakpoint"
                }
                else {
                    // unaffected by remove
                    breakpointsArrayNew[breakpointRow] = "ace_breakpoint"
                }
            }
        }

        // remove all old breakpoints
        for (const key of Object.keys(breakpointsArrayOld)) {
            let breakpointRow = parseInt(key)
            editor.session.clearBreakpoint(breakpointRow);
        }

        // add all new breakpoints
        for (const key of Object.keys(breakpointsArrayNew)) {
            let breakpointRow = parseInt(key)
            editor.session.setBreakpoint(breakpointRow);
        }
    }
})
Was this page helpful?
0 / 5 - 0 ratings