From ffcf1de3538d93dcb4cc1a119bd4f28fa578d3df Mon Sep 17 00:00:00 2001 From: t9md Date: Fri, 6 Jan 2017 04:26:19 +0900 Subject: [PATCH 01/18] WIP to rewrite --- lib/main.coffee | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/main.coffee b/lib/main.coffee index e4a3b79..7e64a7c 100644 --- a/lib/main.coffee +++ b/lib/main.coffee @@ -164,29 +164,30 @@ module.exports = highlightKeyword: (editor, scanRange, keyword, color) -> return [] unless editor.isAlive() - classNames = "quick-highlight #{color}" - pattern = ///#{_.escapeRegExp(keyword)}///g + + markerOptions = {invalidate: 'inside'} + decorationOptions = {type: 'highlight', class: "quick-highlight #{color}"} + decorations = [] - editor.scanInBufferRange pattern, scanRange, ({range}) => - decorations.push(@decorateRange(editor, range, {classNames})) + editor.scanInBufferRange ///#{_.escapeRegExp(keyword)}///g, scanRange, ({range}) -> + marker = editor.markBufferRange(range, markerOptions) + decorations.push(editor.decorateMarker(marker, decorationOptions)) decorations clearEditor: (editor) -> - if @decorationsByEditor.has(editor) - d.getMarker().destroy() for d in @decorationsByEditor.get(editor) + if decorations = @decorationsByEditor.get(editor) + for decoration in decorations + decoration.getMarker().destroy() @decorationsByEditor.delete(editor) clear: -> - @decorationsByEditor.forEach (decorations, editor) => - @clearEditor(editor) + @decorationsByEditor.forEach (decorations) -> + for decoration in decorations + decoration.getMarker().destroy() @decorationsByEditor.clear() @keywords.reset() @statusBarManager.clear() - decorateRange: (editor, range, {classNames}) -> - marker = editor.markBufferRange(range, invalidate: 'inside') - editor.decorateMarker(marker, {type: 'highlight', class: classNames}) - getCountForKeyword: (editor, keyword) -> getCountForKeyword(editor, keyword) From e177e2ed35b6be11be60a668ec3ed842f3365ce5 Mon Sep 17 00:00:00 2001 From: t9md Date: Fri, 6 Jan 2017 13:38:21 +0900 Subject: [PATCH 02/18] WIP spec Pass --- lib/main.coffee | 94 +++++++++++++++----------------- spec/quick-highlight-spec.coffee | 58 ++++++++++---------- 2 files changed, 74 insertions(+), 78 deletions(-) diff --git a/lib/main.coffee b/lib/main.coffee index 7e64a7c..9981403 100644 --- a/lib/main.coffee +++ b/lib/main.coffee @@ -11,34 +11,15 @@ settings = require './settings' getCursorWord } = require './utils' -# Utils -# ------------------------- -class KeywordManager - colors: ['01', '02', '03', '04', '05', '06', '07'] - index: null - constructor: -> - @reset() - add: (keyword) -> - @index = (@index + 1) % @colors.length - @kw2color[keyword] = @colors[@index] - delete: (keyword) -> - delete @kw2color[keyword] - has: (keyword) -> - keyword of @kw2color - reset: (keyword) -> - @kw2color = Object.create(null) - @index = -1 - each: (fn) -> - fn(keyword, color) for keyword, color of @kw2color - module.exports = config: settings.config + colorNumbers: ['01', '02', '03', '04', '05', '06', '07'] activate: (state) -> @subscriptions = new CompositeDisposable @emitter = new Emitter - @decorationsByEditor = new Map - @keywords = new KeywordManager() + @markersByEditor = new Map + @colorByKeyword = new Map() @statusBarManager = new StatusBarManager toggle = @toggle.bind(this) @@ -92,13 +73,14 @@ module.exports = @subscriptions.remove(arg) clearSelectionDecoration: -> - d.getMarker().destroy() for d in @selectionDecorations ? [] - @selectionDecorations = null + for marker in @selectionMarkers ? [] + marker.destroy() + @selectionMarkers = null shouldExcludeEditor: (editor) -> + editorElement = editor.element scopes = settings.get('highlightSelectionExcludeScopes') - scopes.some (scope) -> - matchScope(editor.element, scope) + scopes.some (scope) -> matchScope(editorElement, scope) highlightSelection: (editor) -> @clearSelectionDecoration() @@ -107,7 +89,7 @@ module.exports = return unless @needToHighlightSelection(selection) keyword = selection.getText() return unless scanRange = getVisibleBufferRange(editor) - @selectionDecorations = @highlightKeyword(editor, scanRange, keyword, 'box-selection') + @selectionMarkers = @highlightKeyword(editor, scanRange, keyword, 'box-selection') needToHighlightSelection: (selection) -> switch @@ -124,7 +106,7 @@ module.exports = @clear() @clearSelectionDecoration() @subscriptions.dispose() - {@decorationsByEditor, @subscriptions, @keywords} = {} + {@markersByEditor, @subscriptions} = {} locked: false isLocked: -> @@ -138,16 +120,25 @@ module.exports = @locked = false value + getNextColor: -> + @colorIndex ?= -1 + @colorIndex = (@colorIndex + 1) % @colorNumbers.length + @colorNumbers[@colorIndex] + toggle: (editor, keyword) -> keyword ?= editor.getSelectedText() or @withLock(-> getCursorWord(editor)) - if @keywords.has(keyword) - @keywords.delete(keyword) + + if @colorByKeyword.has(keyword) + @colorByKeyword.delete(keyword) @statusBarManager.clear() else - @keywords.add(keyword) + @colorByKeyword.set(keyword, @getNextColor()) + if settings.get('displayCountOnStatusBar') @statusBarManager.update(@getCountForKeyword(editor, keyword)) - @refreshEditor(editor) for editor in getVisibleEditors() + + for editor in getVisibleEditors() + @refreshEditor(editor) refreshEditor: (editor) -> @clearEditor(editor) @@ -155,12 +146,13 @@ module.exports = renderEditor: (editor) -> return unless scanRange = getVisibleBufferRange(editor) - decorations = [] + markers = [] decorationStyle = settings.get('decorate') - @keywords.each (keyword, color) => - color = "#{decorationStyle}-#{color}" - decorations = decorations.concat(@highlightKeyword(editor, scanRange, keyword, color)) - @decorationsByEditor.set(editor, decorations) + + @colorByKeyword.forEach (color, keyword) => + colorName = "#{decorationStyle}-#{color}" + markers = markers.concat(@highlightKeyword(editor, scanRange, keyword, colorName)) + @markersByEditor.set(editor, markers) highlightKeyword: (editor, scanRange, keyword, color) -> return [] unless editor.isAlive() @@ -168,24 +160,28 @@ module.exports = markerOptions = {invalidate: 'inside'} decorationOptions = {type: 'highlight', class: "quick-highlight #{color}"} - decorations = [] + markers = [] editor.scanInBufferRange ///#{_.escapeRegExp(keyword)}///g, scanRange, ({range}) -> marker = editor.markBufferRange(range, markerOptions) - decorations.push(editor.decorateMarker(marker, decorationOptions)) - decorations + editor.decorateMarker(marker, decorationOptions) + markers.push(marker) + markers clearEditor: (editor) -> - if decorations = @decorationsByEditor.get(editor) - for decoration in decorations - decoration.getMarker().destroy() - @decorationsByEditor.delete(editor) + if markers = @markersByEditor.get(editor) + for marker in markers + marker.destroy() + @markersByEditor.delete(editor) clear: -> - @decorationsByEditor.forEach (decorations) -> - for decoration in decorations - decoration.getMarker().destroy() - @decorationsByEditor.clear() - @keywords.reset() + @markersByEditor.forEach (markers) -> + for marker in markers + marker.destroy() + @markersByEditor.clear() + + @colorByKeyword.clear() + @colorIndex = null + @statusBarManager.clear() getCountForKeyword: (editor, keyword) -> diff --git a/spec/quick-highlight-spec.coffee b/spec/quick-highlight-spec.coffee index ef7ef7b..7a92786 100644 --- a/spec/quick-highlight-spec.coffee +++ b/spec/quick-highlight-spec.coffee @@ -92,57 +92,57 @@ describe "quick-highlight", -> describe "quick-highlight:toggle", -> it "decorate keyword under cursor", -> - expect(main.keywords.has('orange')).toBe true - expect(main.keywords.has('apple')).toBe false + expect(main.colorByKeyword.has('orange')).toBe true + expect(main.colorByKeyword.has('apple')).toBe false expect(editor).toHaveDecorations length: 3, color: '01', text: 'orange' it "remove decoration when if already decorated", -> - expect(main.keywords.has('orange')).toBe true - expect(main.keywords.has('apple')).toBe false + expect(main.colorByKeyword.has('orange')).toBe true + expect(main.colorByKeyword.has('apple')).toBe false expect(editor).lengthOfDecorationsToBe 3 dispatchCommand('quick-highlight:toggle') - expect(main.keywords.has('orange')).toBe false - expect(main.keywords.has('apple')).toBe false + expect(main.colorByKeyword.has('orange')).toBe false + expect(main.colorByKeyword.has('apple')).toBe false expect(editor).lengthOfDecorationsToBe 0 it "can decorate multiple keyword simultaneously", -> - expect(main.keywords.has('orange')).toBe true - expect(main.keywords.has('apple')).toBe false + expect(main.colorByKeyword.has('orange')).toBe true + expect(main.colorByKeyword.has('apple')).toBe false expect(editor).lengthOfDecorationsToBe 3 editor.setCursorScreenPosition [1, 12] dispatchCommand('quick-highlight:toggle') - expect(main.keywords.has('orange')).toBe true - expect(main.keywords.has('apple')).toBe true + expect(main.colorByKeyword.has('orange')).toBe true + expect(main.colorByKeyword.has('apple')).toBe true expect(editor).lengthOfDecorationsToBe 6 expect(editor).toHaveDecorations color: '01', length: 3, text: 'orange' expect(editor).toHaveDecorations color: '02', length: 3, text: 'apple' it "destroy decoration when editor is destroyed", -> - expect(main.keywords.has('orange')).toBe true + expect(main.colorByKeyword.has('orange')).toBe true expect(editor).lengthOfDecorationsToBe 3 editor.destroy() expect(editor).toHaveAllMarkerDestoyed() describe "quick-highlight:clear", -> it "clear all decorations", -> - expect(main.keywords.has('orange')).toBe true - expect(main.keywords.has('apple')).toBe false + expect(main.colorByKeyword.has('orange')).toBe true + expect(main.colorByKeyword.has('apple')).toBe false expect(editor).lengthOfDecorationsToBe 3 editor.setCursorScreenPosition [1, 12] dispatchCommand('quick-highlight:toggle') - expect(main.keywords.has('orange')).toBe true - expect(main.keywords.has('apple')).toBe true + expect(main.colorByKeyword.has('orange')).toBe true + expect(main.colorByKeyword.has('apple')).toBe true expect(editor).lengthOfDecorationsToBe 6 dispatchCommand('quick-highlight:clear') expect(editor).lengthOfDecorationsToBe 0 - expect(main.keywords.has('orange')).toBe false - expect(main.keywords.has('apple')).toBe false + expect(main.colorByKeyword.has('orange')).toBe false + expect(main.colorByKeyword.has('apple')).toBe false expect(editor).toHaveAllMarkerDestoyed() - expect(main.decorationsByEditor.has(editor)).toBe false + expect(main.markersByEditor.has(editor)).toBe false describe "multiple editors is displayed", -> [editor2, editor2Element] = [] @@ -158,14 +158,14 @@ describe "quick-highlight", -> expect(editor2).toBeActiveEditor() dispatchCommand('quick-highlight:clear') expect(editor).lengthOfDecorationsToBe 0 - expect(main.decorationsByEditor.has(editor)).toBe false + expect(main.markersByEditor.has(editor)).toBe false expect(editor2).lengthOfDecorationsToBe 0 - expect(main.decorationsByEditor.has(editor2)).toBe false + expect(main.markersByEditor.has(editor2)).toBe false it "can decorate keyword across visible editors", -> dispatchCommand('quick-highlight:toggle', editor2Element) - expect(main.keywords.has('orange')).toBe true - expect(main.keywords.has('apple')).toBe false + expect(main.colorByKeyword.has('orange')).toBe true + expect(main.colorByKeyword.has('apple')).toBe false expect(editor).toHaveDecorations color: '01', length: 3, text: 'orange' expect(editor2).toHaveDecorations color: '01', length: 3, text: 'orange' @@ -181,8 +181,8 @@ describe "quick-highlight", -> it "decorate keywords when new editor was opened", -> dispatchCommand('quick-highlight:toggle', editor2Element) - expect(main.keywords.has('orange')).toBe true - expect(main.keywords.has('apple')).toBe false + expect(main.colorByKeyword.has('orange')).toBe true + expect(main.colorByKeyword.has('apple')).toBe false editor3 = null pathSample3 = atom.project.resolvePath "sample-3" @@ -200,10 +200,10 @@ describe "quick-highlight", -> beforeEach -> dispatchCommand('quick-highlight:clear') expect(editor).lengthOfDecorationsToBe 0 - expect(main.keywords.has('orange')).toBe false - expect(main.keywords.has('apple')).toBe false + expect(main.colorByKeyword.has('orange')).toBe false + expect(main.colorByKeyword.has('apple')).toBe false expect(editor).toHaveAllMarkerDestoyed() - expect(main.decorationsByEditor.has(editor)).toBe false + expect(main.markersByEditor.has(editor)).toBe false it "decorate selected keyword", -> dispatchCommand('editor:select-word') @@ -319,8 +319,8 @@ describe "quick-highlight", -> dispatchCommand('quick-highlight:toggle', element: editorElement4) editor4.setCursorBufferPosition [3, 0] dispatchCommand('quick-highlight:toggle', element: editorElement4) - expect(main.keywords.has('orange')).toBe true - expect(main.keywords.has('apple')).toBe true + expect(main.colorByKeyword.has('orange')).toBe true + expect(main.colorByKeyword.has('apple')).toBe true describe "decorate only visible area", -> it "update decoration on scroll", -> From 580dd2bfd1064c8b4ffee5d6889e15418770a199 Mon Sep 17 00:00:00 2001 From: t9md Date: Fri, 6 Jan 2017 14:37:29 +0900 Subject: [PATCH 03/18] cleanup BEFORE aggressive refactoring. excercise DONE --- lib/main.coffee | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/lib/main.coffee b/lib/main.coffee index 9981403..09d8443 100644 --- a/lib/main.coffee +++ b/lib/main.coffee @@ -28,25 +28,31 @@ module.exports = 'quick-highlight:clear': => @clear() debouncedhighlightSelection = null - settings.observe 'highlightSelectionDelay', (delay) => - debouncedhighlightSelection = _.debounce(@highlightSelection.bind(this), delay) + highlightSelection = @highlightSelection.bind(this) + settings.observe 'highlightSelectionDelay', (delay) -> + debouncedhighlightSelection = _.debounce(highlightSelection, delay) + + refreshEditor = @refreshEditor.bind(this) @subscribe atom.workspace.observeTextEditors (editor) => editorSubs = new CompositeDisposable - editorSubs.add editor.onDidStopChanging => - return unless atom.workspace.getActiveTextEditor() is editor - URI = editor.getURI() - for e in getVisibleEditors() when (e.getURI() is URI) - @refreshEditor(e) + editorSubs.add editor.onDidStopChanging -> + if editor is atom.workspace.getActiveTextEditor() + URI = editor.getURI() + isChangedEditor = (editor) -> editor.getURI() is URI + getVisibleEditors() + .filter(isChangedEditor) + .forEach(refreshEditor) editorElement = editor.element - editorSubs.add(editor.element.onDidChangeScrollTop => @refreshEditor(editor)) + refresh = @refreshEditor.bind(this, editor) + editorSubs.add(editorElement.onDidChangeScrollTop(refresh)) # [FIXME] # @refreshEditor depends on editorElement.getVisibleRowRange() but it return # [undefined, undefined] when it called on editorElement which is not attached yet. # So we separately need to cover this case from Atom v1.1.0 - editorSubs.add(editorElement.onDidAttach => @refreshEditor(editor)) + editorSubs.add(editorElement.onDidAttach(refresh)) editorSubs.add editor.onDidChangeSelectionRange ({selection}) => if selection.isLastSelection() and not @isLocked() @@ -178,7 +184,6 @@ module.exports = for marker in markers marker.destroy() @markersByEditor.clear() - @colorByKeyword.clear() @colorIndex = null From 2ef43f78cd98473313ee2897f31a01df7126ae66 Mon Sep 17 00:00:00 2001 From: t9md Date: Fri, 6 Jan 2017 16:22:51 +0900 Subject: [PATCH 04/18] getting improve, extract as QuickHighlightView --- lib/main.coffee | 110 +++++++------------------------- lib/quick-highlight-view.coffee | 75 ++++++++++++++++++++++ 2 files changed, 99 insertions(+), 86 deletions(-) create mode 100644 lib/quick-highlight-view.coffee diff --git a/lib/main.coffee b/lib/main.coffee index 09d8443..6072145 100644 --- a/lib/main.coffee +++ b/lib/main.coffee @@ -2,9 +2,9 @@ _ = require 'underscore-plus' StatusBarManager = require './status-bar-manager' settings = require './settings' +QuickHighlightView = require './quick-highlight-view' { - matchScope getVisibleEditors getVisibleBufferRange getCountForKeyword @@ -18,8 +18,8 @@ module.exports = activate: (state) -> @subscriptions = new CompositeDisposable @emitter = new Emitter - @markersByEditor = new Map - @colorByKeyword = new Map() + @viewByEditor = new Map + @colorsByKeyword = new Map() @statusBarManager = new StatusBarManager toggle = @toggle.bind(this) @@ -28,13 +28,16 @@ module.exports = 'quick-highlight:clear': => @clear() debouncedhighlightSelection = null - highlightSelection = @highlightSelection.bind(this) + highlightSelection = -> settings.observe 'highlightSelectionDelay', (delay) -> debouncedhighlightSelection = _.debounce(highlightSelection, delay) refreshEditor = @refreshEditor.bind(this) @subscribe atom.workspace.observeTextEditors (editor) => + view = new QuickHighlightView(editor, this) + @viewByEditor.set(view, editor) + editorSubs = new CompositeDisposable editorSubs.add editor.onDidStopChanging -> if editor is atom.workspace.getActiveTextEditor() @@ -46,7 +49,6 @@ module.exports = editorElement = editor.element refresh = @refreshEditor.bind(this, editor) - editorSubs.add(editorElement.onDidChangeScrollTop(refresh)) # [FIXME] # @refreshEditor depends on editorElement.getVisibleRowRange() but it return @@ -57,20 +59,20 @@ module.exports = editorSubs.add editor.onDidChangeSelectionRange ({selection}) => if selection.isLastSelection() and not @isLocked() debouncedhighlightSelection(editor) - editorSubs.add(editorElement.onDidChangeScrollTop => @highlightSelection(editor)) editorSubs.add editor.onDidDestroy => - @clearEditor(editor) + # @clearEditor(editor) editorSubs.dispose() @unsubscribe(editorSubs) @subscribe(editorSubs) @subscribe atom.workspace.onDidChangeActivePaneItem (item) => + null @statusBarManager.clear() - if item?.getText? # Check if instance of TextEditor - @refreshEditor(item) - @highlightSelection(item) + # if item?.getText? # Check if instance of TextEditor + # @refreshEditor(item) + # @highlightSelection(item) subscribe: (args...) -> @subscriptions.add args... @@ -78,41 +80,13 @@ module.exports = unsubscribe: (arg) -> @subscriptions.remove(arg) - clearSelectionDecoration: -> - for marker in @selectionMarkers ? [] - marker.destroy() - @selectionMarkers = null - - shouldExcludeEditor: (editor) -> - editorElement = editor.element - scopes = settings.get('highlightSelectionExcludeScopes') - scopes.some (scope) -> matchScope(editorElement, scope) - - highlightSelection: (editor) -> - @clearSelectionDecoration() - return if @shouldExcludeEditor(editor) - selection = editor.getLastSelection() - return unless @needToHighlightSelection(selection) - keyword = selection.getText() - return unless scanRange = getVisibleBufferRange(editor) - @selectionMarkers = @highlightKeyword(editor, scanRange, keyword, 'box-selection') - - needToHighlightSelection: (selection) -> - switch - when (not settings.get('highlightSelection')) - , selection.isEmpty() - , not selection.getBufferRange().isSingleLine() - , selection.getText().length < settings.get('highlightSelectionMinimumLength') - , /[^\S]/.test(selection.getText()) - false - else - true - deactivate: -> @clear() - @clearSelectionDecoration() + @viewByEditor.forEach (view) -> + view.destroy() + @subscriptions.dispose() - {@markersByEditor, @subscriptions} = {} + {@subscriptions} = {} locked: false isLocked: -> @@ -133,60 +107,24 @@ module.exports = toggle: (editor, keyword) -> keyword ?= editor.getSelectedText() or @withLock(-> getCursorWord(editor)) - - if @colorByKeyword.has(keyword) - @colorByKeyword.delete(keyword) + if @colorsByKeyword.has(keyword) + @colorsByKeyword.delete(keyword) @statusBarManager.clear() else - @colorByKeyword.set(keyword, @getNextColor()) - + @colorsByKeyword.set(keyword, @getNextColor()) if settings.get('displayCountOnStatusBar') @statusBarManager.update(@getCountForKeyword(editor, keyword)) + @emitter.emit('did-change-keyword', {@colorsByKeyword}) - for editor in getVisibleEditors() - @refreshEditor(editor) + onDidChangeKeyword: (fn) -> @emitter.on('did-change-keyword', fn) refreshEditor: (editor) -> - @clearEditor(editor) - @renderEditor(editor) - - renderEditor: (editor) -> - return unless scanRange = getVisibleBufferRange(editor) - markers = [] - decorationStyle = settings.get('decorate') - - @colorByKeyword.forEach (color, keyword) => - colorName = "#{decorationStyle}-#{color}" - markers = markers.concat(@highlightKeyword(editor, scanRange, keyword, colorName)) - @markersByEditor.set(editor, markers) - - highlightKeyword: (editor, scanRange, keyword, color) -> - return [] unless editor.isAlive() - - markerOptions = {invalidate: 'inside'} - decorationOptions = {type: 'highlight', class: "quick-highlight #{color}"} - - markers = [] - editor.scanInBufferRange ///#{_.escapeRegExp(keyword)}///g, scanRange, ({range}) -> - marker = editor.markBufferRange(range, markerOptions) - editor.decorateMarker(marker, decorationOptions) - markers.push(marker) - markers - - clearEditor: (editor) -> - if markers = @markersByEditor.get(editor) - for marker in markers - marker.destroy() - @markersByEditor.delete(editor) + null clear: -> - @markersByEditor.forEach (markers) -> - for marker in markers - marker.destroy() - @markersByEditor.clear() - @colorByKeyword.clear() + @colorsByKeyword.clear() + @emitter.emit('did-change-keyword', {@colorsByKeyword}) @colorIndex = null - @statusBarManager.clear() getCountForKeyword: (editor, keyword) -> diff --git a/lib/quick-highlight-view.coffee b/lib/quick-highlight-view.coffee new file mode 100644 index 0000000..454414f --- /dev/null +++ b/lib/quick-highlight-view.coffee @@ -0,0 +1,75 @@ +_ = require 'underscore-plus' +{CompositeDisposable} = require 'atom' +settings = require './settings' + +{ + getVisibleEditors + matchScope +} = require './utils' + +module.exports = + class QuickHighlightView + constructor: (@editor, @main) -> + @editorElement = @editor.element + @markerLayers = [] + + @disposables = new CompositeDisposable + @disposables.add( + @editor.onDidDestroy(@destroy.bind(this)) + @editor.onDidChangeSelectionRange(@highlightSelection.bind(this)) + @main.onDidChangeKeyword(@refresh.bind(this)) + ) + + needSelectionHighlight: (selection) -> + editorElement = @editor.element + scopes = settings.get('highlightSelectionExcludeScopes') + switch + when (not settings.get('highlightSelection')) + , selection.isEmpty() + , (scopes.some (scope) -> matchScope(editorElement, scope)) + , not selection.getBufferRange().isSingleLine() + , selection.getText().length < settings.get('highlightSelectionMinimumLength') + , /[^\S]/.test(selection.getText()) + false + else + true + + highlightSelection: ({selection}) -> + @markerLayerForSelectionHighlight?.destroy() + return unless @needSelectionHighlight(selection) + if keyword = selection.getText() + @markerLayerForSelectionHighlight = @highlight(keyword, 'box-selection') + + highlight: (keyword, color) -> + markerLayer = @editor.addMarkerLayer() + decorationStyle = settings.get('decorate') + if color is 'box-selection' + colorName = color + else + colorName = "#{decorationStyle}-#{color}" + decorationOptions = {type: 'highlight', class: "quick-highlight #{colorName}"} + @editor.decorateMarkerLayer(markerLayer, decorationOptions) + + markerOptions = {invalidate: 'inside'} + pattern = ///#{_.escapeRegExp(keyword)}///g + @editor.scan pattern, ({range, matchText}) -> + markerLayer.markBufferRange(range, markerOptions) + markerLayer + + refresh: ({colorsByKeyword}) -> + decorationStyle = settings.get('decorate') + @clear() + colorsByKeyword.forEach (color, keyword) => + @markerLayers.push(@highlight(keyword, color)) + + isVisible: -> + @editor in getVisibleEditors() + + clear: -> + for markerLayer in @markerLayers + markerLayer.destroy() + + destroy: -> + @clear() + @markerLayerForSelectionHighlight?.destroy() + @disposable.dispose() From 51b737238a8ef723fca6880a4623e00dd9d59fdf Mon Sep 17 00:00:00 2001 From: t9md Date: Fri, 6 Jan 2017 18:00:01 +0900 Subject: [PATCH 05/18] 1st phase rewrite DONE --- lib/keyword-manager.coffee | 43 +++++++++++++ lib/main.coffee | 107 ++++---------------------------- lib/quick-highlight-view.coffee | 34 ++++++++-- lib/utils.coffee | 24 ------- 4 files changed, 82 insertions(+), 126 deletions(-) create mode 100644 lib/keyword-manager.coffee diff --git a/lib/keyword-manager.coffee b/lib/keyword-manager.coffee new file mode 100644 index 0000000..d9bcb5b --- /dev/null +++ b/lib/keyword-manager.coffee @@ -0,0 +1,43 @@ +{CompositeDisposable, Emitter} = require 'atom' + +module.exports = + class KeywordManager + colorNumbers: ['01', '02', '03', '04', '05', '06', '07'] + + onDidChangeKeyword: (fn) -> + @emitter.on('did-change-keyword', fn) + + constructor: -> + @emitter = new Emitter + @colorsByKeyword = new Map + + has: (keyword) -> + @colorsByKeyword.has(keyword) + + add: (keyword) -> + @colorsByKeyword.set(keyword, @getNextColor()) + @emitter.emit('did-change-keyword') + + delete: (keyword) -> + @colorsByKeyword.delete(keyword) + @emitter.emit('did-change-keyword') + + toggle: (keyword) -> + + if @has(keyword) + @delete(keyword) + else + @add(keyword) + + clear: -> + @colorsByKeyword.clear() + @colorIndex = null + @emitter.emit('did-change-keyword') + + getNextColor: -> + @colorIndex ?= -1 + @colorIndex = (@colorIndex + 1) % @colorNumbers.length + @colorNumbers[@colorIndex] + + destroy: -> + @clear() diff --git a/lib/main.coffee b/lib/main.coffee index 6072145..2e8bab7 100644 --- a/lib/main.coffee +++ b/lib/main.coffee @@ -1,25 +1,22 @@ {CompositeDisposable, Disposable, Emitter, Range} = require 'atom' _ = require 'underscore-plus' -StatusBarManager = require './status-bar-manager' settings = require './settings' QuickHighlightView = require './quick-highlight-view' +KeywordManager = require './keyword-manager' +StatusBarManager = require './status-bar-manager' { - getVisibleEditors - getVisibleBufferRange - getCountForKeyword getCursorWord } = require './utils' module.exports = config: settings.config - colorNumbers: ['01', '02', '03', '04', '05', '06', '07'] activate: (state) -> @subscriptions = new CompositeDisposable @emitter = new Emitter @viewByEditor = new Map - @colorsByKeyword = new Map() + @keywordManager = new KeywordManager @statusBarManager = new StatusBarManager toggle = @toggle.bind(this) @@ -27,115 +24,33 @@ module.exports = 'quick-highlight:toggle': -> toggle(@getModel()) 'quick-highlight:clear': => @clear() - debouncedhighlightSelection = null - highlightSelection = -> - settings.observe 'highlightSelectionDelay', (delay) -> - debouncedhighlightSelection = _.debounce(highlightSelection, delay) - - refreshEditor = @refreshEditor.bind(this) - @subscribe atom.workspace.observeTextEditors (editor) => - view = new QuickHighlightView(editor, this) - @viewByEditor.set(view, editor) - - editorSubs = new CompositeDisposable - editorSubs.add editor.onDidStopChanging -> - if editor is atom.workspace.getActiveTextEditor() - URI = editor.getURI() - isChangedEditor = (editor) -> editor.getURI() is URI - getVisibleEditors() - .filter(isChangedEditor) - .forEach(refreshEditor) - - editorElement = editor.element - refresh = @refreshEditor.bind(this, editor) - - # [FIXME] - # @refreshEditor depends on editorElement.getVisibleRowRange() but it return - # [undefined, undefined] when it called on editorElement which is not attached yet. - # So we separately need to cover this case from Atom v1.1.0 - editorSubs.add(editorElement.onDidAttach(refresh)) - - editorSubs.add editor.onDidChangeSelectionRange ({selection}) => - if selection.isLastSelection() and not @isLocked() - debouncedhighlightSelection(editor) - - editorSubs.add editor.onDidDestroy => - # @clearEditor(editor) - editorSubs.dispose() - @unsubscribe(editorSubs) - - @subscribe(editorSubs) + view = new QuickHighlightView(editor, {@keywordManager, @statusBarManager}) + @viewByEditor.set(editor, view) @subscribe atom.workspace.onDidChangeActivePaneItem (item) => - null - @statusBarManager.clear() - # if item?.getText? # Check if instance of TextEditor - # @refreshEditor(item) - # @highlightSelection(item) + @viewByEditor.forEach (view) -> view.refresh(item) subscribe: (args...) -> @subscriptions.add args... - unsubscribe: (arg) -> - @subscriptions.remove(arg) - deactivate: -> @clear() - @viewByEditor.forEach (view) -> - view.destroy() - + @viewByEditor.forEach (view) -> view.destroy() @subscriptions.dispose() {@subscriptions} = {} - locked: false - isLocked: -> - @locked - - withLock: (fn) -> - try - @locked = true - value = fn() - finally - @locked = false - value - - getNextColor: -> - @colorIndex ?= -1 - @colorIndex = (@colorIndex + 1) % @colorNumbers.length - @colorNumbers[@colorIndex] - toggle: (editor, keyword) -> - keyword ?= editor.getSelectedText() or @withLock(-> getCursorWord(editor)) - if @colorsByKeyword.has(keyword) - @colorsByKeyword.delete(keyword) - @statusBarManager.clear() - else - @colorsByKeyword.set(keyword, @getNextColor()) - if settings.get('displayCountOnStatusBar') - @statusBarManager.update(@getCountForKeyword(editor, keyword)) - @emitter.emit('did-change-keyword', {@colorsByKeyword}) - - onDidChangeKeyword: (fn) -> @emitter.on('did-change-keyword', fn) - - refreshEditor: (editor) -> - null + keyword ?= editor.getSelectedText() or getCursorWord(editor) + @keywordManager.toggle(keyword) clear: -> - @colorsByKeyword.clear() - @emitter.emit('did-change-keyword', {@colorsByKeyword}) - @colorIndex = null - @statusBarManager.clear() - - getCountForKeyword: (editor, keyword) -> - getCountForKeyword(editor, keyword) + @keywordManager.clear() consumeStatusBar: (statusBar) -> @statusBarManager.initialize(statusBar) @statusBarManager.attach() - @subscriptions.add new Disposable => - @statusBarManager.detach() - @statusBarManager = null + @subscriptions.add(new Disposable => @statusBarManager.detach()) consumeVim: ({Base}) -> toggle = @toggle.bind(this) diff --git a/lib/quick-highlight-view.coffee b/lib/quick-highlight-view.coffee index 454414f..5cf0217 100644 --- a/lib/quick-highlight-view.coffee +++ b/lib/quick-highlight-view.coffee @@ -9,17 +9,28 @@ settings = require './settings' module.exports = class QuickHighlightView - constructor: (@editor, @main) -> + + constructor: (@editor, {@keywordManager, @statusBarManager}) -> @editorElement = @editor.element @markerLayers = [] @disposables = new CompositeDisposable + + highlightSelection = null + @disposables.add settings.observe 'highlightSelectionDelay', (delay) => + highlightSelection = _.debounce(@highlightSelection.bind(this), delay) + @disposables.add( @editor.onDidDestroy(@destroy.bind(this)) - @editor.onDidChangeSelectionRange(@highlightSelection.bind(this)) - @main.onDidChangeKeyword(@refresh.bind(this)) + + # Don't pass function directly since we UPDATE highlightSelection on config change + @editor.onDidChangeSelectionRange(({selection}) -> highlightSelection(selection)) + + @keywordManager.onDidChangeKeyword(@refresh.bind(this, null)) + @editor.onDidStopChanging(@refresh.bind(this, null)) ) + needSelectionHighlight: (selection) -> editorElement = @editor.element scopes = settings.get('highlightSelectionExcludeScopes') @@ -34,7 +45,7 @@ module.exports = else true - highlightSelection: ({selection}) -> + highlightSelection: (selection) -> @markerLayerForSelectionHighlight?.destroy() return unless @needSelectionHighlight(selection) if keyword = selection.getText() @@ -56,20 +67,31 @@ module.exports = markerLayer.markBufferRange(range, markerOptions) markerLayer - refresh: ({colorsByKeyword}) -> + refresh: (activeEditor) -> + colorsByKeyword = @keywordManager.colorsByKeyword decorationStyle = settings.get('decorate') + @clear() + colorsByKeyword.forEach (color, keyword) => @markerLayers.push(@highlight(keyword, color)) + activeEditor ?= atom.workspace.getActiveTextEditor() + + if activeEditor? and activeEditor is @editor and @markerLayer + @statusBarManager.clear() + if settings.get('displayCountOnStatusBar') and (markerLayer = _.last(@markerLayers))? + @statusBarManager.update(markerLayer.getMarkerCount()) + isVisible: -> @editor in getVisibleEditors() clear: -> for markerLayer in @markerLayers markerLayer.destroy() + @markerLayer = [] destroy: -> @clear() @markerLayerForSelectionHighlight?.destroy() - @disposable.dispose() + @disposables.dispose() diff --git a/lib/utils.coffee b/lib/utils.coffee index 3a37224..de4deb9 100644 --- a/lib/utils.coffee +++ b/lib/utils.coffee @@ -1,26 +1,8 @@ -_ = require 'underscore-plus' -{Range} = require 'atom' - getVisibleEditors = -> atom.workspace.getPanes() .map (pane) -> pane.getActiveEditor() .filter (editor) -> editor? -getVisibleBufferRange = (editor) -> - editorElement = editor.element - unless visibleRowRange = editorElement.getVisibleRowRange() - # When editorElement.component is not yet available it return null - # Hope this guard fix issue https://github.com/t9md/atom-quick-highlight/issues/7 - return null - - [startRow, endRow] = visibleRowRange.map (row) -> - editor.bufferRowForScreenRow(row) - - # FIXME: editorElement.getVisibleRowRange() return [NaN, NaN] when - # it called to editorElement still not yet attached. - return null if (isNaN(startRow) or isNaN(endRow)) - new Range([startRow, 0], [endRow, Infinity]) - getCursorWord = (editor) -> selection = editor.getLastSelection() {cursor} = selection @@ -30,11 +12,6 @@ getCursorWord = (editor) -> cursor.setBufferPosition(cursorPosition) word -getCountForKeyword = (editor, keyword) -> - count = 0 - editor.scan(///#{_.escapeRegExp(keyword)}///g, -> count++) - count - matchScope = (editorElement, scope) -> containsCount = 0 classNames = scope.split('.') @@ -47,6 +24,5 @@ module.exports = { matchScope getVisibleEditors getVisibleBufferRange - getCountForKeyword getCursorWord } From f9d48df61fc69bfda19288d18d198cece6c6a38f Mon Sep 17 00:00:00 2001 From: t9md Date: Fri, 6 Jan 2017 18:09:20 +0900 Subject: [PATCH 06/18] no longer use subscribe wrapper function --- lib/main.coffee | 18 ++++++------------ lib/utils.coffee | 1 - 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/lib/main.coffee b/lib/main.coffee index 2e8bab7..5cac9b1 100644 --- a/lib/main.coffee +++ b/lib/main.coffee @@ -20,22 +20,19 @@ module.exports = @statusBarManager = new StatusBarManager toggle = @toggle.bind(this) - @subscribe atom.commands.add 'atom-text-editor:not([mini])', + @subscriptions.add atom.commands.add 'atom-text-editor:not([mini])', 'quick-highlight:toggle': -> toggle(@getModel()) - 'quick-highlight:clear': => @clear() + 'quick-highlight:clear': => @keywordManager.clear() - @subscribe atom.workspace.observeTextEditors (editor) => + @subscriptions.add atom.workspace.observeTextEditors (editor) => view = new QuickHighlightView(editor, {@keywordManager, @statusBarManager}) @viewByEditor.set(editor, view) - @subscribe atom.workspace.onDidChangeActivePaneItem (item) => + @subscriptions.add atom.workspace.onDidChangeActivePaneItem (item) => @viewByEditor.forEach (view) -> view.refresh(item) - subscribe: (args...) -> - @subscriptions.add args... - deactivate: -> - @clear() + @keywordManager.clear() @viewByEditor.forEach (view) -> view.destroy() @subscriptions.dispose() {@subscriptions} = {} @@ -44,9 +41,6 @@ module.exports = keyword ?= editor.getSelectedText() or getCursorWord(editor) @keywordManager.toggle(keyword) - clear: -> - @keywordManager.clear() - consumeStatusBar: (statusBar) -> @statusBarManager.initialize(statusBar) @statusBarManager.attach() @@ -62,4 +56,4 @@ module.exports = mutateSelection: (selection) -> toggle(@editor, selection.getText()) - @subscribe(QuickHighlight.registerCommand()) + @subscriptions.add(QuickHighlight.registerCommand()) diff --git a/lib/utils.coffee b/lib/utils.coffee index de4deb9..9e0c86b 100644 --- a/lib/utils.coffee +++ b/lib/utils.coffee @@ -23,6 +23,5 @@ matchScope = (editorElement, scope) -> module.exports = { matchScope getVisibleEditors - getVisibleBufferRange getCursorWord } From f135f61240a0019b1ec37f5013820fb52130ecf9 Mon Sep 17 00:00:00 2001 From: t9md Date: Fri, 6 Jan 2017 18:15:16 +0900 Subject: [PATCH 07/18] :gift: InnerWord precomposed version of vmp command --- lib/main.coffee | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/main.coffee b/lib/main.coffee index 5cac9b1..7b0164e 100644 --- a/lib/main.coffee +++ b/lib/main.coffee @@ -56,4 +56,10 @@ module.exports = mutateSelection: (selection) -> toggle(@editor, selection.getText()) - @subscriptions.add(QuickHighlight.registerCommand()) + class QuickHighlightWord extends QuickHighlight + target: "InnerWord" + + @subscriptions.add( + QuickHighlight.registerCommand() + QuickHighlightWord.registerCommand() + ) From a032c71db094259cbcbe02680dca5f804f034d80 Mon Sep 17 00:00:00 2001 From: t9md Date: Sat, 7 Jan 2017 10:29:34 +0900 Subject: [PATCH 08/18] default underline style, efficient rendering(skip useless re-hl) --- README.md | 22 -------- lib/keyword-manager.coffee | 21 +++++--- lib/main.coffee | 10 +++- lib/quick-highlight-view.coffee | 93 ++++++++++++++++++++++----------- lib/settings.coffee | 4 +- lib/utils.coffee | 10 ++++ styles/quick-highlight.less | 35 ++++++++++++- 7 files changed, 130 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index 3f163a8..d7927c2 100644 --- a/README.md +++ b/README.md @@ -54,28 +54,6 @@ atom-text-editor .quick-highlight.box-selection .region { background-color: transparent; border-color: @syntax-text-color; } - -// For manual-highlight(0 to 7) color -//=================================== -// Less mixin to make manual-highlight to underlined style -.quick-highlight-underline(@name) { - .quick-highlight.@{name} .region { - border-width: 0px; - border-radius: 0px; - border-bottom-width: 2px; - border-bottom-style: solid; - } -} - -atom-text-editor { - .quick-highlight-underline(box-01); - .quick-highlight-underline(box-02); - .quick-highlight-underline(box-03); - .quick-highlight-underline(box-04); - .quick-highlight-underline(box-05); - .quick-highlight-underline(box-06); - .quick-highlight-underline(box-07); -} ``` ## vim-mode-plus operator diff --git a/lib/keyword-manager.coffee b/lib/keyword-manager.coffee index d9bcb5b..9e8192b 100644 --- a/lib/keyword-manager.coffee +++ b/lib/keyword-manager.coffee @@ -1,11 +1,15 @@ -{CompositeDisposable, Emitter} = require 'atom' +{Emitter} = require 'atom' module.exports = class KeywordManager colorNumbers: ['01', '02', '03', '04', '05', '06', '07'] + visibleEditors: null + activeItem: null + latestKeyword: null - onDidChangeKeyword: (fn) -> - @emitter.on('did-change-keyword', fn) + onDidAddKeyword: (fn) -> @emitter.on('did-add-keyword', fn) + onDidDeleteKeyword: (fn) -> @emitter.on('did-delete-keyword', fn) + onDidClearKeyword: (fn) -> @emitter.on('did-clear-keyword', fn) constructor: -> @emitter = new Emitter @@ -15,15 +19,16 @@ module.exports = @colorsByKeyword.has(keyword) add: (keyword) -> - @colorsByKeyword.set(keyword, @getNextColor()) - @emitter.emit('did-change-keyword') + color = @getNextColor() + @colorsByKeyword.set(keyword, color) + @latestKeyword = keyword + @emitter.emit('did-add-keyword', {keyword, color}) delete: (keyword) -> @colorsByKeyword.delete(keyword) - @emitter.emit('did-change-keyword') + @emitter.emit('did-delete-keyword', {keyword}) toggle: (keyword) -> - if @has(keyword) @delete(keyword) else @@ -32,7 +37,7 @@ module.exports = clear: -> @colorsByKeyword.clear() @colorIndex = null - @emitter.emit('did-change-keyword') + @emitter.emit('did-clear-keyword') getNextColor: -> @colorIndex ?= -1 diff --git a/lib/main.coffee b/lib/main.coffee index 7b0164e..9430725 100644 --- a/lib/main.coffee +++ b/lib/main.coffee @@ -5,6 +5,12 @@ QuickHighlightView = require './quick-highlight-view' KeywordManager = require './keyword-manager' StatusBarManager = require './status-bar-manager' +# - Refresh onDidChangeActivePaneItem +# - But dont't refresh in-visible editor +# - Update statusbar count on activeEditor was changed +# - Clear marker for in-visible editor? +# - Update only keyword added/remove(No need to refresh whole keywords) + { getCursorWord } = require './utils' @@ -28,8 +34,8 @@ module.exports = view = new QuickHighlightView(editor, {@keywordManager, @statusBarManager}) @viewByEditor.set(editor, view) - @subscriptions.add atom.workspace.onDidChangeActivePaneItem (item) => - @viewByEditor.forEach (view) -> view.refresh(item) + @subscriptions.add atom.workspace.onDidChangeActivePaneItem => + @viewByEditor.forEach (view) -> view.refresh() deactivate: -> @keywordManager.clear() diff --git a/lib/quick-highlight-view.coffee b/lib/quick-highlight-view.coffee index 5cf0217..df64a9b 100644 --- a/lib/quick-highlight-view.coffee +++ b/lib/quick-highlight-view.coffee @@ -5,14 +5,16 @@ settings = require './settings' { getVisibleEditors matchScope + collectKeywordRanges } = require './utils' module.exports = class QuickHighlightView + rendered: false + manualDecorationStyle: null constructor: (@editor, {@keywordManager, @statusBarManager}) -> - @editorElement = @editor.element - @markerLayers = [] + @markerLayersByKeyword = new Map @disposables = new CompositeDisposable @@ -20,17 +22,22 @@ module.exports = @disposables.add settings.observe 'highlightSelectionDelay', (delay) => highlightSelection = _.debounce(@highlightSelection.bind(this), delay) + @disposables.add settings.observe 'decorate', (decorationStyle) => + @manualDecorationStyle = decorationStyle + @reset() + @disposables.add( @editor.onDidDestroy(@destroy.bind(this)) # Don't pass function directly since we UPDATE highlightSelection on config change @editor.onDidChangeSelectionRange(({selection}) -> highlightSelection(selection)) - @keywordManager.onDidChangeKeyword(@refresh.bind(this, null)) - @editor.onDidStopChanging(@refresh.bind(this, null)) + @keywordManager.onDidAddKeyword(@addHighlight.bind(this)) + @keywordManager.onDidDeleteKeyword(@deleteHighlight.bind(this)) + @keywordManager.onDidClearKeyword(@clear.bind(this)) + @editor.onDidStopChanging(@reset.bind(this)) ) - needSelectionHighlight: (selection) -> editorElement = @editor.element scopes = settings.get('highlightSelectionExcludeScopes') @@ -52,44 +59,70 @@ module.exports = @markerLayerForSelectionHighlight = @highlight(keyword, 'box-selection') highlight: (keyword, color) -> + ranges = collectKeywordRanges(@editor, keyword) + return null if ranges.length is 0 + markerLayer = @editor.addMarkerLayer() - decorationStyle = settings.get('decorate') - if color is 'box-selection' - colorName = color - else - colorName = "#{decorationStyle}-#{color}" - decorationOptions = {type: 'highlight', class: "quick-highlight #{colorName}"} + decorationOptions = {type: 'highlight', class: "quick-highlight #{color}"} @editor.decorateMarkerLayer(markerLayer, decorationOptions) - markerOptions = {invalidate: 'inside'} - pattern = ///#{_.escapeRegExp(keyword)}///g - @editor.scan pattern, ({range, matchText}) -> + for range in ranges markerLayer.markBufferRange(range, markerOptions) markerLayer - refresh: (activeEditor) -> - colorsByKeyword = @keywordManager.colorsByKeyword - decorationStyle = settings.get('decorate') + addHighlight: ({keyword, color}) -> + if not @markerLayersByKeyword.has(keyword) + if markerLayer = @highlight(keyword, "#{settings.get('decorate')}-#{color}") + @markerLayersByKeyword.set(keyword, markerLayer) + @updateStatusBarIfNecesssary() - @clear() + deleteHighlight: ({keyword}) -> + if @markerLayersByKeyword.has(keyword) + @markerLayersByKeyword.get(keyword).destroy() + @markerLayersByKeyword.delete(keyword) + @updateStatusBarIfNecesssary() + + clear: -> + @markerLayersByKeyword.forEach (markerLayer) -> + markerLayer.destroy() + @markerLayersByKeyword.clear() + render: -> + {colorsByKeyword} = @keywordManager colorsByKeyword.forEach (color, keyword) => - @markerLayers.push(@highlight(keyword, color)) + @addHighlight({keyword, color}) - activeEditor ?= atom.workspace.getActiveTextEditor() + reset: -> + @clear() + @render() + @updateStatusBarIfNecesssary() - if activeEditor? and activeEditor is @editor and @markerLayer - @statusBarManager.clear() - if settings.get('displayCountOnStatusBar') and (markerLayer = _.last(@markerLayers))? - @statusBarManager.update(markerLayer.getMarkerCount()) + refresh: -> + isVisible = @editor in getVisibleEditors() + if isVisible is @wasVisible + @updateStatusBarIfNecesssary() + return - isVisible: -> - @editor in getVisibleEditors() + if isVisible + @render() + else + @clear() - clear: -> - for markerLayer in @markerLayers - markerLayer.destroy() - @markerLayer = [] + @wasVisible = isVisible + @updateStatusBarIfNecesssary() + + getMarkerCountForKeyword: (keyword) -> + if @markerLayersByKeyword.has(keyword) + @markerLayersByKeyword.get(keyword).getMarkerCount() + else + 0 + + updateStatusBarIfNecesssary: -> + if settings.get('displayCountOnStatusBar') and @editor is atom.workspace.getActiveTextEditor() + @statusBarManager.clear() + count = @getMarkerCountForKeyword(@keywordManager.latestKeyword) + if count > 0 + @statusBarManager.update(count) destroy: -> @clear() diff --git a/lib/settings.coffee b/lib/settings.coffee index babb701..1d33282 100644 --- a/lib/settings.coffee +++ b/lib/settings.coffee @@ -31,8 +31,8 @@ class Settings module.exports = new Settings 'quick-highlight', decorate: - default: 'box' - enum: ['box', 'highlight'] + default: 'underline' + enum: ['underline', 'box', 'highlight'] description: "Decoation style for highlight" highlightSelection: default: true diff --git a/lib/utils.coffee b/lib/utils.coffee index 9e0c86b..f1d422f 100644 --- a/lib/utils.coffee +++ b/lib/utils.coffee @@ -1,8 +1,17 @@ +_ = require 'underscore-plus' + getVisibleEditors = -> atom.workspace.getPanes() .map (pane) -> pane.getActiveEditor() .filter (editor) -> editor? +collectKeywordRanges = (editor, keyword) -> + pattern = ///#{_.escapeRegExp(keyword)}///g + ranges = [] + editor.scan pattern, ({range}) -> + ranges.push(range) + ranges + getCursorWord = (editor) -> selection = editor.getLastSelection() {cursor} = selection @@ -21,6 +30,7 @@ matchScope = (editorElement, scope) -> containsCount is classNames.length module.exports = { + collectKeywordRanges matchScope getVisibleEditors getCursorWord diff --git a/styles/quick-highlight.less b/styles/quick-highlight.less index c4a3811..c667330 100644 --- a/styles/quick-highlight.less +++ b/styles/quick-highlight.less @@ -15,6 +15,13 @@ border-style: solid; } +.underline-box () { + box-sizing: border-box; + border-width: 2px; + border-bottom-width: 2px; + border-style: solid; +} + .box(@name, @color) { .quick-highlight.@{name} .region { .round-box; @@ -22,6 +29,7 @@ z-index: 1; } } + .box(@name) { .quick-highlight.@{name} .region { .round-box; @@ -37,6 +45,17 @@ } } +.underline(@name, @color) { + .quick-highlight.@{name} .region { + .color(border-color, @color); + box-sizing: border-box; + border-width: 0px; + border-bottom-width: 2px; + border-style: solid; + z-index: 1; + } +} + @color-base: hsl(0, 100%, 50%); @color01: spin(@color-base, 0); @@ -47,7 +66,20 @@ @color06: spin(@color-base, 225); @color07: spin(@color-base, 45); +.status-bar-quick-highlight { + background-color: red; + color: green; +} + atom-text-editor { + .underline(underline-01, @color01); + .underline(underline-02, @color02); + .underline(underline-03, @color03); + .underline(underline-04, @color04); + .underline(underline-05, @color05); + .underline(underline-06, @color06); + .underline(underline-07, @color07); + .box(box-01, @color01); .box(box-02, @color02); .box(box-03, @color03); @@ -55,7 +87,6 @@ atom-text-editor { .box(box-05, @color05); .box(box-06, @color06); .box(box-07, @color07); - .box(box-selection); .highlight(highlight-01, @color01); .highlight(highlight-02, @color02); @@ -64,4 +95,6 @@ atom-text-editor { .highlight(highlight-05, @color05); .highlight(highlight-06, @color06); .highlight(highlight-07, @color07); + + .box(box-selection); } From 06055bb55074b599311341feed4cb940f402c620 Mon Sep 17 00:00:00 2001 From: t9md Date: Sat, 7 Jan 2017 17:13:14 +0900 Subject: [PATCH 09/18] make it more REACTIVE, sync each view by diffing with masterKeywords --- lib/keyword-manager.coffee | 37 +++++++------------- lib/quick-highlight-view.coffee | 62 +++++++++++++-------------------- 2 files changed, 38 insertions(+), 61 deletions(-) diff --git a/lib/keyword-manager.coffee b/lib/keyword-manager.coffee index 9e8192b..f48d6d9 100644 --- a/lib/keyword-manager.coffee +++ b/lib/keyword-manager.coffee @@ -7,42 +7,31 @@ module.exports = activeItem: null latestKeyword: null - onDidAddKeyword: (fn) -> @emitter.on('did-add-keyword', fn) - onDidDeleteKeyword: (fn) -> @emitter.on('did-delete-keyword', fn) - onDidClearKeyword: (fn) -> @emitter.on('did-clear-keyword', fn) + onDidChangeKeyword: (fn) -> @emitter.on('did-change-keyword', fn) + emitDidChangeKeyword: -> @emitter.emit('did-change-keyword') constructor: -> @emitter = new Emitter - @colorsByKeyword = new Map + @reset() - has: (keyword) -> - @colorsByKeyword.has(keyword) - - add: (keyword) -> - color = @getNextColor() - @colorsByKeyword.set(keyword, color) - @latestKeyword = keyword - @emitter.emit('did-add-keyword', {keyword, color}) - - delete: (keyword) -> - @colorsByKeyword.delete(keyword) - @emitter.emit('did-delete-keyword', {keyword}) + reset: -> + @keywordToColor = Object.create(null) + @colorIndex = -1 toggle: (keyword) -> - if @has(keyword) - @delete(keyword) + if keyword of @keywordToColor + delete @keywordToColor[keyword] else - @add(keyword) + @keywordToColor[keyword] = @getNextColor() + @latestKeyword = keyword + @emitDidChangeKeyword() clear: -> - @colorsByKeyword.clear() - @colorIndex = null - @emitter.emit('did-clear-keyword') + @reset() + @emitDidChangeKeyword() getNextColor: -> - @colorIndex ?= -1 @colorIndex = (@colorIndex + 1) % @colorNumbers.length @colorNumbers[@colorIndex] destroy: -> - @clear() diff --git a/lib/quick-highlight-view.coffee b/lib/quick-highlight-view.coffee index df64a9b..c627656 100644 --- a/lib/quick-highlight-view.coffee +++ b/lib/quick-highlight-view.coffee @@ -10,31 +10,25 @@ settings = require './settings' module.exports = class QuickHighlightView - rendered: false - manualDecorationStyle: null constructor: (@editor, {@keywordManager, @statusBarManager}) -> - @markerLayersByKeyword = new Map + @keywordToMarkerLayer = Object.create(null) @disposables = new CompositeDisposable highlightSelection = null - @disposables.add settings.observe 'highlightSelectionDelay', (delay) => + updateHighlightSelection = (delay) => highlightSelection = _.debounce(@highlightSelection.bind(this), delay) - @disposables.add settings.observe 'decorate', (decorationStyle) => - @manualDecorationStyle = decorationStyle - @reset() - @disposables.add( + settings.observe('highlightSelectionDelay', updateHighlightSelection) + settings.observe('decorate', @reset.bind(this)) @editor.onDidDestroy(@destroy.bind(this)) # Don't pass function directly since we UPDATE highlightSelection on config change @editor.onDidChangeSelectionRange(({selection}) -> highlightSelection(selection)) - @keywordManager.onDidAddKeyword(@addHighlight.bind(this)) - @keywordManager.onDidDeleteKeyword(@deleteHighlight.bind(this)) - @keywordManager.onDidClearKeyword(@clear.bind(this)) + @keywordManager.onDidChangeKeyword(@refresh.bind(this)) @editor.onDidStopChanging(@reset.bind(this)) ) @@ -71,56 +65,50 @@ module.exports = markerLayer addHighlight: ({keyword, color}) -> - if not @markerLayersByKeyword.has(keyword) + if keyword not of @keywordToMarkerLayer if markerLayer = @highlight(keyword, "#{settings.get('decorate')}-#{color}") - @markerLayersByKeyword.set(keyword, markerLayer) - @updateStatusBarIfNecesssary() + @keywordToMarkerLayer[keyword] = markerLayer deleteHighlight: ({keyword}) -> - if @markerLayersByKeyword.has(keyword) - @markerLayersByKeyword.get(keyword).destroy() - @markerLayersByKeyword.delete(keyword) - @updateStatusBarIfNecesssary() + if keyword of @keywordToMarkerLayer + @keywordToMarkerLayer[keyword].destroy() + delete @keywordToMarkerLayer[keyword] clear: -> - @markerLayersByKeyword.forEach (markerLayer) -> + for keyword, markerLayer of @keywordToMarkerLayer markerLayer.destroy() - @markerLayersByKeyword.clear() + @keywordToMarkerLayer = Object.create(null) render: -> - {colorsByKeyword} = @keywordManager - colorsByKeyword.forEach (color, keyword) => + {keywordToColor} = @keywordManager + masterKeywords = _.keys(keywordToColor) + currentKeywords = _.keys(@keywordToMarkerLayer) + keywordsToAdd = _.without(masterKeywords, currentKeywords...) + keywordsToDelete = _.without(currentKeywords, masterKeywords...) + + for keyword in keywordsToDelete + @deleteHighlight({keyword}) + + for keyword in keywordsToAdd when color = keywordToColor[keyword] @addHighlight({keyword, color}) reset: -> @clear() - @render() - @updateStatusBarIfNecesssary() + @refresh() refresh: -> isVisible = @editor in getVisibleEditors() - if isVisible is @wasVisible - @updateStatusBarIfNecesssary() - return - if isVisible @render() else @clear() - - @wasVisible = isVisible @updateStatusBarIfNecesssary() - getMarkerCountForKeyword: (keyword) -> - if @markerLayersByKeyword.has(keyword) - @markerLayersByKeyword.get(keyword).getMarkerCount() - else - 0 - updateStatusBarIfNecesssary: -> if settings.get('displayCountOnStatusBar') and @editor is atom.workspace.getActiveTextEditor() @statusBarManager.clear() - count = @getMarkerCountForKeyword(@keywordManager.latestKeyword) + keyword = @keywordManager.latestKeyword + count = @keywordToMarkerLayer[keyword]?.getMarkerCount() ? 0 if count > 0 @statusBarManager.update(count) From c0d0cf237c63ef55fe071513ab7ea8d26e4f56b9 Mon Sep 17 00:00:00 2001 From: t9md Date: Sat, 7 Jan 2017 17:18:58 +0900 Subject: [PATCH 10/18] cleanup --- lib/keyword-manager.coffee | 2 -- lib/main.coffee | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/keyword-manager.coffee b/lib/keyword-manager.coffee index f48d6d9..a0f32a9 100644 --- a/lib/keyword-manager.coffee +++ b/lib/keyword-manager.coffee @@ -3,8 +3,6 @@ module.exports = class KeywordManager colorNumbers: ['01', '02', '03', '04', '05', '06', '07'] - visibleEditors: null - activeItem: null latestKeyword: null onDidChangeKeyword: (fn) -> @emitter.on('did-change-keyword', fn) diff --git a/lib/main.coffee b/lib/main.coffee index 9430725..fd16881 100644 --- a/lib/main.coffee +++ b/lib/main.coffee @@ -38,7 +38,7 @@ module.exports = @viewByEditor.forEach (view) -> view.refresh() deactivate: -> - @keywordManager.clear() + @keywordManager.destroy() @viewByEditor.forEach (view) -> view.destroy() @subscriptions.dispose() {@subscriptions} = {} From a6d62c0f514f4f545923ff54828fba697fffd8ea Mon Sep 17 00:00:00 2001 From: t9md Date: Sat, 7 Jan 2017 17:31:41 +0900 Subject: [PATCH 11/18] simplify further --- lib/quick-highlight-view.coffee | 25 ++++++++++++------------- lib/settings.coffee | 5 ++++- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/lib/quick-highlight-view.coffee b/lib/quick-highlight-view.coffee index c627656..c09d5a0 100644 --- a/lib/quick-highlight-view.coffee +++ b/lib/quick-highlight-view.coffee @@ -13,6 +13,7 @@ module.exports = constructor: (@editor, {@keywordManager, @statusBarManager}) -> @keywordToMarkerLayer = Object.create(null) + @decorationStyle = settings.get('decorate') @disposables = new CompositeDisposable @@ -22,7 +23,7 @@ module.exports = @disposables.add( settings.observe('highlightSelectionDelay', updateHighlightSelection) - settings.observe('decorate', @reset.bind(this)) + settings.onDidChange('decorate', @onChangeDecorationStyle.bind(this)) @editor.onDidDestroy(@destroy.bind(this)) # Don't pass function directly since we UPDATE highlightSelection on config change @@ -32,6 +33,10 @@ module.exports = @editor.onDidStopChanging(@reset.bind(this)) ) + onChangeDecorationStyle: ({newValue}) -> + @decorationStyle = newValue + @reset() + needSelectionHighlight: (selection) -> editorElement = @editor.element scopes = settings.get('highlightSelectionExcludeScopes') @@ -64,16 +69,6 @@ module.exports = markerLayer.markBufferRange(range, markerOptions) markerLayer - addHighlight: ({keyword, color}) -> - if keyword not of @keywordToMarkerLayer - if markerLayer = @highlight(keyword, "#{settings.get('decorate')}-#{color}") - @keywordToMarkerLayer[keyword] = markerLayer - - deleteHighlight: ({keyword}) -> - if keyword of @keywordToMarkerLayer - @keywordToMarkerLayer[keyword].destroy() - delete @keywordToMarkerLayer[keyword] - clear: -> for keyword, markerLayer of @keywordToMarkerLayer markerLayer.destroy() @@ -86,11 +81,15 @@ module.exports = keywordsToAdd = _.without(masterKeywords, currentKeywords...) keywordsToDelete = _.without(currentKeywords, masterKeywords...) + # Delete for keyword in keywordsToDelete - @deleteHighlight({keyword}) + @keywordToMarkerLayer[keyword].destroy() + delete @keywordToMarkerLayer[keyword] + # Add for keyword in keywordsToAdd when color = keywordToColor[keyword] - @addHighlight({keyword, color}) + if markerLayer = @highlight(keyword, "#{@decorationStyle}-#{color}") + @keywordToMarkerLayer[keyword] = markerLayer reset: -> @clear() diff --git a/lib/settings.coffee b/lib/settings.coffee index 1d33282..d74bc29 100644 --- a/lib/settings.coffee +++ b/lib/settings.coffee @@ -27,7 +27,10 @@ class Settings @set(param, not @get(param)) observe: (param, fn) -> - atom.config.observe "#{@scope}.#{param}", fn + atom.config.observe("#{@scope}.#{param}", fn) + + onDidChange: (param, fn) -> + atom.config.onDidChange("#{@scope}.#{param}", fn) module.exports = new Settings 'quick-highlight', decorate: From e2c85ea818ea63daa900c3970cb850f53a624f19 Mon Sep 17 00:00:00 2001 From: t9md Date: Sat, 7 Jan 2017 17:38:41 +0900 Subject: [PATCH 12/18] create markerLayer regardless of keyword existence for simplicity --- lib/quick-highlight-view.coffee | 11 ++++------- lib/utils.coffee | 8 -------- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/lib/quick-highlight-view.coffee b/lib/quick-highlight-view.coffee index c09d5a0..63eb381 100644 --- a/lib/quick-highlight-view.coffee +++ b/lib/quick-highlight-view.coffee @@ -5,7 +5,6 @@ settings = require './settings' { getVisibleEditors matchScope - collectKeywordRanges } = require './utils' module.exports = @@ -58,14 +57,13 @@ module.exports = @markerLayerForSelectionHighlight = @highlight(keyword, 'box-selection') highlight: (keyword, color) -> - ranges = collectKeywordRanges(@editor, keyword) - return null if ranges.length is 0 - markerLayer = @editor.addMarkerLayer() decorationOptions = {type: 'highlight', class: "quick-highlight #{color}"} @editor.decorateMarkerLayer(markerLayer, decorationOptions) markerOptions = {invalidate: 'inside'} - for range in ranges + + pattern = ///#{_.escapeRegExp(keyword)}///g + @editor.scan pattern, ({range}) -> markerLayer.markBufferRange(range, markerOptions) markerLayer @@ -88,8 +86,7 @@ module.exports = # Add for keyword in keywordsToAdd when color = keywordToColor[keyword] - if markerLayer = @highlight(keyword, "#{@decorationStyle}-#{color}") - @keywordToMarkerLayer[keyword] = markerLayer + @keywordToMarkerLayer[keyword] = @highlight(keyword, "#{@decorationStyle}-#{color}") reset: -> @clear() diff --git a/lib/utils.coffee b/lib/utils.coffee index f1d422f..01d8eae 100644 --- a/lib/utils.coffee +++ b/lib/utils.coffee @@ -5,13 +5,6 @@ getVisibleEditors = -> .map (pane) -> pane.getActiveEditor() .filter (editor) -> editor? -collectKeywordRanges = (editor, keyword) -> - pattern = ///#{_.escapeRegExp(keyword)}///g - ranges = [] - editor.scan pattern, ({range}) -> - ranges.push(range) - ranges - getCursorWord = (editor) -> selection = editor.getLastSelection() {cursor} = selection @@ -30,7 +23,6 @@ matchScope = (editorElement, scope) -> containsCount is classNames.length module.exports = { - collectKeywordRanges matchScope getVisibleEditors getCursorWord From 13737b8fa7e22e980b002f7e5beed89ce7e8dcaf Mon Sep 17 00:00:00 2001 From: t9md Date: Sat, 7 Jan 2017 17:51:05 +0900 Subject: [PATCH 13/18] DONE: fix space only exclusion for selection HL --- lib/quick-highlight-view.coffee | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/lib/quick-highlight-view.coffee b/lib/quick-highlight-view.coffee index 63eb381..55319e8 100644 --- a/lib/quick-highlight-view.coffee +++ b/lib/quick-highlight-view.coffee @@ -38,14 +38,14 @@ module.exports = needSelectionHighlight: (selection) -> editorElement = @editor.element - scopes = settings.get('highlightSelectionExcludeScopes') + excludeScopes = settings.get('highlightSelectionExcludeScopes') switch when (not settings.get('highlightSelection')) , selection.isEmpty() - , (scopes.some (scope) -> matchScope(editorElement, scope)) + , (excludeScopes.some (scope) -> matchScope(editorElement, scope)) , not selection.getBufferRange().isSingleLine() , selection.getText().length < settings.get('highlightSelectionMinimumLength') - , /[^\S]/.test(selection.getText()) + , (not /\S/.test(selection.getText())) false else true @@ -58,13 +58,9 @@ module.exports = highlight: (keyword, color) -> markerLayer = @editor.addMarkerLayer() - decorationOptions = {type: 'highlight', class: "quick-highlight #{color}"} - @editor.decorateMarkerLayer(markerLayer, decorationOptions) - markerOptions = {invalidate: 'inside'} - - pattern = ///#{_.escapeRegExp(keyword)}///g - @editor.scan pattern, ({range}) -> - markerLayer.markBufferRange(range, markerOptions) + @editor.decorateMarkerLayer(markerLayer, type: 'highlight', class: "quick-highlight #{color}") + @editor.scan ///#{_.escapeRegExp(keyword)}///g, ({range}) -> + markerLayer.markBufferRange(range, invalidate: 'inside') markerLayer clear: -> From 53055079f4f1459425d4c68bf1b8fa9437743be3 Mon Sep 17 00:00:00 2001 From: t9md Date: Sat, 7 Jan 2017 23:08:25 +0900 Subject: [PATCH 14/18] No longer explicitly clear inVisible editor's decoration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead, just update based on DIFF on next time become Visible. Also quick-highlight:clear affects all editor(clear all editor’s decoration) --- lib/keyword-manager.coffee | 5 ++++- lib/quick-highlight-view.coffee | 29 +++++++++++++++++------------ 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/lib/keyword-manager.coffee b/lib/keyword-manager.coffee index a0f32a9..4da96dc 100644 --- a/lib/keyword-manager.coffee +++ b/lib/keyword-manager.coffee @@ -8,6 +8,9 @@ module.exports = onDidChangeKeyword: (fn) -> @emitter.on('did-change-keyword', fn) emitDidChangeKeyword: -> @emitter.emit('did-change-keyword') + onDidClearKeyword: (fn) -> @emitter.on('did-clear-keyword', fn) + emitDidClearKeyword: -> @emitter.emit('did-clear-keyword') + constructor: -> @emitter = new Emitter @reset() @@ -26,7 +29,7 @@ module.exports = clear: -> @reset() - @emitDidChangeKeyword() + @emitDidClearKeyword() getNextColor: -> @colorIndex = (@colorIndex + 1) % @colorNumbers.length diff --git a/lib/quick-highlight-view.coffee b/lib/quick-highlight-view.coffee index 55319e8..0c57677 100644 --- a/lib/quick-highlight-view.coffee +++ b/lib/quick-highlight-view.coffee @@ -29,6 +29,7 @@ module.exports = @editor.onDidChangeSelectionRange(({selection}) -> highlightSelection(selection)) @keywordManager.onDidChangeKeyword(@refresh.bind(this)) + @keywordManager.onDidClearKeyword(@clear.bind(this)) @editor.onDidStopChanging(@reset.bind(this)) ) @@ -68,20 +69,25 @@ module.exports = markerLayer.destroy() @keywordToMarkerLayer = Object.create(null) - render: -> - {keywordToColor} = @keywordManager - masterKeywords = _.keys(keywordToColor) + getDiff: -> + masterKeywords = _.keys(@keywordManager.keywordToColor) currentKeywords = _.keys(@keywordToMarkerLayer) - keywordsToAdd = _.without(masterKeywords, currentKeywords...) - keywordsToDelete = _.without(currentKeywords, masterKeywords...) + newKeywords = _.without(masterKeywords, currentKeywords...) + oldKeywords = _.without(currentKeywords, masterKeywords...) + if newKeywords.length or oldKeywords.length + {newKeywords, oldKeywords} + else + null + render: ({newKeywords, oldKeywords}) -> # Delete - for keyword in keywordsToDelete + for keyword in oldKeywords @keywordToMarkerLayer[keyword].destroy() delete @keywordToMarkerLayer[keyword] # Add - for keyword in keywordsToAdd when color = keywordToColor[keyword] + {keywordToColor} = @keywordManager + for keyword in newKeywords when color = keywordToColor[keyword] @keywordToMarkerLayer[keyword] = @highlight(keyword, "#{@decorationStyle}-#{color}") reset: -> @@ -89,11 +95,10 @@ module.exports = @refresh() refresh: -> - isVisible = @editor in getVisibleEditors() - if isVisible - @render() - else - @clear() + return unless @editor in getVisibleEditors() + + if diff = @getDiff() + @render(diff) @updateStatusBarIfNecesssary() updateStatusBarIfNecesssary: -> From fa9a74f74989d233de1c0fb65a135b4ef29962fa Mon Sep 17 00:00:00 2001 From: t9md Date: Sun, 8 Jan 2017 02:51:46 +0900 Subject: [PATCH 15/18] Use fat allow to avoid messy X.bind(this) --- lib/main.coffee | 9 +++----- lib/quick-highlight-view.coffee | 41 +++++++++++++++++++-------------- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/lib/main.coffee b/lib/main.coffee index fd16881..d3019cd 100644 --- a/lib/main.coffee +++ b/lib/main.coffee @@ -6,9 +6,9 @@ KeywordManager = require './keyword-manager' StatusBarManager = require './status-bar-manager' # - Refresh onDidChangeActivePaneItem -# - But dont't refresh in-visible editor +# - But dont't refresh invisible editor # - Update statusbar count on activeEditor was changed -# - Clear marker for in-visible editor? +# - Clear marker for invisible editor? # - Update only keyword added/remove(No need to refresh whole keywords) { @@ -34,14 +34,11 @@ module.exports = view = new QuickHighlightView(editor, {@keywordManager, @statusBarManager}) @viewByEditor.set(editor, view) - @subscriptions.add atom.workspace.onDidChangeActivePaneItem => - @viewByEditor.forEach (view) -> view.refresh() - deactivate: -> @keywordManager.destroy() @viewByEditor.forEach (view) -> view.destroy() @subscriptions.dispose() - {@subscriptions} = {} + @subscriptions = null toggle: (editor, keyword) -> keyword ?= editor.getSelectedText() or getCursorWord(editor) diff --git a/lib/quick-highlight-view.coffee b/lib/quick-highlight-view.coffee index 0c57677..4ffdf4d 100644 --- a/lib/quick-highlight-view.coffee +++ b/lib/quick-highlight-view.coffee @@ -7,33 +7,40 @@ settings = require './settings' matchScope } = require './utils' +# - Refresh onDidChangeActivePaneItem +# - But dont't refresh invisible editor +# - Update statusbar count on activeEditor was changed +# - Clear marker for invisible editor?: Decide to skip to avoid clere/re-render. +# - Update only keyword added/remove: Achived by diffing. + module.exports = class QuickHighlightView + decorationStyle: null constructor: (@editor, {@keywordManager, @statusBarManager}) -> @keywordToMarkerLayer = Object.create(null) - @decorationStyle = settings.get('decorate') @disposables = new CompositeDisposable highlightSelection = null updateHighlightSelection = (delay) => - highlightSelection = _.debounce(@highlightSelection.bind(this), delay) + highlightSelection = _.debounce(@highlightSelection, delay) @disposables.add( settings.observe('highlightSelectionDelay', updateHighlightSelection) - settings.onDidChange('decorate', @onChangeDecorationStyle.bind(this)) - @editor.onDidDestroy(@destroy.bind(this)) + settings.observe('decorate', @observeDecorationStyle) + @editor.onDidDestroy(@destroy) # Don't pass function directly since we UPDATE highlightSelection on config change @editor.onDidChangeSelectionRange(({selection}) -> highlightSelection(selection)) - @keywordManager.onDidChangeKeyword(@refresh.bind(this)) - @keywordManager.onDidClearKeyword(@clear.bind(this)) - @editor.onDidStopChanging(@reset.bind(this)) + atom.workspace.onDidChangeActivePaneItem(@refresh) + @keywordManager.onDidChangeKeyword(@refresh) + @keywordManager.onDidClearKeyword(@clear) + @editor.onDidStopChanging(@reset) ) - onChangeDecorationStyle: ({newValue}) -> + observeDecorationStyle: (newValue) => @decorationStyle = newValue @reset() @@ -51,7 +58,7 @@ module.exports = else true - highlightSelection: (selection) -> + highlightSelection: (selection) => @markerLayerForSelectionHighlight?.destroy() return unless @needSelectionHighlight(selection) if keyword = selection.getText() @@ -64,11 +71,6 @@ module.exports = markerLayer.markBufferRange(range, invalidate: 'inside') markerLayer - clear: -> - for keyword, markerLayer of @keywordToMarkerLayer - markerLayer.destroy() - @keywordToMarkerLayer = Object.create(null) - getDiff: -> masterKeywords = _.keys(@keywordManager.keywordToColor) currentKeywords = _.keys(@keywordToMarkerLayer) @@ -90,11 +92,16 @@ module.exports = for keyword in newKeywords when color = keywordToColor[keyword] @keywordToMarkerLayer[keyword] = @highlight(keyword, "#{@decorationStyle}-#{color}") - reset: -> + clear: => + for keyword, markerLayer of @keywordToMarkerLayer + markerLayer.destroy() + @keywordToMarkerLayer = Object.create(null) + + reset: => @clear() @refresh() - refresh: -> + refresh: => return unless @editor in getVisibleEditors() if diff = @getDiff() @@ -109,7 +116,7 @@ module.exports = if count > 0 @statusBarManager.update(count) - destroy: -> + destroy: => @clear() @markerLayerForSelectionHighlight?.destroy() @disposables.dispose() From f10a7afddb1eaa2974371ea6024766248646f8b8 Mon Sep 17 00:00:00 2001 From: t9md Date: Sun, 8 Jan 2017 03:25:53 +0900 Subject: [PATCH 16/18] cleanup --- lib/main.coffee | 6 ------ lib/quick-highlight-view.coffee | 16 +++++++++------- lib/settings.coffee | 1 + 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/lib/main.coffee b/lib/main.coffee index d3019cd..f407533 100644 --- a/lib/main.coffee +++ b/lib/main.coffee @@ -5,12 +5,6 @@ QuickHighlightView = require './quick-highlight-view' KeywordManager = require './keyword-manager' StatusBarManager = require './status-bar-manager' -# - Refresh onDidChangeActivePaneItem -# - But dont't refresh invisible editor -# - Update statusbar count on activeEditor was changed -# - Clear marker for invisible editor? -# - Update only keyword added/remove(No need to refresh whole keywords) - { getCursorWord } = require './utils' diff --git a/lib/quick-highlight-view.coffee b/lib/quick-highlight-view.coffee index 4ffdf4d..0d179bb 100644 --- a/lib/quick-highlight-view.coffee +++ b/lib/quick-highlight-view.coffee @@ -13,6 +13,9 @@ settings = require './settings' # - Clear marker for invisible editor?: Decide to skip to avoid clere/re-render. # - Update only keyword added/remove: Achived by diffing. +# https://github.com/atom/text-buffer/pull/192 +# Use markerLayer.clear() in future + module.exports = class QuickHighlightView decorationStyle: null @@ -44,24 +47,23 @@ module.exports = @decorationStyle = newValue @reset() - needSelectionHighlight: (selection) -> + needSelectionHighlight: (text) -> editorElement = @editor.element excludeScopes = settings.get('highlightSelectionExcludeScopes') switch when (not settings.get('highlightSelection')) - , selection.isEmpty() , (excludeScopes.some (scope) -> matchScope(editorElement, scope)) - , not selection.getBufferRange().isSingleLine() - , selection.getText().length < settings.get('highlightSelectionMinimumLength') - , (not /\S/.test(selection.getText())) + , /\n/.test(text) + , text.length < settings.get('highlightSelectionMinimumLength') + , (not /\S/.test(text)) false else true highlightSelection: (selection) => @markerLayerForSelectionHighlight?.destroy() - return unless @needSelectionHighlight(selection) - if keyword = selection.getText() + keyword = selection.getText() + if @needSelectionHighlight(keyword) @markerLayerForSelectionHighlight = @highlight(keyword, 'box-selection') highlight: (keyword, color) -> diff --git a/lib/settings.coffee b/lib/settings.coffee index d74bc29..befe25b 100644 --- a/lib/settings.coffee +++ b/lib/settings.coffee @@ -41,6 +41,7 @@ module.exports = new Settings 'quick-highlight', default: true highlightSelectionMinimumLength: default: 2 + minimum: 1 description: "Minimum length of selection to be highlight" highlightSelectionExcludeScopes: default: ['vim-mode-plus.visual-mode.blockwise'] From 1acffd7186e7485c986be64cfdc6f791a183c66b Mon Sep 17 00:00:00 2001 From: t9md Date: Sun, 8 Jan 2017 05:58:26 +0900 Subject: [PATCH 17/18] :white_check_mark: spec update --- README.md | 14 +- spec/fixtures/sample-3 | 6 +- spec/quick-highlight-spec.coffee | 330 ++++++++++--------------------- 3 files changed, 114 insertions(+), 236 deletions(-) diff --git a/README.md b/README.md index d7927c2..dc95c3a 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,10 @@ - `quick-highlight:toggle` toggle highlight for selected or cursor word. - `quick-highlight:clear` clear all highlight. -- `vim-mode-plus-user:quick-highlight` Operator for [vim-mode-plus](https://atom.io/packages/vim-mode-plus). + +And following two operator for [vim-mode-plus](https://atom.io/packages/vim-mode-plus) user. +- `vim-mode-plus-user:quick-highlight`: Operator to highlight by text-object or motion. +- `vim-mode-plus-user:quick-highlight-word` Highlight cursor word, similar to `quick-highlight:toggle`, but well fit for vim's block cursor orientation. And `.` repeatable. # Keymap @@ -24,17 +27,10 @@ e.g. 'cmd-k M': 'quick-highlight:clear' ``` -* vim-mode user -```coffeescript -'atom-text-editor.vim-mode.normal-mode, atom-text-editor.vim-mode.visual-mode': - 'space m': 'quick-highlight:toggle' - 'space M': 'quick-highlight:clear' -``` - * vim-mode-plus user ```coffeescript 'atom-text-editor.vim-mode-plus.normal-mode, atom-text-editor.vim-mode-plus.visual-mode': - 'space m': 'quick-highlight:toggle' + 'space m': 'vim-mode-plus-user:quick-highlight-word' 'space M': 'quick-highlight:clear' 'g m': 'vim-mode-plus-user:quick-highlight' ``` diff --git a/spec/fixtures/sample-3 b/spec/fixtures/sample-3 index 73aeebf..5c10f23 100644 --- a/spec/fixtures/sample-3 +++ b/spec/fixtures/sample-3 @@ -1,6 +1,6 @@ orange - apple + apple orange - apple + apple orange - apple + apple diff --git a/spec/quick-highlight-spec.coffee b/spec/quick-highlight-spec.coffee index 7a92786..c683298 100644 --- a/spec/quick-highlight-spec.coffee +++ b/spec/quick-highlight-spec.coffee @@ -1,56 +1,34 @@ _ = require 'underscore-plus' -MARKER_REGEXP = /^quick-highlight/ # Helpers # ------------------------- getDecorations = (editor) -> - editor.getHighlightDecorations().filter (d) -> - d.properties.class.match MARKER_REGEXP + pattern = /^quick-highlight/ -getView = (model) -> - atom.views.getView(model) - -getVisibleBufferRowRange = (editor) -> - getView(editor).getVisibleRowRange().map (row) -> - editor.bufferRowForScreenRow row + decorations = [] + for id, decoration of editor.decorationsStateForScreenRowRange(0, editor.getLineCount()) + if decoration.properties.class.match(pattern) + decorations.push(decoration) + decorations setConfig = (name, value) -> atom.config.set("quick-highlight.#{name}", value) +ensureDecorations = (editor, options) -> + decorations = getDecorations(editor) + groupedDecoration = _.groupBy decorations, (decoration) -> + decoration.properties.class.replace(/^quick-highlight /, '') + + toText = ({bufferRange}) -> editor.getTextInBufferRange(bufferRange) + for color, texts of options + decoratedTexts = groupedDecoration[color].map(toText) + expect(decoratedTexts).toEqual(texts) + delete groupedDecoration[color] + + expect(groupedDecoration).toEqual({}) + # Main # ------------------------- -addCustomMatchers = (spec) -> - getNotText = -> - if spec.isNot then " not" else "" - - spec.addMatchers - toHaveDecorations: (expected) -> - notText = getNotText() - editor = @actual - decos = getDecorations(editor) - if expected.color? - pattern = ///#{_.escapeRegExp(expected.color)}/// - decos = decos.filter (d) -> d.properties.class.match pattern - - lengthOK = decos.length is expected.length - if expected.length is 0 - lengthOK - else - texts = (editor.getTextInBufferRange(d.getMarker().getBufferRange()) for d in decos) - this.message = -> "Expected #{jasmine.pp(texts)}, length: #{texts.length} to#{notText} #{jasmine.pp(expected)}" - lengthOK and _.all(texts, (text) -> text is expected.text) - - lengthOfDecorationsToBe: (expected) -> - getDecorations(@actual).length is expected - - toHaveAllMarkerDestoyed: (expected) -> - editor = @actual - results = (d.getMarker().isDestroyed() for d in getDecorations(editor)) - _.all results - - toBeActiveEditor: -> - @actual is atom.workspace.getActiveTextEditor() - describe "quick-highlight", -> [editor, editorContent, editorElement, main, workspaceElement, pathSample1, pathSample2] = [] @@ -59,166 +37,108 @@ describe "quick-highlight", -> atom.commands.dispatch(element, command) beforeEach -> - addCustomMatchers(this) spyOn(_._, "now").andCallFake -> window.now - workspaceElement = getView(atom.workspace) - jasmine.attachToDOM workspaceElement - activationPromise = null + workspaceElement = atom.views.getView(atom.workspace) + jasmine.attachToDOM(workspaceElement) editorContent = """ orange - apple + apple orange - apple + apple orange - apple + apple """ waitsForPromise -> - atom.workspace.open('sample-1').then (e) -> - editor = e - editor.setText editorContent - - runs -> - editorElement = getView(editor) - editor.setCursorBufferPosition([0, 0]) - activationPromise = atom.packages.activatePackage("quick-highlight").then (pack) -> + atom.packages.activatePackage("quick-highlight").then (pack) -> main = pack.mainModule - dispatchCommand('quick-highlight:toggle') waitsForPromise -> - activationPromise + atom.workspace.open('sample-1').then (e) -> + editor = e + editor.setText(editorContent) + editor.setCursorBufferPosition([0, 0]) + editorElement = editor.element describe "quick-highlight:toggle", -> - it "decorate keyword under cursor", -> - expect(main.colorByKeyword.has('orange')).toBe true - expect(main.colorByKeyword.has('apple')).toBe false - expect(editor).toHaveDecorations length: 3, color: '01', text: 'orange' + it "highlight keyword under cursor", -> + dispatchCommand('quick-highlight:toggle') + ensureDecorations(editor, "underline-01": ['orange', 'orange', 'orange']) it "remove decoration when if already decorated", -> - expect(main.colorByKeyword.has('orange')).toBe true - expect(main.colorByKeyword.has('apple')).toBe false - expect(editor).lengthOfDecorationsToBe 3 dispatchCommand('quick-highlight:toggle') - - expect(main.colorByKeyword.has('orange')).toBe false - expect(main.colorByKeyword.has('apple')).toBe false - expect(editor).lengthOfDecorationsToBe 0 + ensureDecorations(editor, "underline-01": ['orange', 'orange', 'orange']) + dispatchCommand('quick-highlight:toggle') + expect(getDecorations(editor)).toHaveLength(0) it "can decorate multiple keyword simultaneously", -> - expect(main.colorByKeyword.has('orange')).toBe true - expect(main.colorByKeyword.has('apple')).toBe false - expect(editor).lengthOfDecorationsToBe 3 - editor.setCursorScreenPosition [1, 12] dispatchCommand('quick-highlight:toggle') - - expect(main.colorByKeyword.has('orange')).toBe true - expect(main.colorByKeyword.has('apple')).toBe true - expect(editor).lengthOfDecorationsToBe 6 - expect(editor).toHaveDecorations color: '01', length: 3, text: 'orange' - expect(editor).toHaveDecorations color: '02', length: 3, text: 'apple' - - it "destroy decoration when editor is destroyed", -> - expect(main.colorByKeyword.has('orange')).toBe true - expect(editor).lengthOfDecorationsToBe 3 - editor.destroy() - expect(editor).toHaveAllMarkerDestoyed() + ensureDecorations editor, + "underline-01": ['orange', 'orange', 'orange'] + editor.setCursorBufferPosition([1, 3]) + dispatchCommand('quick-highlight:toggle') + ensureDecorations editor, + "underline-01": ['orange', 'orange', 'orange'] + "underline-02": ['apple', 'apple', 'apple'] describe "quick-highlight:clear", -> it "clear all decorations", -> - expect(main.colorByKeyword.has('orange')).toBe true - expect(main.colorByKeyword.has('apple')).toBe false - expect(editor).lengthOfDecorationsToBe 3 - editor.setCursorScreenPosition [1, 12] dispatchCommand('quick-highlight:toggle') - - expect(main.colorByKeyword.has('orange')).toBe true - expect(main.colorByKeyword.has('apple')).toBe true - expect(editor).lengthOfDecorationsToBe 6 - + editor.setCursorBufferPosition([1, 3]) + dispatchCommand('quick-highlight:toggle') + expect(getDecorations(editor)).toHaveLength(6) dispatchCommand('quick-highlight:clear') - expect(editor).lengthOfDecorationsToBe 0 - expect(main.colorByKeyword.has('orange')).toBe false - expect(main.colorByKeyword.has('apple')).toBe false - expect(editor).toHaveAllMarkerDestoyed() - expect(main.markersByEditor.has(editor)).toBe false + expect(getDecorations(editor)).toHaveLength(0) describe "multiple editors is displayed", -> [editor2, editor2Element] = [] beforeEach -> waitsForPromise -> - atom.workspace.open('sample-2', {split: 'right'}).then (e) -> + atom.workspace.open('sample-2', split: 'right').then (e) -> editor2 = e - editor2.setText editorContent - editor2Element = getView(editor2) + editor2.setText(editorContent) + editor2Element = editor2.element editor2.setCursorBufferPosition [0, 0] runs -> - expect(editor2).toBeActiveEditor() - dispatchCommand('quick-highlight:clear') - expect(editor).lengthOfDecorationsToBe 0 - expect(main.markersByEditor.has(editor)).toBe false - expect(editor2).lengthOfDecorationsToBe 0 - expect(main.markersByEditor.has(editor2)).toBe false - - it "can decorate keyword across visible editors", -> + expect(atom.workspace.getActiveTextEditor()).toBe(editor2) + + it "can highlight keyword across editors", -> dispatchCommand('quick-highlight:toggle', editor2Element) - expect(main.colorByKeyword.has('orange')).toBe true - expect(main.colorByKeyword.has('apple')).toBe false - expect(editor).toHaveDecorations color: '01', length: 3, text: 'orange' - expect(editor2).toHaveDecorations color: '01', length: 3, text: 'orange' - - it "clear selectionDecorations when activePane changed", -> - dispatchCommand('core:select-right', element: editor2Element) - dispatchCommand('core:select-right', element: editor2Element) - advanceClock(150) - expect(editor2.getSelectedText()).toBe "or" - expect(getDecorations(editor2)).toHaveLength 3 - dispatchCommand('window:focus-next-pane', element: editor2Element) - expect(editor).toBeActiveEditor() - expect(getDecorations(editor2)).toHaveLength 0 + ensureDecorations(editor, "underline-01": ['orange', 'orange', 'orange']) + ensureDecorations(editor2, "underline-01": ['orange', 'orange', 'orange']) it "decorate keywords when new editor was opened", -> dispatchCommand('quick-highlight:toggle', editor2Element) - expect(main.colorByKeyword.has('orange')).toBe true - expect(main.colorByKeyword.has('apple')).toBe false - editor3 = null - pathSample3 = atom.project.resolvePath "sample-3" + pathSample3 = atom.project.resolvePath("sample-3") waitsForPromise -> - atom.workspace.open(pathSample3, {split: 'right'}).then (e) -> + atom.workspace.open(pathSample3, split: 'right').then (e) -> editor3 = e runs -> - expect(editor).toHaveDecorations color: '01', length: 3, text: 'orange' - expect(editor2).toHaveDecorations color: '01', length: 3, text: 'orange' - expect(editor3).toHaveDecorations color: '01', length: 3, text: 'orange' + ensureDecorations(editor, "underline-01": ['orange', 'orange', 'orange']) + ensureDecorations(editor2, "underline-01": ['orange', 'orange', 'orange']) + ensureDecorations(editor3, "underline-01": ['orange', 'orange', 'orange']) describe "selection changed when highlightSelection", -> - beforeEach -> - dispatchCommand('quick-highlight:clear') - expect(editor).lengthOfDecorationsToBe 0 - expect(main.colorByKeyword.has('orange')).toBe false - expect(main.colorByKeyword.has('apple')).toBe false - expect(editor).toHaveAllMarkerDestoyed() - expect(main.markersByEditor.has(editor)).toBe false - it "decorate selected keyword", -> dispatchCommand('editor:select-word') advanceClock(150) - expect(editor).toHaveDecorations length: 3, color: 'selection', text: 'orange' + ensureDecorations(editor, "box-selection": ['orange', 'orange', 'orange']) - it "clear decoration when selection is cleared", -> + it "clear highlight when selection is cleared", -> dispatchCommand('editor:select-word') advanceClock(150) - expect(editor).toHaveDecorations length: 3, color: 'selection', text: 'orange' + ensureDecorations(editor, "box-selection": ['orange', 'orange', 'orange']) editor.clearSelections() advanceClock(150) - expect(getDecorations(editor)).toHaveLength 0 + expect(getDecorations(editor)).toHaveLength(0) - it "won't decorate selectedText length is less than highlightSelectionMinimumLength", -> + it "won't highlight selectedText length is less than highlightSelectionMinimumLength", -> setConfig('highlightSelectionMinimumLength', 3) dispatchCommand('core:select-right') advanceClock(150) @@ -231,26 +151,23 @@ describe "quick-highlight", -> dispatchCommand('core:select-right') advanceClock(150) expect(editor.getSelectedText()).toBe "ora" - expect(editor).toHaveDecorations color: 'selection', length: 3, text: 'ora' + ensureDecorations(editor, "box-selection": ['ora', 'ora', 'ora']) - it "won't decorate when selection is all white space", -> + it "won't highlight when selection is all white space", -> editor.setCursorBufferPosition([1, 0]) dispatchCommand('core:select-right') - dispatchCommand('core:select-right') advanceClock(150) - {start, end} = editor.getLastSelection().getBufferRange() - expect(start).toEqual [1, 0] - expect(end).toEqual [1, 4] + expect(editor.getSelectedText()).toBe " " expect(getDecorations(editor)).toHaveLength 0 - it "won't decorate when selection is multi-line", -> - editor.setCursorBufferPosition([1, 0]) + it "won't highlight when selection is multi-line", -> dispatchCommand('core:select-down') + dispatchCommand('core:select-down') + expect(editor.getSelectedText()).toBe "orange\n apple\n" advanceClock(150) - expect(editor.getLastSelection().isEmpty()).toBe false expect(getDecorations(editor)).toHaveLength 0 - it "won't decorate when highlightSelection is disabled", -> + it "won't highlight when highlightSelection is disabled", -> setConfig('highlightSelection', false) dispatchCommand('editor:select-word') advanceClock(150) @@ -264,15 +181,15 @@ describe "quick-highlight", -> 'hoge', ]) - it "won't decorate when editor have specified scope case-1", -> - editorElement.classList.add 'foo', 'bar' + it "won't highlight when editor have specified scope case-1", -> + editorElement.classList.add('foo', 'bar') dispatchCommand('editor:select-word') advanceClock(150) expect(editor.getSelectedText()).toBe "orange" expect(getDecorations(editor)).toHaveLength 0 - it "won't decorate when editor have specified scope case-2", -> - editorElement.classList.add 'hoge' + it "won't highlight when editor have specified scope case-2", -> + editorElement.classList.add('hoge') dispatchCommand('editor:select-word') advanceClock(150) expect(editor.getSelectedText()).toBe "orange" @@ -282,65 +199,25 @@ describe "quick-highlight", -> beforeEach -> setConfig('highlightSelectionDelay', 300) - it "highlight selection after specified delay", -> + xit "highlight selection after specified delay", -> + # FIXME: It delay works in interactive use, but I can't make it success in this spec. + # need investigation. dispatchCommand('editor:select-word') - advanceClock(150) expect(editor.getSelectedText()).toBe "orange" expect(getDecorations(editor)).toHaveLength 0 advanceClock(150) - expect(editor).toHaveDecorations color: 'selection', length: 3, text: 'orange' - - describe "editor is scrolled", -> - [editor4, editorElement4] = [] - lineHeightPx = 10 - rowsPerPage = 10 - scroll = (editor) -> - el = getView(editor) - amountInPixel = editor.getRowsPerPage() * editor.getLineHeightInPixels() - el.setScrollTop(el.getScrollTop() + amountInPixel) - - beforeEach -> - runs -> - dispatchCommand('quick-highlight:clear') - - pathSample4 = atom.project.resolvePath "sample-4" - - waitsForPromise -> - atom.workspace.open(pathSample4).then (e) -> - editor4 = e - editorElement4 = getView(editor4) - editorElement4.setHeight(rowsPerPage * lineHeightPx) - editorElement4.style.font = "12px monospace" - editorElement4.style.lineHeight = "#{lineHeightPx}px" - atom.views.performDocumentPoll() - - runs -> - editor4.setCursorScreenPosition [1, 0] - dispatchCommand('quick-highlight:toggle', element: editorElement4) - editor4.setCursorBufferPosition [3, 0] - dispatchCommand('quick-highlight:toggle', element: editorElement4) - expect(main.colorByKeyword.has('orange')).toBe true - expect(main.colorByKeyword.has('apple')).toBe true - - describe "decorate only visible area", -> - it "update decoration on scroll", -> - expect(editor4).toHaveDecorations color: '01', length: 1, text: 'orange' - expect(editor4).toHaveDecorations color: '02', length: 1, text: 'apple' - scroll(editor4) - expect(editor4).toHaveDecorations color: '01', length: 2, text: 'orange' - expect(editor4).toHaveDecorations color: '02', length: 1, text: 'apple' - scroll(editor4) - expect(editor4).toHaveDecorations color: '01', length: 0 - expect(editor4).toHaveDecorations color: '02', length: 3, text: 'apple' - - describe "::getCountForKeyword", -> - it 'return count of keyword within editor', -> - expect(main.getCountForKeyword(editor4, 'orange')).toBe 3 - expect(main.getCountForKeyword(editor4, 'apple')).toBe 5 + expect(getDecorations(editor)).toHaveLength 0 + advanceClock(150) + ensureDecorations(editor, "box-selection": ['orange', 'orange', 'orange']) describe "displayCountOnStatusBar", -> - [container, span] = [] + [editor3, container, span] = [] beforeEach -> + editor.setText """ + apple orange + orange lemon orange + apple + """ waitsForPromise -> atom.packages.activatePackage("status-bar") @@ -348,24 +225,29 @@ describe "quick-highlight", -> main.statusBarManager.tile? runs -> - dispatchCommand('quick-highlight:clear', element: editorElement4) - editor4.setCursorScreenPosition [1, 0] - dispatchCommand('quick-highlight:toggle', element: editorElement4) - expect(editor4).toHaveDecorations color: '01', length: 1, text: 'orange' container = workspaceElement.querySelector('#status-bar-quick-highlight') span = container.querySelector('span') it 'display latest highlighted count on statusbar', -> + editor.setCursorBufferPosition([0, 0]) + dispatchCommand('quick-highlight:toggle') + ensureDecorations(editor, "underline-01": ['apple', 'apple']) expect(container.style.display).toBe 'inline-block' - expect(span.textContent).toBe '3' + expect(span.textContent).toBe '2' - editor4.setCursorScreenPosition [3, 0] - dispatchCommand('quick-highlight:toggle', element: editorElement4) - expect(editor4).toHaveDecorations color: '02', length: 1, text: 'apple' + editor.setCursorBufferPosition([1, 0]) + dispatchCommand('quick-highlight:toggle') + ensureDecorations editor, + "underline-01": ['apple', 'apple'] + "underline-02": ['orange', 'orange', 'orange'] expect(container.style.display).toBe 'inline-block' - expect(span.textContent).toBe '5' + expect(span.textContent).toBe '3' - it 'hide count when decoration cleared', -> - dispatchCommand('quick-highlight:toggle', element: editorElement4) - expect(editor4).lengthOfDecorationsToBe 0 - expect(container.style.display).toBe 'none' + editor.setCursorBufferPosition([1, 10]) + dispatchCommand('quick-highlight:toggle') + ensureDecorations editor, + "underline-01": ['apple', 'apple'] + "underline-02": ['orange', 'orange', 'orange'] + "underline-03": ['lemon'] + expect(container.style.display).toBe 'inline-block' + expect(span.textContent).toBe '1' From 51637f501e36a7fb657cdd403ce43dc6c2c1e6bf Mon Sep 17 00:00:00 2001 From: t9md Date: Sun, 8 Jan 2017 06:20:00 +0900 Subject: [PATCH 18/18] cleanup pass spec --- spec/quick-highlight-spec.coffee | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/spec/quick-highlight-spec.coffee b/spec/quick-highlight-spec.coffee index c683298..9c4c580 100644 --- a/spec/quick-highlight-spec.coffee +++ b/spec/quick-highlight-spec.coffee @@ -37,8 +37,6 @@ describe "quick-highlight", -> atom.commands.dispatch(element, command) beforeEach -> - spyOn(_._, "now").andCallFake -> window.now - workspaceElement = atom.views.getView(atom.workspace) jasmine.attachToDOM(workspaceElement) @@ -125,6 +123,9 @@ describe "quick-highlight", -> ensureDecorations(editor3, "underline-01": ['orange', 'orange', 'orange']) describe "selection changed when highlightSelection", -> + beforeEach -> + spyOn(_._, "now").andCallFake -> window.now + it "decorate selected keyword", -> dispatchCommand('editor:select-word') advanceClock(150) @@ -199,30 +200,29 @@ describe "quick-highlight", -> beforeEach -> setConfig('highlightSelectionDelay', 300) - xit "highlight selection after specified delay", -> - # FIXME: It delay works in interactive use, but I can't make it success in this spec. - # need investigation. + it "highlight selection after specified delay", -> dispatchCommand('editor:select-word') expect(editor.getSelectedText()).toBe "orange" expect(getDecorations(editor)).toHaveLength 0 - advanceClock(150) + advanceClock(100) expect(getDecorations(editor)).toHaveLength 0 - advanceClock(150) + advanceClock(100) + expect(getDecorations(editor)).toHaveLength 0 + advanceClock(100) + expect(getDecorations(editor)).toHaveLength 3 ensureDecorations(editor, "box-selection": ['orange', 'orange', 'orange']) describe "displayCountOnStatusBar", -> [editor3, container, span] = [] beforeEach -> editor.setText """ - apple orange - orange lemon orange - apple - """ - waitsForPromise -> - atom.packages.activatePackage("status-bar") + apple orange + orange lemon orange + apple + """ - waitsFor -> - main.statusBarManager.tile? + waitsForPromise -> atom.packages.activatePackage("status-bar") + waitsFor -> main.statusBarManager.tile? runs -> container = workspaceElement.querySelector('#status-bar-quick-highlight')