<commit_before>/*
 * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved.
 *  
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"), 
 * to deal in the Software without restriction, including without limitation 
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 * and/or sell copies of the Software, and to permit persons to whom the 
 * Software is furnished to do so, subject to the following conditions:
 *  
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *  
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 * DEALINGS IN THE SOFTWARE.
 * 
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global define, $ */


/**
 * Text-editing commands that apply to whichever Editor is currently focused
 */
define(function (require, exports, module) {
    "use strict";

    // Load dependent modules
    var Commands           = require("command/Commands"),
        Strings            = require("strings"),
        CommandManager     = require("command/CommandManager"),
        EditorManager      = require("editor/EditorManager"),
        StringUtils        = require("utils/StringUtils"),
        TokenUtils         = require("utils/TokenUtils"),
        CodeMirror         = require("thirdparty/CodeMirror/lib/codemirror"),
        _                  = require("thirdparty/lodash");
    
    /**
     * List of constants
     */
    var DIRECTION_UP    = -1;
    var DIRECTION_DOWN  = +1;
        
    /**
     * @private
     * Creates special regular expressions that matches the line prefix but not the block prefix or suffix
     * @param {!string} lineSyntax  a line comment prefix
     * @param {!string} blockSyntax  a block comment prefix or suffix
     * @return {RegExp}
     */
    function _createSpecialLineExp(lineSyntax, blockSyntax) {
        var i, character, escapedCharacter,
            subExps   = [],
            prevChars = "";
        
        for (i = lineSyntax.length; i < blockSyntax.length; i++) {
            character = blockSyntax.charAt(i);
            escapedCharacter = StringUtils.regexEscape(character);
            subExps.push(prevChars + "[^" + escapedCharacter + "]");
            if (prevChars) {
                subExps.push(prevChars + "$");
            }
            prevChars += escapedCharacter;
        }
        return new RegExp("^\\s*" + StringUtils.regexEscape(lineSyntax) + "($|" + subExps.join("|") + ")");
    }
    
    /**
     * @private
     * Creates regular expressions for multiple line comment prefixes
     * @param {!Array.<string>} prefixes  the line comment prefixes
     * @param {string=} blockPrefix  the block comment prefix
     * @param {string=} blockSuffix  the block comment suffix
     * @return {Array.<RegExp>}
     */
    function _createLineExpressions(prefixes, blockPrefix, blockSuffix) {
        var lineExp = [], escapedPrefix, nothingPushed;
        
        prefixes.forEach(function (prefix) {
            escapedPrefix = StringUtils.regexEscape(prefix);
            nothingPushed = true;
            
            if (blockPrefix && blockPrefix.indexOf(prefix) === 0) {
                lineExp.push(_createSpecialLineExp(prefix, blockPrefix));
                nothingPushed = false;
            }
            if (blockSuffix && blockPrefix !== blockSuffix && blockSuffix.indexOf(prefix) === 0) {
                lineExp.push(_createSpecialLineExp(prefix, blockSuffix));
                nothingPushed = false;
            }
            if (nothingPushed) {
                lineExp.push(new RegExp("^\\s*" + escapedPrefix));
            }
        });
        return lineExp;
    }
    
    /**
     * @private
     * Returns true if any regular expression matches the given string
     * @param {!string} string  where to look
     * @param {!Array.<RegExp>} expressions  what to look
     * @return {boolean}
     */
    function _matchExpressions(string, expressions) {
        return expressions.some(function (exp) {
            return string.match(exp);
        });
    }
    
    /**
     * @private
     * Returns the line comment prefix that best matches the string. Since there might be line comment prefixes
     * that are prefixes of other line comment prefixes, it searches through all and returns the longest line
     * comment prefix that matches the string.
     * @param {!string} string  where to look
     * @param {!Array.<RegExp>} expressions  the line comment regular expressions
     * @param {!Array.<string>} prefixes  the line comment prefixes
     * @return {string}
     */
    function _getLinePrefix(string, expressions, prefixes) {
        var result = null;
        expressions.forEach(function (exp, index) {
            if (string.match(exp) && ((result && result.length < prefixes[index].length) || !result)) {
                result = prefixes[index];
            }
        });
        return result;
    }
    
    /**
     * @private
     * Searches between startLine and endLine to check if there is at least one line commented with a line comment, and
     * skips all the block comments.
     * @param {!Editor} editor
     * @param {!number} startLine  valid line inside the document
     * @param {!number} endLine  valid line inside the document
     * @param {!Array.<RegExp>} lineExp  an array of line comment prefixes regular expressions
     * @return {boolean} true if there is at least one uncommented line
     */
    function _containsNotLineComment(editor, startLine, endLine, lineExp) {
        var i, line,
            containsNotLineComment = false;
        
        for (i = startLine; i <= endLine; i++) {
            line = editor.document.getLine(i);
            // A line is commented out if it starts with 0-N whitespace chars, then a line comment prefix
            if (line.match(/\S/) && !_matchExpressions(line, lineExp)) {
                containsNotLineComment = true;
                break;
            }
        }
        return containsNotLineComment;
    }
    
    /**
     * @private
     * Generates an edit that adds or removes line-comment tokens to all the lines in the selected range,
     * preserving selection and cursor position. Applies to currently focused Editor. The given selection
     * must already be a line selection in the form returned by `Editor.convertToLineSelections()`.
     * 
     * If all non-whitespace lines are already commented out, then we uncomment; otherwise we comment
     * out. Commenting out adds the prefix at column 0 of every line. Uncommenting removes the first prefix
     * on each line (if any - empty lines might not have one).
     *
     * @param {!Editor} editor
     * @param {!Array.<string>} prefixes, e.g. ["//"]
     * @param {string=} blockPrefix, e.g. "<!--"
     * @param {string=} blockSuffix, e.g. "-->"
     * @param {!Editor} editor The editor to edit within.
     * @param {!{selectionForEdit: {start:{line:number, ch:number}, end:{line:number, ch:number}, reversed:boolean, primary:boolean}, 
     *           selectionsToTrack: Array.<{start:{line:number, ch:number}, end:{line:number, ch:number}, reversed:boolean, primary:boolean}>}}
     *      lineSel A line selection as returned from `Editor.convertToLineSelections()`. `selectionForEdit` is the selection to perform
     *      the line comment operation on, and `selectionsToTrack` are a set of selections associated with this line that need to be
     *      tracked through the edit.
     * @return {{edit: {text: string, start:{line: number, ch: number}, end:?{line: number, ch: number}}|Array.<{text: string, start:{line: number, ch: number}, end:?{line: number, ch: number}}>,
     *                  selection: {start:{line:number, ch:number}, end:{line:number, ch:number}, primary:boolean, reversed: boolean, isBeforeEdit: boolean}>}|
     *                  Array.<{start:{line:number, ch:number}, end:{line:number, ch:number}, primary:boolean, reversed: boolean, isBeforeEdit: boolean}>}}
     *      An edit description suitable for including in the edits array passed to `Document.doMultipleEdits()`.
     */
    function _getLineCommentPrefixEdit(editor, prefixes, blockPrefix, blockSuffix, lineSel) {
        var doc         = editor.document,
            sel         = lineSel.selectionForEdit,
            trackedSels = lineSel.selectionsToTrack,
            lineExp     = _createLineExpressions(prefixes, blockPrefix, blockSuffix),
            startLine   = sel.start.line,
            endLine     = sel.end.line,
            editGroup   = [];

        // In full-line selection, cursor pos is start of next line - but don't want to modify that line
        if (sel.end.ch === 0) {
            endLine--;
        }

        // Decide if we're commenting vs. un-commenting
        // Are there any non-blank lines that aren't commented out? (We ignore blank lines because
        // some editors like Sublime don't comment them out)
        var i, line, prefix, commentI,
            containsNotLineComment = _containsNotLineComment(editor, startLine, endLine, lineExp);
        
        if (containsNotLineComment) {
            // Comment out - prepend the first prefix to each line
            line = doc.getLine(startLine);
            var originalCursorPosition = line.search(/\S|$/);
            
            var firstCharPosition,cursorPosition = originalCursorPosition;
            
            for (i = startLine; i <= endLine; i++) {
                //ignore the first line and recalculate cursor position for first non white space char of every line
                if(i!=startLine){
                    line = doc.getLine(i);
                    var firstCharPosition = line.search(/\S|$/);
                }
                
                //if the non space first character position is before original start position , put comment at the new position otherwise older pos
                if(firstCharPosition<originalCursorPosition){
                    cursorPosition= firstCharPosition;
                }else{
                    cursorPosition= originalCursorPosition;
                }
                editGroup.push({text: prefixes[0], start: {line: i, ch: cursorPosition}});
            }

            // Make sure tracked selections include the prefix that was added at start of range
            _.each(trackedSels, function (trackedSel) {
                if (trackedSel.start.ch === 0 && CodeMirror.cmpPos(trackedSel.start, trackedSel.end) !== 0) {
                    trackedSel.start = {line: trackedSel.start.line, ch: 0};
                    trackedSel.end = {line: trackedSel.end.line, ch: (trackedSel.end.line === endLine ? trackedSel.end.ch + prefixes[0].length : 0)};
                } else {
                    trackedSel.isBeforeEdit = true;
                }
            });
        } else {
            // Uncomment - remove the prefix on each line (if any)
            for (i = startLine; i <= endLine; i++) {
                line   = doc.getLine(i);<commit_msg>lint error of equality - lint is awesome<commit_after>/*
 * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved.
 *  
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"), 
 * to deal in the Software without restriction, including without limitation 
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 * and/or sell copies of the Software, and to permit persons to whom the 
 * Software is furnished to do so, subject to the following conditions:
 *  
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *  
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 * DEALINGS IN THE SOFTWARE.
 * 
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global define, $ */


/**
 * Text-editing commands that apply to whichever Editor is currently focused
 */
define(function (require, exports, module) {
    "use strict";

    // Load dependent modules
    var Commands           = require("command/Commands"),
        Strings            = require("strings"),
        CommandManager     = require("command/CommandManager"),
        EditorManager      = require("editor/EditorManager"),
        StringUtils        = require("utils/StringUtils"),
        TokenUtils         = require("utils/TokenUtils"),
        CodeMirror         = require("thirdparty/CodeMirror/lib/codemirror"),
        _                  = require("thirdparty/lodash");
    
    /**
     * List of constants
     */
    var DIRECTION_UP    = -1;
    var DIRECTION_DOWN  = +1;
        
    /**
     * @private
     * Creates special regular expressions that matches the line prefix but not the block prefix or suffix
     * @param {!string} lineSyntax  a line comment prefix
     * @param {!string} blockSyntax  a block comment prefix or suffix
     * @return {RegExp}
     */
    function _createSpecialLineExp(lineSyntax, blockSyntax) {
        var i, character, escapedCharacter,
            subExps   = [],
            prevChars = "";
        
        for (i = lineSyntax.length; i < blockSyntax.length; i++) {
            character = blockSyntax.charAt(i);
            escapedCharacter = StringUtils.regexEscape(character);
            subExps.push(prevChars + "[^" + escapedCharacter + "]");
            if (prevChars) {
                subExps.push(prevChars + "$");
            }
            prevChars += escapedCharacter;
        }
        return new RegExp("^\\s*" + StringUtils.regexEscape(lineSyntax) + "($|" + subExps.join("|") + ")");
    }
    
    /**
     * @private
     * Creates regular expressions for multiple line comment prefixes
     * @param {!Array.<string>} prefixes  the line comment prefixes
     * @param {string=} blockPrefix  the block comment prefix
     * @param {string=} blockSuffix  the block comment suffix
     * @return {Array.<RegExp>}
     */
    function _createLineExpressions(prefixes, blockPrefix, blockSuffix) {
        var lineExp = [], escapedPrefix, nothingPushed;
        
        prefixes.forEach(function (prefix) {
            escapedPrefix = StringUtils.regexEscape(prefix);
            nothingPushed = true;
            
            if (blockPrefix && blockPrefix.indexOf(prefix) === 0) {
                lineExp.push(_createSpecialLineExp(prefix, blockPrefix));
                nothingPushed = false;
            }
            if (blockSuffix && blockPrefix !== blockSuffix && blockSuffix.indexOf(prefix) === 0) {
                lineExp.push(_createSpecialLineExp(prefix, blockSuffix));
                nothingPushed = false;
            }
            if (nothingPushed) {
                lineExp.push(new RegExp("^\\s*" + escapedPrefix));
            }
        });
        return lineExp;
    }
    
    /**
     * @private
     * Returns true if any regular expression matches the given string
     * @param {!string} string  where to look
     * @param {!Array.<RegExp>} expressions  what to look
     * @return {boolean}
     */
    function _matchExpressions(string, expressions) {
        return expressions.some(function (exp) {
            return string.match(exp);
        });
    }
    
    /**
     * @private
     * Returns the line comment prefix that best matches the string. Since there might be line comment prefixes
     * that are prefixes of other line comment prefixes, it searches through all and returns the longest line
     * comment prefix that matches the string.
     * @param {!string} string  where to look
     * @param {!Array.<RegExp>} expressions  the line comment regular expressions
     * @param {!Array.<string>} prefixes  the line comment prefixes
     * @return {string}
     */
    function _getLinePrefix(string, expressions, prefixes) {
        var result = null;
        expressions.forEach(function (exp, index) {
            if (string.match(exp) && ((result && result.length < prefixes[index].length) || !result)) {
                result = prefixes[index];
            }
        });
        return result;
    }
    
    /**
     * @private
     * Searches between startLine and endLine to check if there is at least one line commented with a line comment, and
     * skips all the block comments.
     * @param {!Editor} editor
     * @param {!number} startLine  valid line inside the document
     * @param {!number} endLine  valid line inside the document
     * @param {!Array.<RegExp>} lineExp  an array of line comment prefixes regular expressions
     * @return {boolean} true if there is at least one uncommented line
     */
    function _containsNotLineComment(editor, startLine, endLine, lineExp) {
        var i, line,
            containsNotLineComment = false;
        
        for (i = startLine; i <= endLine; i++) {
            line = editor.document.getLine(i);
            // A line is commented out if it starts with 0-N whitespace chars, then a line comment prefix
            if (line.match(/\S/) && !_matchExpressions(line, lineExp)) {
                containsNotLineComment = true;
                break;
            }
        }
        return containsNotLineComment;
    }
    
    /**
     * @private
     * Generates an edit that adds or removes line-comment tokens to all the lines in the selected range,
     * preserving selection and cursor position. Applies to currently focused Editor. The given selection
     * must already be a line selection in the form returned by `Editor.convertToLineSelections()`.
     * 
     * If all non-whitespace lines are already commented out, then we uncomment; otherwise we comment
     * out. Commenting out adds the prefix at column 0 of every line. Uncommenting removes the first prefix
     * on each line (if any - empty lines might not have one).
     *
     * @param {!Editor} editor
     * @param {!Array.<string>} prefixes, e.g. ["//"]
     * @param {string=} blockPrefix, e.g. "<!--"
     * @param {string=} blockSuffix, e.g. "-->"
     * @param {!Editor} editor The editor to edit within.
     * @param {!{selectionForEdit: {start:{line:number, ch:number}, end:{line:number, ch:number}, reversed:boolean, primary:boolean}, 
     *           selectionsToTrack: Array.<{start:{line:number, ch:number}, end:{line:number, ch:number}, reversed:boolean, primary:boolean}>}}
     *      lineSel A line selection as returned from `Editor.convertToLineSelections()`. `selectionForEdit` is the selection to perform
     *      the line comment operation on, and `selectionsToTrack` are a set of selections associated with this line that need to be
     *      tracked through the edit.
     * @return {{edit: {text: string, start:{line: number, ch: number}, end:?{line: number, ch: number}}|Array.<{text: string, start:{line: number, ch: number}, end:?{line: number, ch: number}}>,
     *                  selection: {start:{line:number, ch:number}, end:{line:number, ch:number}, primary:boolean, reversed: boolean, isBeforeEdit: boolean}>}|
     *                  Array.<{start:{line:number, ch:number}, end:{line:number, ch:number}, primary:boolean, reversed: boolean, isBeforeEdit: boolean}>}}
     *      An edit description suitable for including in the edits array passed to `Document.doMultipleEdits()`.
     */
    function _getLineCommentPrefixEdit(editor, prefixes, blockPrefix, blockSuffix, lineSel) {
        var doc         = editor.document,
            sel         = lineSel.selectionForEdit,
            trackedSels = lineSel.selectionsToTrack,
            lineExp     = _createLineExpressions(prefixes, blockPrefix, blockSuffix),
            startLine   = sel.start.line,
            endLine     = sel.end.line,
            editGroup   = [];

        // In full-line selection, cursor pos is start of next line - but don't want to modify that line
        if (sel.end.ch === 0) {
            endLine--;
        }

        // Decide if we're commenting vs. un-commenting
        // Are there any non-blank lines that aren't commented out? (We ignore blank lines because
        // some editors like Sublime don't comment them out)
        var i, line, prefix, commentI,
            containsNotLineComment = _containsNotLineComment(editor, startLine, endLine, lineExp);
        
        if (containsNotLineComment) {
            // Comment out - prepend the first prefix to each line
            line = doc.getLine(startLine);
            var originalCursorPosition = line.search(/\S|$/);
            
            var firstCharPosition,cursorPosition = originalCursorPosition;
            
            for (i = startLine; i <= endLine; i++) {
                //ignore the first line and recalculate cursor position for first non white space char of every line
                if(i!==startLine){
                    line = doc.getLine(i);
                    var firstCharPosition = line.search(/\S|$/);
                }
                
                //if the non space first character position is before original start position , put comment at the new position otherwise older pos
                if(firstCharPosition<originalCursorPosition){
                    cursorPosition= firstCharPosition;
                }else{
                    cursorPosition= originalCursorPosition;
                }
                editGroup.push({text: prefixes[0], start: {line: i, ch: cursorPosition}});
            }

            // Make sure tracked selections include the prefix that was added at start of range
            _.each(trackedSels, function (trackedSel) {
                if (trackedSel.start.ch === 0 && CodeMirror.cmpPos(trackedSel.start, trackedSel.end) !== 0) {
                    trackedSel.start = {line: trackedSel.start.line, ch: 0};
                    trackedSel.end = {line: trackedSel.end.line, ch: (trackedSel.end.line === endLine ? trackedSel.end.ch + prefixes[0].length : 0)};
                } else {
                    trackedSel.isBeforeEdit = true;
                }
            });
        } else {
            // Uncomment - remove the prefix on each line (if any)
            for (i = startLine; i <= endLine; i++) {
                line   = doc.getLine(i);
{'fraction_non_alphanumeric': 0.09943083822007588, 'fraction_numerical': 0.0017247326664367024, 'mean_word_length': 2.3052586575459597, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6701480', 'n_tokens_mistral': 6198, 'n_tokens_neox': 5927, 'n_words': 2067}