gutter-container.js
1 const { Emitter } = require('event-kit'); 2 const Gutter = require('./gutter'); 3 4 module.exports = class GutterContainer { 5 constructor(textEditor) { 6 this.gutters = []; 7 this.textEditor = textEditor; 8 this.emitter = new Emitter(); 9 } 10 11 scheduleComponentUpdate() { 12 this.textEditor.scheduleComponentUpdate(); 13 } 14 15 destroy() { 16 // Create a copy, because `Gutter::destroy` removes the gutter from 17 // GutterContainer's @gutters. 18 const guttersToDestroy = this.gutters.slice(0); 19 for (let gutter of guttersToDestroy) { 20 if (gutter.name !== 'line-number') { 21 gutter.destroy(); 22 } 23 } 24 this.gutters = []; 25 this.emitter.dispose(); 26 } 27 28 addGutter(options) { 29 options = options || {}; 30 const gutterName = options.name; 31 if (gutterName === null) { 32 throw new Error('A name is required to create a gutter.'); 33 } 34 if (this.gutterWithName(gutterName)) { 35 throw new Error( 36 'Tried to create a gutter with a name that is already in use.' 37 ); 38 } 39 const newGutter = new Gutter(this, options); 40 41 let inserted = false; 42 // Insert the gutter into the gutters array, sorted in ascending order by 'priority'. 43 // This could be optimized, but there are unlikely to be many gutters. 44 for (let i = 0; i < this.gutters.length; i++) { 45 if (this.gutters[i].priority >= newGutter.priority) { 46 this.gutters.splice(i, 0, newGutter); 47 inserted = true; 48 break; 49 } 50 } 51 if (!inserted) { 52 this.gutters.push(newGutter); 53 } 54 this.scheduleComponentUpdate(); 55 this.emitter.emit('did-add-gutter', newGutter); 56 return newGutter; 57 } 58 59 getGutters() { 60 return this.gutters.slice(); 61 } 62 63 gutterWithName(name) { 64 for (let gutter of this.gutters) { 65 if (gutter.name === name) { 66 return gutter; 67 } 68 } 69 return null; 70 } 71 72 observeGutters(callback) { 73 for (let gutter of this.getGutters()) { 74 callback(gutter); 75 } 76 return this.onDidAddGutter(callback); 77 } 78 79 onDidAddGutter(callback) { 80 return this.emitter.on('did-add-gutter', callback); 81 } 82 83 onDidRemoveGutter(callback) { 84 return this.emitter.on('did-remove-gutter', callback); 85 } 86 87 /* 88 Section: Private Methods 89 */ 90 91 // Processes the destruction of the gutter. Throws an error if this gutter is 92 // not within this gutterContainer. 93 removeGutter(gutter) { 94 const index = this.gutters.indexOf(gutter); 95 if (index > -1) { 96 this.gutters.splice(index, 1); 97 this.scheduleComponentUpdate(); 98 this.emitter.emit('did-remove-gutter', gutter.name); 99 } else { 100 throw new Error( 101 'The given gutter cannot be removed because it is not ' + 102 'within this GutterContainer.' 103 ); 104 } 105 } 106 107 // The public interface is Gutter::decorateMarker or TextEditor::decorateMarker. 108 addGutterDecoration(gutter, marker, options) { 109 if (gutter.type === 'line-number') { 110 options.type = 'line-number'; 111 } else { 112 options.type = 'gutter'; 113 } 114 options.gutterName = gutter.name; 115 return this.textEditor.decorateMarker(marker, options); 116 } 117 };