const {Emitter} = require('event-kit') const Gutter = require('./gutter') module.exports = class GutterContainer { constructor (textEditor) { this.gutters = [] this.textEditor = textEditor this.emitter = new Emitter() } scheduleComponentUpdate () { this.textEditor.scheduleComponentUpdate() } destroy () { // Create a copy, because `Gutter::destroy` removes the gutter from // GutterContainer's @gutters. const guttersToDestroy = this.gutters.slice(0) for (let gutter of guttersToDestroy) { if (gutter.name !== 'line-number') { gutter.destroy() } } this.gutters = [] this.emitter.dispose() } addGutter (options) { options = options || {} const gutterName = options.name if (gutterName === null) { throw new Error('A name is required to create a gutter.') } if (this.gutterWithName(gutterName)) { throw new Error('Tried to create a gutter with a name that is already in use.') } const newGutter = new Gutter(this, options) let inserted = false // Insert the gutter into the gutters array, sorted in ascending order by 'priority'. // This could be optimized, but there are unlikely to be many gutters. for (let i = 0; i < this.gutters.length; i++) { if (this.gutters[i].priority >= newGutter.priority) { this.gutters.splice(i, 0, newGutter) inserted = true break } } if (!inserted) { this.gutters.push(newGutter) } this.scheduleComponentUpdate() this.emitter.emit('did-add-gutter', newGutter) return newGutter } getGutters () { return this.gutters.slice() } gutterWithName (name) { for (let gutter of this.gutters) { if (gutter.name === name) { return gutter } } return null } observeGutters (callback) { for (let gutter of this.getGutters()) { callback(gutter) } return this.onDidAddGutter(callback) } onDidAddGutter (callback) { return this.emitter.on('did-add-gutter', callback) } onDidRemoveGutter (callback) { return this.emitter.on('did-remove-gutter', callback) } /* Section: Private Methods */ // Processes the destruction of the gutter. Throws an error if this gutter is // not within this gutterContainer. removeGutter (gutter) { const index = this.gutters.indexOf(gutter) if (index > -1) { this.gutters.splice(index, 1) this.scheduleComponentUpdate() this.emitter.emit('did-remove-gutter', gutter.name) } else { throw new Error('The given gutter cannot be removed because it is not ' + 'within this GutterContainer.' ) } } // The public interface is Gutter::decorateMarker or TextEditor::decorateMarker. addGutterDecoration (gutter, marker, options) { if (gutter.type === 'line-number') { options.type = 'line-number' } else { options.type = 'gutter' } options.gutterName = gutter.name return this.textEditor.decorateMarker(marker, options) } }