From 8ad6a66f6d57030807ae9e1d163bfea70398c1ab Mon Sep 17 00:00:00 2001 From: abe33 Date: Sun, 6 Mar 2016 15:53:47 +0100 Subject: [PATCH] Implement sorted decorations rendering Closes #453 --- lib/main.js | 11 ++++++ lib/minimap-element.js | 5 +++ lib/mixins/canvas-drawer.js | 47 ++++++++++++++--------- lib/mixins/decoration-management.js | 18 ++++++++- lib/mixins/plugin-management.js | 43 ++++++++++++++++++--- spec/minimap-element-spec.js | 59 +++++++++++++++++++++++++++++ 6 files changed, 159 insertions(+), 24 deletions(-) diff --git a/lib/main.js b/lib/main.js index 55c97dcc..168e902f 100644 --- a/lib/main.js +++ b/lib/main.js @@ -255,6 +255,17 @@ class Main { return this.emitter.on('did-deactivate-plugin', callback) } + /** + * Registers a callback to listen to the `did-change-plugin-order` event of + * the package. + * + * @param {function(event:Object):void} callback the callback function + * @return {Disposable} a disposable to stop listening to the event + */ + onDidChangePluginOrder (callback) { + return this.emitter.on('did-change-plugin-order', callback) + } + /** * Returns the `Minimap` class * diff --git a/lib/minimap-element.js b/lib/minimap-element.js index 988faec6..97ee421f 100644 --- a/lib/minimap-element.js +++ b/lib/minimap-element.js @@ -2,6 +2,7 @@ import {CompositeDisposable, Disposable} from 'atom' import {EventsDelegation, AncestorsMethods} from 'atom-utils' +import Main from './main' import include from './decorators/include' import element from './decorators/element' import DOMStylesReader from './mixins/dom-styles-reader' @@ -666,6 +667,10 @@ export default class MinimapElement { this.requestUpdate() })) + this.subscriptions.add(Main.onDidChangePluginOrder(() => { + this.requestForcedUpdate() + })) + this.setStandAlone(this.minimap.isStandAlone()) if (this.width != null && this.height != null) { diff --git a/lib/mixins/canvas-drawer.js b/lib/mixins/canvas-drawer.js index aae41c43..e1c562dd 100644 --- a/lib/mixins/canvas-drawer.js +++ b/lib/mixins/canvas-drawer.js @@ -2,6 +2,7 @@ import _ from 'underscore-plus' import Mixin from 'mixto' +import Main from '../main' import CanvasLayer from '../canvas-layer' /** @@ -331,7 +332,8 @@ export default class CanvasDrawer extends Mixin { canvasHeight: canvasHeight, lineHeight: lineHeight, charWidth: charWidth, - charHeight: charHeight + charHeight: charHeight, + orders: Main.getPluginsOrder() } for (let screenRow = firstRow; screenRow <= lastRow; screenRow++) { @@ -339,9 +341,10 @@ export default class CanvasDrawer extends Mixin { renderData.yRow = renderData.row * lineHeight renderData.screenRow = screenRow - this.drawDecorations(screenRow, decorations, 'line', renderData, this.drawLineDecoration) - - this.drawDecorations(screenRow, decorations, 'highlight-under', renderData, this.drawHighlightDecoration) + this.drawDecorations(screenRow, decorations, renderData, { + 'line': this.drawLineDecoration, + 'highlight-under': this.drawHighlightDecoration + }) } this.backLayer.context.fill() @@ -374,7 +377,8 @@ export default class CanvasDrawer extends Mixin { canvasHeight: canvasHeight, lineHeight: lineHeight, charWidth: charWidth, - charHeight: charHeight + charHeight: charHeight, + orders: Main.getPluginsOrder() } for (let screenRow = firstRow; screenRow <= lastRow; screenRow++) { @@ -382,9 +386,10 @@ export default class CanvasDrawer extends Mixin { renderData.yRow = renderData.row * lineHeight renderData.screenRow = screenRow - this.drawDecorations(screenRow, decorations, 'highlight-over', renderData, this.drawHighlightDecoration) - - this.drawDecorations(screenRow, decorations, 'highlight-outline', renderData, this.drawHighlightOutlineDecoration) + this.drawDecorations(screenRow, decorations, renderData, { + 'highlight-outline': this.drawHighlightOutlineDecoration, + 'highlight-over': this.drawHighlightDecoration + }) } renderData.context.fill() @@ -513,19 +518,27 @@ export default class CanvasDrawer extends Mixin { * @param {number} screenRow the screen row index for which * render decorations * @param {Object} decorations the object containing all the decorations - * @param {string} type the type of decorations to render * @param {Object} renderData the object containing the render data - * @param {Fundtion} renderMethod the method to call to render - * the decorations + * @param {Object} types an object with the type to render as key and the + * render method as value * @access private */ - drawDecorations (screenRow, decorations, type, renderData, renderMethod) { - let ref - decorations = (ref = decorations[type]) != null ? ref[screenRow] : void 0 + drawDecorations (screenRow, decorations, renderData, types) { + let decorationsToRender = [] + + for (let i in types) { + decorationsToRender = decorationsToRender.concat( + decorations[i] != null ? decorations[i][screenRow] || [] : [] + ) + } + + decorationsToRender.sort((a, b) => + (renderData.orders[a.properties.plugin] || 0) - (renderData.orders[b.properties.plugin] || 0) + ) - if (decorations != null ? decorations.length : void 0) { - for (let i = 0, len = decorations.length; i < len; i++) { - renderMethod.call(this, decorations[i], renderData) + if (decorationsToRender != null ? decorationsToRender.length : void 0) { + for (let i = 0, len = decorationsToRender.length; i < len; i++) { + types[decorationsToRender[i].properties.type].call(this, decorationsToRender[i], renderData) } } } diff --git a/lib/mixins/decoration-management.js b/lib/mixins/decoration-management.js index 09950b1b..6daa5c6c 100644 --- a/lib/mixins/decoration-management.js +++ b/lib/mixins/decoration-management.js @@ -1,5 +1,7 @@ 'use babel' +import _ from 'underscore-plus' +import path from 'path' import Mixin from 'mixto' import {Emitter} from 'atom' import Decoration from '../decoration' @@ -307,7 +309,11 @@ export default class DecorationManagement extends Mixin { decorationParams.type = 'highlight-over' } - const {type} = decorationParams + const {type, plugin} = decorationParams + + if (plugin == null) { + decorationParams.plugin = this.getOriginatorPackageName() + } if (decorationParams.scope == null && decorationParams['class'] != null) { let cls = decorationParams['class'].split(' ').join('.') @@ -395,6 +401,16 @@ export default class DecorationManagement extends Mixin { return decoration } + getOriginatorPackageName () { + const line = new Error().stack.split('\n')[3] + const filePath = line.split('(')[1].replace(')', '') + const re = new RegExp( + atom.packages.getPackageDirPaths().join('|') + _.escapeRegExp(path.sep) + ) + const plugin = filePath.replace(re, '').split(path.sep)[0].replace(/minimap-|-minimap/, '') + return plugin.indexOf(path.sep) < 0 ? plugin : undefined + } + /** * Given two ranges, it returns an array of ranges representing the * differences between them. diff --git a/lib/mixins/plugin-management.js b/lib/mixins/plugin-management.js index 3e654e0a..208c3ed3 100644 --- a/lib/mixins/plugin-management.js +++ b/lib/mixins/plugin-management.js @@ -46,6 +46,14 @@ export default class PluginManagement extends Mixin { * @access private */ this.pluginsSubscriptions = {} + + /** + * A map that stores the display order for each plugin + * + * @type {Object} + * @access private + */ + this.pluginsOrderMap = {} } /** @@ -149,10 +157,10 @@ export default class PluginManagement extends Mixin { * @access private */ updatesPluginActivationState (name) { - let plugin = this.plugins[name] - let pluginActive = plugin.isActive() - let settingActive = atom.config.get(`minimap.plugins.${name}`) - let event = { name: name, plugin: plugin } + const plugin = this.plugins[name] + const pluginActive = plugin.isActive() + const settingActive = atom.config.get(`minimap.plugins.${name}`) + const event = { name: name, plugin: plugin } if (settingActive && !pluginActive) { plugin.activatePlugin() @@ -207,8 +215,10 @@ export default class PluginManagement extends Mixin { this.updatesPluginActivationState(name) })) - this.pluginsSubscriptions[name].add(atom.config.observe(orderSettingsKey, () => { - + this.pluginsSubscriptions[name].add(atom.config.observe(orderSettingsKey, (order) => { + this.updatePluginsOrderMap(name) + const event = { name: name, plugin: plugin, order: order } + this.emitter.emit('did-change-plugin-order', event) })) this.pluginsSubscriptions[name].add(atom.commands.add('atom-workspace', { @@ -216,8 +226,29 @@ export default class PluginManagement extends Mixin { this.togglePluginActivation(name) } })) + + this.updatePluginsOrderMap(name) + } + + /** + * Updates the display order in the map for the passed-in plugin name. + * + * @param {string} name the name of the plugin to update + * @access private + */ + updatePluginsOrderMap (name) { + const orderSettingsKey = `minimap.plugins.${name}DecorationsOrder` + + this.pluginsOrderMap[name] = atom.config.get(orderSettingsKey) } + /** + * Returns the plugins display order mapped by name. + * + * @return {Object} The plugins order by name + */ + getPluginsOrder () { return this.pluginsOrderMap } + /** * When the `minimap.displayPluginsControls` setting is toggled, * this function will unregister the commands and setting that diff --git a/spec/minimap-element-spec.js b/spec/minimap-element-spec.js index 4fb87d73..d89c6e42 100644 --- a/spec/minimap-element-spec.js +++ b/spec/minimap-element-spec.js @@ -1,6 +1,7 @@ 'use babel' import fs from 'fs-plus' +import Main from '../lib/main' import Minimap from '../lib/minimap' import MinimapElement from '../lib/minimap-element' import {stylesheet} from './helpers/workspace' @@ -23,6 +24,16 @@ function sleep (duration) { waitsFor(() => { return new Date() - t > duration }) } +function createPlugin () { + const plugin = { + active: false, + activatePlugin () { this.active = true }, + deactivatePlugin () { this.active = false }, + isActive () { return this.active } + } + return plugin +} + describe('MinimapElement', () => { let [editor, minimap, largeSample, mediumSample, smallSample, jasmineContent, editorElement, minimapElement, dir] = [] @@ -36,6 +47,7 @@ describe('MinimapElement', () => { atom.config.set('minimap.interline', 1) atom.config.set('minimap.textOpacity', 1) atom.config.set('minimap.smoothScrolling', true) + atom.config.set('minimap.plugins', {}) MinimapElement.registerViewProvider(Minimap) @@ -241,6 +253,53 @@ describe('MinimapElement', () => { expect(() => { nextAnimationFrame() }).not.toThrow() }) + it('renders the decorations based on the order settings', () => { + atom.config.set('minimap.displayPluginsControls', true) + + const pluginFoo = createPlugin() + const pluginBar = createPlugin() + + Main.registerPlugin('foo', pluginFoo) + Main.registerPlugin('bar', pluginBar) + + atom.config.set('minimap.plugins.fooDecorationsOrder', 1) + + const calls = [] + spyOn(minimapElement, 'drawLineDecoration').andCallFake((d) => { + calls.push(d.getProperties().plugin) + }) + spyOn(minimapElement, 'drawHighlightDecoration').andCallFake((d) => { + calls.push(d.getProperties().plugin) + }) + + minimap.decorateMarker(editor.markBufferRange([[1, 0], [1, 10]]), {type: 'line', color: '#0000FF', plugin: 'bar'}) + minimap.decorateMarker(editor.markBufferRange([[1, 0], [1, 10]]), {type: 'highlight-under', color: '#0000FF', plugin: 'foo'}) + + editorElement.setScrollTop(0) + + waitsFor(() => { return nextAnimationFrame !== noAnimationFrame }) + runs(() => { + nextAnimationFrame() + + expect(calls).toEqual(['bar', 'foo']) + + atom.config.set('minimap.plugins.fooDecorationsOrder', -1) + + calls.length = 0 + }) + + waitsFor(() => { return nextAnimationFrame !== noAnimationFrame }) + + runs(() => { + nextAnimationFrame() + + expect(calls).toEqual(['foo', 'bar']) + + Main.unregisterPlugin('foo') + Main.unregisterPlugin('bar') + }) + }) + it('renders the visible line decorations', () => { spyOn(minimapElement, 'drawLineDecoration').andCallThrough()