<commit_before>var addDirtyProps = require('../add-dirty-props');
var util = require('../util');
var noop = require('../no-op');
var passThrough = require('../pass-through');
var debounce = require('../debounce');

module.exports = function(_grid, name, lengthName, defaultSize) {
    var grid = _grid;

    var descriptors = [];
    var numFixed = 0;
    var numHeaders = 0;
    var makeDirtyClean = require('../dirty-clean');
    var dirtyClean = makeDirtyClean(grid);
    var builderDirtyClean = makeDirtyClean(grid);
    var selected = [];


    function setDescriptorsDirty(eventOptional) {
        var event = eventOptional || {};
        event.type = 'grid-' + name + '-change';
        grid.eventLoop.fire(event);
        dirtyClean.setDirty();
        builderDirtyClean.setDirty();
    }

    var fireSelectionChange = debounce(function() {
        grid.eventLoop.fire('grid-' + name + '-selection-change');
    }, 1);

    function updateDescriptorIndices() {
        selected = [];
        descriptors.forEach(function(descriptor, i) {
            descriptor.index = i;
            if (descriptor.selected) {
                selected.push(i);
            }
        });
    }

    var api = {
        areBuildersDirty: builderDirtyClean.isDirty,
        isDirty: dirtyClean.isDirty,
        defaultSize: defaultSize,
        add: function(toAdd) {
            if (!toAdd) {
                return;
            }

            if (!util.isArray(toAdd)) {
                toAdd = [toAdd];
            }
            toAdd.forEach(function(descriptor) {
                if (descriptor.header) {
                    descriptors.splice(numHeaders, 0, descriptor);
                    numFixed++;
                    numHeaders++;
                } else {
                    // if the column is fixed and the last one added is fixed (we only allow fixed at the beginning for now)
                    if (descriptor.fixed) {
                        if (!descriptors.length || descriptors[descriptors.length - 1].fixed) {
                            numFixed++;
                        } else {
                            throw 'Cannot add a fixed column after an unfixed one';
                        }
                    }
                    descriptors.push(descriptor);
                }
            });
            updateDescriptorIndices();
            setDescriptorsDirty({
                action: 'add',
                descriptors: toAdd
            });
        },
        addHeaders: function(toAdd) {
            if (!toAdd) {
                return;
            }

            if (!util.isArray(toAdd)) {
                toAdd = [toAdd];
            }
            toAdd.forEach(function(header) {
                header.header = true;
            });
            api.add(toAdd);
        },
        header: function(index) {
            return descriptors[index];
        },
        get: function(index) {
            return descriptors[index];
        },
        length: function(includeHeaders) {
            var subtract = includeHeaders ? 0 : numHeaders;
            return descriptors.length - subtract;
        },
        remove: function(descriptor, dontUpdateIndex) {
            var index = descriptors.indexOf(descriptor);
            if (index !== -1) {
                descriptors.splice(index, 1);
                if (descriptor.header) {
                    numFixed--;
                    numHeaders--;
                } else if (descriptor.fixed) {
                    numFixed--;
                }
            }
            if (!dontUpdateIndex) {
                updateDescriptorIndices();
                setDescriptorsDirty({
                    action: 'remove',
                    descriptors: [descriptor]
                });
            }
        },
        clear: function(includeHeaders) {
            var removed = descriptors.slice(0).map(function(descriptor) {
                if (includeHeaders || !descriptor.header) {
                    api.remove(descriptor, true);
                    return descriptor;
                }
            });
            updateDescriptorIndices();
            setDescriptorsDirty({
                action: 'remove',
                descriptors: removed
            });
        },
        move: function(fromIndexes, target, after) {
            if (!util.isArray(fromIndexes)) {
                fromIndexes = [fromIndexes];
            }

            if (fromIndexes.length === 1) {
                // the single move case is easier and doesn't require the after hint
                var from = fromIndexes[0];
                descriptors.splice(target, 0, descriptors.splice(from, 1)[0]);
                setDescriptorsDirty({
                    action: 'move',
                    descriptors: [api.get(from), api.get(target)]
                });
            } else {
                var toValue = descriptors[target];
                var removed = fromIndexes.sort(function compareNumbers(a, b) {
                    return b - a;
                }).map(function(fromIndex) {
                    var removedDescriptors = descriptors.splice(fromIndex, 1);
                    return removedDescriptors[0];

                });
                removed.reverse();
                var spliceArgs = [descriptors.indexOf(toValue) + (after ? 1 : 0), 0].concat(removed);
                descriptors.splice.apply(descriptors, spliceArgs);
                updateDescriptorIndices();
                setDescriptorsDirty({
                    action: 'move',
                    descriptors: removed.concat(toValue)
                });
            }
        },
        numHeaders: function() {
            return numHeaders;
        },
        numFixed: function(excludeHeaders) {
            return numFixed - (excludeHeaders ? numHeaders : 0);
        },
        toVirtual: function(dataIndex) {
            return dataIndex + api.numHeaders();
        },
        toData: function(virtualIndex) {
            return virtualIndex - api.numHeaders();
        },

        select: function(indexes, dontFire) {
            if (!util.isArray(indexes)) {
                indexes = [indexes];
            }
            var changes = indexes.map(function(idx) {
                var descriptor = api[name](idx);
                if (!descriptor.selected) {
                    descriptor.selected = true;
                    selected.push(idx);
                    return idx;
                }
            });
            if (changes.length && !dontFire) {
                fireSelectionChange();
            }
        },
        deselect: function(indexes, dontFire) {
            if (!util.isArray(indexes)) {
                indexes = [indexes];
            }
            var changes = indexes.map(function(idx) {
                var descriptor = api[name](idx);
                if (descriptor.selected) {
                    descriptor.selected = false;
                    selected.splice(selected.indexOf(idx), 1);
                    return idx;
                }
            });
            if (changes.length && !dontFire) {
                fireSelectionChange();
            }
        },
        toggleSelect: function(index) {
            var descriptor = api[name](index);
            if (descriptor.selected) {
                api.deselect(index);
            } else {
                api.select(index);
            }
        },
        clearSelected: function() {
            // have to make a copy or we are iterating the same array we're removing from yikes.
            return api.deselect(api.getSelected().slice(0));
        },
        getSelected: function() {
            return selected;
        },
        create: function(builder) {
            var descriptor = {};
            var fixed = false;<commit_msg>filter out descriptorless indexes and warn<commit_after>var addDirtyProps = require('../add-dirty-props');
var util = require('../util');
var noop = require('../no-op');
var passThrough = require('../pass-through');
var debounce = require('../debounce');

module.exports = function(_grid, name, lengthName, defaultSize) {
    var grid = _grid;

    var descriptors = [];
    var numFixed = 0;
    var numHeaders = 0;
    var makeDirtyClean = require('../dirty-clean');
    var dirtyClean = makeDirtyClean(grid);
    var builderDirtyClean = makeDirtyClean(grid);
    var selected = [];


    function setDescriptorsDirty(eventOptional) {
        var event = eventOptional || {};
        event.type = 'grid-' + name + '-change';
        grid.eventLoop.fire(event);
        dirtyClean.setDirty();
        builderDirtyClean.setDirty();
    }

    var fireSelectionChange = debounce(function() {
        grid.eventLoop.fire('grid-' + name + '-selection-change');
    }, 1);

    function updateDescriptorIndices() {
        selected = [];
        descriptors.forEach(function(descriptor, i) {
            descriptor.index = i;
            if (descriptor.selected) {
                selected.push(i);
            }
        });
    }

    var api = {
        areBuildersDirty: builderDirtyClean.isDirty,
        isDirty: dirtyClean.isDirty,
        defaultSize: defaultSize,
        add: function(toAdd) {
            if (!toAdd) {
                return;
            }

            if (!util.isArray(toAdd)) {
                toAdd = [toAdd];
            }
            toAdd.forEach(function(descriptor) {
                if (descriptor.header) {
                    descriptors.splice(numHeaders, 0, descriptor);
                    numFixed++;
                    numHeaders++;
                } else {
                    // if the column is fixed and the last one added is fixed (we only allow fixed at the beginning for now)
                    if (descriptor.fixed) {
                        if (!descriptors.length || descriptors[descriptors.length - 1].fixed) {
                            numFixed++;
                        } else {
                            throw 'Cannot add a fixed column after an unfixed one';
                        }
                    }
                    descriptors.push(descriptor);
                }
            });
            updateDescriptorIndices();
            setDescriptorsDirty({
                action: 'add',
                descriptors: toAdd
            });
        },
        addHeaders: function(toAdd) {
            if (!toAdd) {
                return;
            }

            if (!util.isArray(toAdd)) {
                toAdd = [toAdd];
            }
            toAdd.forEach(function(header) {
                header.header = true;
            });
            api.add(toAdd);
        },
        header: function(index) {
            return descriptors[index];
        },
        get: function(index) {
            return descriptors[index];
        },
        length: function(includeHeaders) {
            var subtract = includeHeaders ? 0 : numHeaders;
            return descriptors.length - subtract;
        },
        remove: function(descriptor, dontUpdateIndex) {
            var index = descriptors.indexOf(descriptor);
            if (index !== -1) {
                descriptors.splice(index, 1);
                if (descriptor.header) {
                    numFixed--;
                    numHeaders--;
                } else if (descriptor.fixed) {
                    numFixed--;
                }
            }
            if (!dontUpdateIndex) {
                updateDescriptorIndices();
                setDescriptorsDirty({
                    action: 'remove',
                    descriptors: [descriptor]
                });
            }
        },
        clear: function(includeHeaders) {
            var removed = descriptors.slice(0).map(function(descriptor) {
                if (includeHeaders || !descriptor.header) {
                    api.remove(descriptor, true);
                    return descriptor;
                }
            });
            updateDescriptorIndices();
            setDescriptorsDirty({
                action: 'remove',
                descriptors: removed
            });
        },
        move: function(fromIndexes, target, after) {
            if (!util.isArray(fromIndexes)) {
                fromIndexes = [fromIndexes];
            }

            if (fromIndexes.length === 1) {
                // the single move case is easier and doesn't require the after hint
                var from = fromIndexes[0];
                descriptors.splice(target, 0, descriptors.splice(from, 1)[0]);
                setDescriptorsDirty({
                    action: 'move',
                    descriptors: [api.get(from), api.get(target)]
                });
            } else {
                var toValue = descriptors[target];
                var removed = fromIndexes.sort(function compareNumbers(a, b) {
                    return b - a;
                }).map(function(fromIndex) {
                    var removedDescriptors = descriptors.splice(fromIndex, 1);
                    return removedDescriptors[0];

                });
                removed.reverse();
                var spliceArgs = [descriptors.indexOf(toValue) + (after ? 1 : 0), 0].concat(removed);
                descriptors.splice.apply(descriptors, spliceArgs);
                updateDescriptorIndices();
                setDescriptorsDirty({
                    action: 'move',
                    descriptors: removed.concat(toValue)
                });
            }
        },
        numHeaders: function() {
            return numHeaders;
        },
        numFixed: function(excludeHeaders) {
            return numFixed - (excludeHeaders ? numHeaders : 0);
        },
        toVirtual: function(dataIndex) {
            return dataIndex + api.numHeaders();
        },
        toData: function(virtualIndex) {
            return virtualIndex - api.numHeaders();
        },

        select: function(indexes, dontFire) {
            if (!util.isArray(indexes)) {
                indexes = [indexes];
            }
            var changes = indexes.filter(function(idx) {
                var hasDescriptor = !!api[name](idx);
                if (!hasDescriptor) {
                    console.warn('Tried to select index that had no descriptor', idx);
                }
                return hasDescriptor;
            }).map(function(idx) {
                var descriptor = api[name](idx);
                if (!descriptor.selected) {
                    descriptor.selected = true;
                    selected.push(idx);
                    return idx;
                }
            });
            if (changes.length && !dontFire) {
                fireSelectionChange();
            }
        },
        deselect: function(indexes, dontFire) {
            if (!util.isArray(indexes)) {
                indexes = [indexes];
            }
            var changes = indexes.filter(function(idx) {
                var hasDescriptor = !!api[name](idx);
                if (!hasDescriptor) {
                    console.warn('Tried to deselect index that had no descriptor', idx);
                }
                return hasDescriptor;
            }).map(function(idx) {
                var descriptor = api[name](idx);
                if (descriptor.selected) {
                    descriptor.selected = false;
                    selected.splice(selected.indexOf(idx), 1);
                    return idx;
                }
            });
            if (changes.length && !dontFire) {
                fireSelectionChange();
            }
        },
        toggleSelect: function(index) {
            var descriptor = api[name](index);
            if (descriptor.selected) {
                api.deselect(index);
            } else {
                api.select(index);
            }
        },
        clearSelected: function() {
            // have to make a copy or we are iterating the same array we're removing from yikes.
            return api.deselect(api.getSelected().slice(0));
        },
        getSelected: function() {
            return selected;
        },
        create: function(builder) {
            var descriptor = {};
            var fixed = false;
{'fraction_non_alphanumeric': 0.10650009277011566, 'fraction_numerical': 0.0027212567258333847, 'mean_word_length': 1.308021695689409, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, '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': '1359026', 'n_tokens_mistral': 4037, 'n_tokens_neox': 3830, 'n_words': 842}