From 2d8be51e71793e3cf313fcda3acff8bb520006b9 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 3 Oct 2012 17:40:59 -0700 Subject: [PATCH 01/11] Add initial editor command super class This can be extended by extensions targetted towards acting on text inside the editor and not contributing any UI --- spec/extensions/editor-command-spec.coffee | 150 +++++++++++++++++++++ src/extensions/editor-command.coffee | 33 +++++ src/extensions/lowercase-command.coffee | 11 ++ src/extensions/uppercase-command.coffee | 11 ++ 4 files changed, 205 insertions(+) create mode 100644 spec/extensions/editor-command-spec.coffee create mode 100644 src/extensions/editor-command.coffee create mode 100644 src/extensions/lowercase-command.coffee create mode 100644 src/extensions/uppercase-command.coffee diff --git a/spec/extensions/editor-command-spec.coffee b/spec/extensions/editor-command-spec.coffee new file mode 100644 index 000000000..9b1b9cf03 --- /dev/null +++ b/spec/extensions/editor-command-spec.coffee @@ -0,0 +1,150 @@ +EditorCommand = require 'editor-command' +LowerCaseCommand = require 'lowercase-command' +UpperCaseCommand = require 'uppercase-command' +RootView = require 'root-view' +fs = require 'fs' + +describe "EditorCommand", -> + [rootView, editor, path] = [] + + beforeEach -> + rootView = new RootView + rootView.open(require.resolve 'fixtures/sample.js') + + rootView.focus() + editor = rootView.getActiveEditor() + + afterEach -> + rootView.remove() + + describe "@alterSelection()", -> + it "returns true when transformed text is non-empty", -> + transformed = false + altered = false + class CustomCommand extends EditorCommand + @getKeymaps: (editor) -> + 'meta-V': 'custom' + + @execute: (editor, event) -> + altered = @alterSelection editor, (text) -> + transformed = true + 'new' + + CustomCommand.activate(rootView) + editor.moveCursorToTop() + editor.selectToEndOfLine() + editor.trigger 'custom' + expect(transformed).toBe true + expect(altered).toBe true + + it "returns false when transformed text is null", -> + transformed = false + altered = false + class CustomCommand extends EditorCommand + @getKeymaps: (editor) -> + 'meta-V': 'custom' + + @execute: (editor, event) -> + altered = @alterSelection editor, (text) -> + transformed = true + null + + CustomCommand.activate(rootView) + editor.moveCursorToTop() + editor.selectToEndOfLine() + editor.trigger 'custom' + expect(transformed).toBe true + expect(altered).toBe false + + it "returns false when transformed text is undefined", -> + transformed = false + altered = false + class CustomCommand extends EditorCommand + @getKeymaps: (editor) -> + 'meta-V': 'custom' + + @execute: (editor, event) -> + altered = @alterSelection editor, (text) -> + transformed = true + undefined + + CustomCommand.activate(rootView) + editor.moveCursorToTop() + editor.selectToEndOfLine() + editor.trigger 'custom' + expect(transformed).toBe true + expect(altered).toBe false + + describe "custom sub-class", -> + it "removes vowels from selected text", -> + class VowelRemover extends EditorCommand + @getKeymaps: (editor) -> + 'meta-V': 'devowel' + + @execute: (editor, event) -> + @alterSelection editor, (text) -> + text.replace(/[aeiouy]/gi, '') + + VowelRemover.activate(rootView) + editor.moveCursorToTop() + editor.selectToEndOfLine() + editor.trigger 'devowel' + expect(editor.lineForBufferRow(0)).toBe 'vr qcksrt = fnctn () {' + expect(editor.getTextInRange(editor.getSelection().getBufferRange())).toBe 'vr qcksrt = fnctn () {' + + it "doesn't transform empty selections", -> + callbackCount = 0 + class CustomCommand extends EditorCommand + @getKeymaps: (editor) -> + 'meta-V': 'custom' + + @execute: (editor, event) -> + @alterSelection editor, (text) -> + callbackCount++ + text + + CustomCommand.activate(rootView) + editor.moveCursorToTop() + editor.selectToEndOfLine() + editor.trigger 'custom' + expect(callbackCount).toBe 1 + editor.clearSelections() + editor.trigger 'custom' + expect(callbackCount).toBe 1 + + it "registers all keymaps", -> + callbackCount = 0 + class CustomCommand extends EditorCommand + @getKeymaps: (editor) -> + 'meta-V': 'custom1' + 'meta-B': 'custom2' + + @execute: (editor, event) -> + @alterSelection editor, (text) -> + callbackCount++ + text + + CustomCommand.activate(rootView) + editor.moveCursorToTop() + editor.selectToEndOfLine() + editor.trigger 'custom1' + expect(callbackCount).toBe 1 + editor.trigger 'custom2' + expect(callbackCount).toBe 2 + + describe "LowerCaseCommand", -> + it "replaces the selected text with all lower case characters", -> + LowerCaseCommand.activate(rootView) + editor.setSelectedBufferRange([[11,14], [11,19]]) + expect(editor.getTextInRange(editor.getSelection().getBufferRange())).toBe 'Array' + editor.trigger 'lowercase' + expect(editor.getTextInRange(editor.getSelection().getBufferRange())).toBe 'array' + + + describe "UpperCaseCommand", -> + it "replaces the selected text with all upper case characters", -> + UpperCaseCommand.activate(rootView) + editor.setSelectedBufferRange([[0,0], [0,3]]) + expect(editor.getTextInRange(editor.getSelection().getBufferRange())).toBe 'var' + editor.trigger 'uppercase' + expect(editor.getTextInRange(editor.getSelection().getBufferRange())).toBe 'VAR' diff --git a/src/extensions/editor-command.coffee b/src/extensions/editor-command.coffee new file mode 100644 index 000000000..100c9ded7 --- /dev/null +++ b/src/extensions/editor-command.coffee @@ -0,0 +1,33 @@ +module.exports = +class EditorCommand + + @activate: (rootView) -> + keymaps = @getKeymaps() + return unless keymaps + + window.keymap.bindKeys '.editor', keymaps + + for editor in rootView.getEditors() + @subscribeToEditor(rootView, editor) + + rootView.on 'editor-open', (e, editor) => + @subscribeToEditor(rootView, editor) + + @subscribeToEditor: (rootView, editor) -> + keymaps = @getKeymaps(rootView, editor) + return unless keymaps + + for key, event of keymaps + editor.on event, => @execute(editor, event) + + @alterSelection: (editor, transform) -> + selection = editor.getSelection() + return false if selection.isEmpty() + + range = selection.getBufferRange() + reverse = selection.isReversed() + text = transform(editor.getTextInRange(range)) + return false if text is null or text is undefined + editor.insertText(text) + selection.setBufferRange(range, {reverse}) + true diff --git a/src/extensions/lowercase-command.coffee b/src/extensions/lowercase-command.coffee new file mode 100644 index 000000000..57ac1242d --- /dev/null +++ b/src/extensions/lowercase-command.coffee @@ -0,0 +1,11 @@ +EditorCommand = require 'editor-command' + +module.exports = +class LowerCaseCommand extends EditorCommand + + @getKeymaps: (editor) -> + 'meta-Y': 'lowercase' + + @execute: (editor, event) -> + @alterSelection editor, (text) -> + text.toLowerCase() diff --git a/src/extensions/uppercase-command.coffee b/src/extensions/uppercase-command.coffee new file mode 100644 index 000000000..5a63f87e8 --- /dev/null +++ b/src/extensions/uppercase-command.coffee @@ -0,0 +1,11 @@ +EditorCommand = require 'editor-command' + +module.exports = +class UpperCaseCommand extends EditorCommand + + @getKeymaps: (editor) -> + 'meta-X': 'uppercase' + + @execute: (editor, event) -> + @alterSelection editor, (text) -> + text.toUpperCase() From d93a1422634640b88332c71a2f400cbd41ac818a Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 3 Oct 2012 21:07:50 -0700 Subject: [PATCH 02/11] Rename alterSelection to editSelectedText --- spec/extensions/editor-command-spec.coffee | 26 +++++++++++----------- src/extensions/editor-command.coffee | 2 +- src/extensions/lowercase-command.coffee | 2 +- src/extensions/uppercase-command.coffee | 2 +- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/spec/extensions/editor-command-spec.coffee b/spec/extensions/editor-command-spec.coffee index 9b1b9cf03..e6d8e4234 100644 --- a/spec/extensions/editor-command-spec.coffee +++ b/spec/extensions/editor-command-spec.coffee @@ -17,16 +17,16 @@ describe "EditorCommand", -> afterEach -> rootView.remove() - describe "@alterSelection()", -> + describe "@editSelectedText()", -> it "returns true when transformed text is non-empty", -> transformed = false - altered = false + edited = false class CustomCommand extends EditorCommand @getKeymaps: (editor) -> 'meta-V': 'custom' @execute: (editor, event) -> - altered = @alterSelection editor, (text) -> + edited = @editSelectedText editor, (text) -> transformed = true 'new' @@ -35,17 +35,17 @@ describe "EditorCommand", -> editor.selectToEndOfLine() editor.trigger 'custom' expect(transformed).toBe true - expect(altered).toBe true + expect(edited).toBe true it "returns false when transformed text is null", -> transformed = false - altered = false + edited = false class CustomCommand extends EditorCommand @getKeymaps: (editor) -> 'meta-V': 'custom' @execute: (editor, event) -> - altered = @alterSelection editor, (text) -> + edited = @editSelectedText editor, (text) -> transformed = true null @@ -54,17 +54,17 @@ describe "EditorCommand", -> editor.selectToEndOfLine() editor.trigger 'custom' expect(transformed).toBe true - expect(altered).toBe false + expect(edited).toBe false it "returns false when transformed text is undefined", -> transformed = false - altered = false + edited = false class CustomCommand extends EditorCommand @getKeymaps: (editor) -> 'meta-V': 'custom' @execute: (editor, event) -> - altered = @alterSelection editor, (text) -> + edited = @editSelectedText editor, (text) -> transformed = true undefined @@ -73,7 +73,7 @@ describe "EditorCommand", -> editor.selectToEndOfLine() editor.trigger 'custom' expect(transformed).toBe true - expect(altered).toBe false + expect(edited).toBe false describe "custom sub-class", -> it "removes vowels from selected text", -> @@ -82,7 +82,7 @@ describe "EditorCommand", -> 'meta-V': 'devowel' @execute: (editor, event) -> - @alterSelection editor, (text) -> + @editSelectedText editor, (text) -> text.replace(/[aeiouy]/gi, '') VowelRemover.activate(rootView) @@ -99,7 +99,7 @@ describe "EditorCommand", -> 'meta-V': 'custom' @execute: (editor, event) -> - @alterSelection editor, (text) -> + @editSelectedText editor, (text) -> callbackCount++ text @@ -120,7 +120,7 @@ describe "EditorCommand", -> 'meta-B': 'custom2' @execute: (editor, event) -> - @alterSelection editor, (text) -> + @editSelectedText editor, (text) -> callbackCount++ text diff --git a/src/extensions/editor-command.coffee b/src/extensions/editor-command.coffee index 100c9ded7..f3310b2ff 100644 --- a/src/extensions/editor-command.coffee +++ b/src/extensions/editor-command.coffee @@ -20,7 +20,7 @@ class EditorCommand for key, event of keymaps editor.on event, => @execute(editor, event) - @alterSelection: (editor, transform) -> + @editSelectedText: (editor, transform) -> selection = editor.getSelection() return false if selection.isEmpty() diff --git a/src/extensions/lowercase-command.coffee b/src/extensions/lowercase-command.coffee index 57ac1242d..51bc9654a 100644 --- a/src/extensions/lowercase-command.coffee +++ b/src/extensions/lowercase-command.coffee @@ -7,5 +7,5 @@ class LowerCaseCommand extends EditorCommand 'meta-Y': 'lowercase' @execute: (editor, event) -> - @alterSelection editor, (text) -> + @editSelectedText editor, (text) -> text.toLowerCase() diff --git a/src/extensions/uppercase-command.coffee b/src/extensions/uppercase-command.coffee index 5a63f87e8..2bc7e8e18 100644 --- a/src/extensions/uppercase-command.coffee +++ b/src/extensions/uppercase-command.coffee @@ -7,5 +7,5 @@ class UpperCaseCommand extends EditorCommand 'meta-X': 'uppercase' @execute: (editor, event) -> - @alterSelection editor, (text) -> + @editSelectedText editor, (text) -> text.toUpperCase() From 863f9f36fbe0437b8bc1a1b3cdfac485f6dd4e9a Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 3 Oct 2012 21:52:50 -0700 Subject: [PATCH 03/11] Rename editSelectedText to replaceSelectedText --- spec/extensions/editor-command-spec.coffee | 14 +++++++------- src/extensions/editor-command.coffee | 4 ++-- src/extensions/lowercase-command.coffee | 2 +- src/extensions/uppercase-command.coffee | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/spec/extensions/editor-command-spec.coffee b/spec/extensions/editor-command-spec.coffee index e6d8e4234..27912bd71 100644 --- a/spec/extensions/editor-command-spec.coffee +++ b/spec/extensions/editor-command-spec.coffee @@ -17,7 +17,7 @@ describe "EditorCommand", -> afterEach -> rootView.remove() - describe "@editSelectedText()", -> + describe "@replaceSelectedText()", -> it "returns true when transformed text is non-empty", -> transformed = false edited = false @@ -26,7 +26,7 @@ describe "EditorCommand", -> 'meta-V': 'custom' @execute: (editor, event) -> - edited = @editSelectedText editor, (text) -> + edited = @replaceSelectedText editor, (text) -> transformed = true 'new' @@ -45,7 +45,7 @@ describe "EditorCommand", -> 'meta-V': 'custom' @execute: (editor, event) -> - edited = @editSelectedText editor, (text) -> + edited = @replaceSelectedText editor, (text) -> transformed = true null @@ -64,7 +64,7 @@ describe "EditorCommand", -> 'meta-V': 'custom' @execute: (editor, event) -> - edited = @editSelectedText editor, (text) -> + edited = @replaceSelectedText editor, (text) -> transformed = true undefined @@ -82,7 +82,7 @@ describe "EditorCommand", -> 'meta-V': 'devowel' @execute: (editor, event) -> - @editSelectedText editor, (text) -> + @replaceSelectedText editor, (text) -> text.replace(/[aeiouy]/gi, '') VowelRemover.activate(rootView) @@ -99,7 +99,7 @@ describe "EditorCommand", -> 'meta-V': 'custom' @execute: (editor, event) -> - @editSelectedText editor, (text) -> + @replaceSelectedText editor, (text) -> callbackCount++ text @@ -120,7 +120,7 @@ describe "EditorCommand", -> 'meta-B': 'custom2' @execute: (editor, event) -> - @editSelectedText editor, (text) -> + @replaceSelectedText editor, (text) -> callbackCount++ text diff --git a/src/extensions/editor-command.coffee b/src/extensions/editor-command.coffee index f3310b2ff..22d11adda 100644 --- a/src/extensions/editor-command.coffee +++ b/src/extensions/editor-command.coffee @@ -20,13 +20,13 @@ class EditorCommand for key, event of keymaps editor.on event, => @execute(editor, event) - @editSelectedText: (editor, transform) -> + @replaceSelectedText: (editor, replace) -> selection = editor.getSelection() return false if selection.isEmpty() range = selection.getBufferRange() reverse = selection.isReversed() - text = transform(editor.getTextInRange(range)) + text = replace(editor.getTextInRange(range)) return false if text is null or text is undefined editor.insertText(text) selection.setBufferRange(range, {reverse}) diff --git a/src/extensions/lowercase-command.coffee b/src/extensions/lowercase-command.coffee index 51bc9654a..1dd6d7a33 100644 --- a/src/extensions/lowercase-command.coffee +++ b/src/extensions/lowercase-command.coffee @@ -7,5 +7,5 @@ class LowerCaseCommand extends EditorCommand 'meta-Y': 'lowercase' @execute: (editor, event) -> - @editSelectedText editor, (text) -> + @replaceSelectedText editor, (text) -> text.toLowerCase() diff --git a/src/extensions/uppercase-command.coffee b/src/extensions/uppercase-command.coffee index 2bc7e8e18..a79b1969b 100644 --- a/src/extensions/uppercase-command.coffee +++ b/src/extensions/uppercase-command.coffee @@ -7,5 +7,5 @@ class UpperCaseCommand extends EditorCommand 'meta-X': 'uppercase' @execute: (editor, event) -> - @editSelectedText editor, (text) -> + @replaceSelectedText editor, (text) -> text.toUpperCase() From ceb496e202908c035d3ea56302c9227fc840b78e Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 4 Oct 2012 11:07:46 -0700 Subject: [PATCH 04/11] Use closure wrapper with current event name --- spec/extensions/editor-command-spec.coffee | 4 ++++ src/extensions/editor-command.coffee | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/spec/extensions/editor-command-spec.coffee b/spec/extensions/editor-command-spec.coffee index 27912bd71..eda5d9c7e 100644 --- a/spec/extensions/editor-command-spec.coffee +++ b/spec/extensions/editor-command-spec.coffee @@ -114,12 +114,14 @@ describe "EditorCommand", -> it "registers all keymaps", -> callbackCount = 0 + eventName = null class CustomCommand extends EditorCommand @getKeymaps: (editor) -> 'meta-V': 'custom1' 'meta-B': 'custom2' @execute: (editor, event) -> + eventName = event @replaceSelectedText editor, (text) -> callbackCount++ text @@ -129,7 +131,9 @@ describe "EditorCommand", -> editor.selectToEndOfLine() editor.trigger 'custom1' expect(callbackCount).toBe 1 + expect(eventName).toBe 'custom1' editor.trigger 'custom2' + expect(eventName).toBe 'custom2' expect(callbackCount).toBe 2 describe "LowerCaseCommand", -> diff --git a/src/extensions/editor-command.coffee b/src/extensions/editor-command.coffee index 22d11adda..426bf65c3 100644 --- a/src/extensions/editor-command.coffee +++ b/src/extensions/editor-command.coffee @@ -18,7 +18,9 @@ class EditorCommand return unless keymaps for key, event of keymaps - editor.on event, => @execute(editor, event) + do (event) => + editor.on event, => + @execute(editor, event) @replaceSelectedText: (editor, replace) -> selection = editor.getSelection() From e87cb34d1d3ce1bfcdbcf19fa962c225d28d2af0 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 4 Oct 2012 16:32:55 -0700 Subject: [PATCH 05/11] Support selecting inserted text --- spec/extensions/editor-command-spec.coffee | 19 +++++++++++++++++++ src/app/editor.coffee | 2 +- src/app/selection.coffee | 5 ++++- src/extensions/editor-command.coffee | 8 +++----- 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/spec/extensions/editor-command-spec.coffee b/spec/extensions/editor-command-spec.coffee index eda5d9c7e..da584fb19 100644 --- a/spec/extensions/editor-command-spec.coffee +++ b/spec/extensions/editor-command-spec.coffee @@ -91,6 +91,25 @@ describe "EditorCommand", -> editor.trigger 'devowel' expect(editor.lineForBufferRow(0)).toBe 'vr qcksrt = fnctn () {' expect(editor.getTextInRange(editor.getSelection().getBufferRange())).toBe 'vr qcksrt = fnctn () {' + expect(editor.getCursorBufferPosition()).toBe(editor.getSelection().getBufferRange().end) + + it "maintains reversed selections", -> + class VowelRemover extends EditorCommand + @getKeymaps: (editor) -> + 'meta-V': 'devowel' + + @execute: (editor, event) -> + @replaceSelectedText editor, (text) -> + text.replace(/[aeiouy]/gi, '') + + VowelRemover.activate(rootView) + editor.moveCursorToTop() + editor.moveCursorToEndOfLine() + editor.selectToBeginningOfLine() + editor.trigger 'devowel' + expect(editor.lineForBufferRow(0)).toBe 'vr qcksrt = fnctn () {' + expect(editor.getTextInRange(editor.getSelection().getBufferRange())).toBe 'vr qcksrt = fnctn () {' + expect(editor.getCursorBufferPosition()).toBe(editor.getSelection().getBufferRange().start) it "doesn't transform empty selections", -> callbackCount = 0 diff --git a/src/app/editor.coffee b/src/app/editor.coffee index 3da6a88a1..41c69752d 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -213,7 +213,7 @@ class Editor extends View deleteToEndOfWord: -> @activeEditSession.deleteToEndOfWord() deleteLine: -> @activeEditSession.deleteLine() cutToEndOfLine: -> @activeEditSession.cutToEndOfLine() - insertText: (text) -> @activeEditSession.insertText(text) + insertText: (text, options) -> @activeEditSession.insertText(text, options) insertNewline: -> @activeEditSession.insertNewline() insertNewlineBelow: -> @activeEditSession.insertNewlineBelow() indent: -> @activeEditSession.indent() diff --git a/src/app/selection.coffee b/src/app/selection.coffee index 0e0b711f7..b666ce8e5 100644 --- a/src/app/selection.coffee +++ b/src/app/selection.coffee @@ -139,7 +139,10 @@ class Selection wasReversed = @isReversed() @clear() newBufferRange = @editSession.buffer.change(oldBufferRange, text) - @cursor.setBufferPosition(newBufferRange.end, skipAtomicTokens: true) if wasReversed + if options.select + @setBufferRange(newBufferRange, reverse: wasReversed) + else + @cursor.setBufferPosition(newBufferRange.end, skipAtomicTokens: true) if wasReversed autoIndent = options.autoIndent ? true diff --git a/src/extensions/editor-command.coffee b/src/extensions/editor-command.coffee index 426bf65c3..8f2e72ab8 100644 --- a/src/extensions/editor-command.coffee +++ b/src/extensions/editor-command.coffee @@ -26,10 +26,8 @@ class EditorCommand selection = editor.getSelection() return false if selection.isEmpty() - range = selection.getBufferRange() - reverse = selection.isReversed() - text = replace(editor.getTextInRange(range)) + text = replace(editor.getTextInRange(selection.getBufferRange())) return false if text is null or text is undefined - editor.insertText(text) - selection.setBufferRange(range, {reverse}) + + editor.insertText(text, select: true) true From 1fa32c48e713c98a523aeb176057bb140ca89b26 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 4 Oct 2012 17:08:42 -0700 Subject: [PATCH 06/11] Invoke onEditor on each extension sub-class --- spec/extensions/editor-command-spec.coffee | 89 +++++++--------------- src/extensions/editor-command.coffee | 23 ++---- src/extensions/lowercase-command.coffee | 10 +-- src/extensions/uppercase-command.coffee | 10 +-- 4 files changed, 44 insertions(+), 88 deletions(-) diff --git a/spec/extensions/editor-command-spec.coffee b/spec/extensions/editor-command-spec.coffee index da584fb19..96b2f08ac 100644 --- a/spec/extensions/editor-command-spec.coffee +++ b/spec/extensions/editor-command-spec.coffee @@ -22,13 +22,12 @@ describe "EditorCommand", -> transformed = false edited = false class CustomCommand extends EditorCommand - @getKeymaps: (editor) -> - 'meta-V': 'custom' - @execute: (editor, event) -> - edited = @replaceSelectedText editor, (text) -> - transformed = true - 'new' + @onEditor: (editor) -> + @register editor, 'meta-V', 'custom', => + edited = @replaceSelectedText editor, (text) -> + transformed = true + 'new' CustomCommand.activate(rootView) editor.moveCursorToTop() @@ -41,13 +40,12 @@ describe "EditorCommand", -> transformed = false edited = false class CustomCommand extends EditorCommand - @getKeymaps: (editor) -> - 'meta-V': 'custom' - @execute: (editor, event) -> - edited = @replaceSelectedText editor, (text) -> - transformed = true - null + @onEditor: (editor) -> + @register editor, 'meta-V', 'custom', => + edited = @replaceSelectedText editor, (text) -> + transformed = true + null CustomCommand.activate(rootView) editor.moveCursorToTop() @@ -60,13 +58,12 @@ describe "EditorCommand", -> transformed = false edited = false class CustomCommand extends EditorCommand - @getKeymaps: (editor) -> - 'meta-V': 'custom' - @execute: (editor, event) -> - edited = @replaceSelectedText editor, (text) -> - transformed = true - undefined + @onEditor: (editor) -> + @register editor, 'meta-V', 'custom', => + edited = @replaceSelectedText editor, (text) -> + transformed = true + undefined CustomCommand.activate(rootView) editor.moveCursorToTop() @@ -78,12 +75,11 @@ describe "EditorCommand", -> describe "custom sub-class", -> it "removes vowels from selected text", -> class VowelRemover extends EditorCommand - @getKeymaps: (editor) -> - 'meta-V': 'devowel' - @execute: (editor, event) -> - @replaceSelectedText editor, (text) -> - text.replace(/[aeiouy]/gi, '') + @onEditor: (editor) -> + @register editor, 'meta-V', 'devowel', => + @replaceSelectedText editor, (text) -> + text.replace(/[aeiouy]/gi, '') VowelRemover.activate(rootView) editor.moveCursorToTop() @@ -95,12 +91,10 @@ describe "EditorCommand", -> it "maintains reversed selections", -> class VowelRemover extends EditorCommand - @getKeymaps: (editor) -> - 'meta-V': 'devowel' - - @execute: (editor, event) -> - @replaceSelectedText editor, (text) -> - text.replace(/[aeiouy]/gi, '') + @onEditor: (editor) -> + @register editor, 'meta-V', 'devowel', => + @replaceSelectedText editor, (text) -> + text.replace(/[aeiouy]/gi, '') VowelRemover.activate(rootView) editor.moveCursorToTop() @@ -114,13 +108,11 @@ describe "EditorCommand", -> it "doesn't transform empty selections", -> callbackCount = 0 class CustomCommand extends EditorCommand - @getKeymaps: (editor) -> - 'meta-V': 'custom' - - @execute: (editor, event) -> - @replaceSelectedText editor, (text) -> - callbackCount++ - text + @onEditor: (editor) -> + @register editor, 'meta-V', 'custom', => + @replaceSelectedText editor, (text) -> + callbackCount++ + text CustomCommand.activate(rootView) editor.moveCursorToTop() @@ -131,30 +123,6 @@ describe "EditorCommand", -> editor.trigger 'custom' expect(callbackCount).toBe 1 - it "registers all keymaps", -> - callbackCount = 0 - eventName = null - class CustomCommand extends EditorCommand - @getKeymaps: (editor) -> - 'meta-V': 'custom1' - 'meta-B': 'custom2' - - @execute: (editor, event) -> - eventName = event - @replaceSelectedText editor, (text) -> - callbackCount++ - text - - CustomCommand.activate(rootView) - editor.moveCursorToTop() - editor.selectToEndOfLine() - editor.trigger 'custom1' - expect(callbackCount).toBe 1 - expect(eventName).toBe 'custom1' - editor.trigger 'custom2' - expect(eventName).toBe 'custom2' - expect(callbackCount).toBe 2 - describe "LowerCaseCommand", -> it "replaces the selected text with all lower case characters", -> LowerCaseCommand.activate(rootView) @@ -163,7 +131,6 @@ describe "EditorCommand", -> editor.trigger 'lowercase' expect(editor.getTextInRange(editor.getSelection().getBufferRange())).toBe 'array' - describe "UpperCaseCommand", -> it "replaces the selected text with all upper case characters", -> UpperCaseCommand.activate(rootView) diff --git a/src/extensions/editor-command.coffee b/src/extensions/editor-command.coffee index 8f2e72ab8..e4a649d07 100644 --- a/src/extensions/editor-command.coffee +++ b/src/extensions/editor-command.coffee @@ -2,25 +2,18 @@ module.exports = class EditorCommand @activate: (rootView) -> - keymaps = @getKeymaps() - return unless keymaps - - window.keymap.bindKeys '.editor', keymaps - for editor in rootView.getEditors() - @subscribeToEditor(rootView, editor) + @onEditor(editor) rootView.on 'editor-open', (e, editor) => - @subscribeToEditor(rootView, editor) + @onEditor(editor) - @subscribeToEditor: (rootView, editor) -> - keymaps = @getKeymaps(rootView, editor) - return unless keymaps - - for key, event of keymaps - do (event) => - editor.on event, => - @execute(editor, event) + @register: (editor, key, event, callback) -> + binding = {} + binding[key] = event + window.keymap.bindKeys '.editor', binding + editor.on event, => + callback(editor, event) @replaceSelectedText: (editor, replace) -> selection = editor.getSelection() diff --git a/src/extensions/lowercase-command.coffee b/src/extensions/lowercase-command.coffee index 1dd6d7a33..7254fb72d 100644 --- a/src/extensions/lowercase-command.coffee +++ b/src/extensions/lowercase-command.coffee @@ -3,9 +3,7 @@ EditorCommand = require 'editor-command' module.exports = class LowerCaseCommand extends EditorCommand - @getKeymaps: (editor) -> - 'meta-Y': 'lowercase' - - @execute: (editor, event) -> - @replaceSelectedText editor, (text) -> - text.toLowerCase() + @onEditor: (editor) -> + @register editor, 'meta-Y', 'lowercase', => + @replaceSelectedText editor, (text) -> + text.toLowerCase() diff --git a/src/extensions/uppercase-command.coffee b/src/extensions/uppercase-command.coffee index a79b1969b..2096a91c9 100644 --- a/src/extensions/uppercase-command.coffee +++ b/src/extensions/uppercase-command.coffee @@ -3,9 +3,7 @@ EditorCommand = require 'editor-command' module.exports = class UpperCaseCommand extends EditorCommand - @getKeymaps: (editor) -> - 'meta-X': 'uppercase' - - @execute: (editor, event) -> - @replaceSelectedText editor, (text) -> - text.toUpperCase() + @onEditor: (editor) -> + @register editor, 'meta-X', 'uppercase', => + @replaceSelectedText editor, (text) -> + text.toUpperCase() From d4aeb1bb95ede61b91c1cfa46306ca01b0d90444 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 10 Oct 2012 18:41:12 -0700 Subject: [PATCH 07/11] Move EditorCommand helpers elsewhere RootView and Editor now have helpers that support binding events to callbacks, binding a callback to all current and future editors, and replacing the selected text via a transforming callback. --- spec/app/editor-spec.coffee | 52 +++++++ spec/app/root-view-spec.coffee | 28 ++++ spec/extensions/editor-command-spec.coffee | 140 ------------------ spec/extensions/lowercase-command-spec.coffee | 23 +++ spec/extensions/uppercase-command-spec.coffee | 23 +++ src/app/editor.coffee | 17 +++ src/app/root-view.coffee | 7 + src/extensions/editor-command.coffee | 26 ---- src/extensions/lowercase-command.coffee | 11 +- src/extensions/uppercase-command.coffee | 11 +- 10 files changed, 162 insertions(+), 176 deletions(-) delete mode 100644 spec/extensions/editor-command-spec.coffee create mode 100644 spec/extensions/lowercase-command-spec.coffee create mode 100644 spec/extensions/uppercase-command-spec.coffee delete mode 100644 src/extensions/editor-command.coffee diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 93fdc36a7..58643bb91 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -1828,3 +1828,55 @@ describe "Editor", -> editor.pageUp() expect(editor.getCursor().getScreenPosition().row).toBe(0) expect(editor.getFirstVisibleScreenRow()).toBe(0) + + describe ".replaceSelectedText()", -> + it "doesn't call the replace function when the selection is empty", -> + replaced = false + edited = false + replacer = (text) -> + replaced = true + 'new' + + editor.moveCursorToTop() + edited = editor.replaceSelectedText(replacer) + expect(replaced).toBe false + expect(edited).toBe false + + it "returns true when transformed text is non-empty", -> + replaced = false + edited = false + replacer = (text) -> + replaced = true + 'new' + + editor.moveCursorToTop() + editor.selectToEndOfLine() + edited = editor.replaceSelectedText(replacer) + expect(replaced).toBe true + expect(edited).toBe true + + it "returns false when transformed text is null", -> + replaced = false + edited = false + replacer = (text) -> + replaced = true + null + + editor.moveCursorToTop() + editor.selectToEndOfLine() + edited = editor.replaceSelectedText(replacer) + expect(replaced).toBe true + expect(edited).toBe false + + it "returns false when transformed text is undefined", -> + replaced = false + edited = false + replacer = (text) -> + replaced = true + undefined + + editor.moveCursorToTop() + editor.selectToEndOfLine() + edited = editor.replaceSelectedText(replacer) + expect(replaced).toBe true + expect(edited).toBe false diff --git a/spec/app/root-view-spec.coffee b/spec/app/root-view-spec.coffee index 8e1c2ca81..3b56766a5 100644 --- a/spec/app/root-view-spec.coffee +++ b/spec/app/root-view-spec.coffee @@ -690,3 +690,31 @@ describe "RootView", -> expect(fs.read(buffer1.getPath())).toBe("edited1") expect(buffer2.isModified()).toBe(false) expect(fs.read(buffer2.getPath())).toBe("edited2") + + fdescribe ".eachEditor(callback)", -> + beforeEach -> + rootView.attachToDom() + + it "invokes the callback for existing editor", -> + count = 0 + callbackEditor = null + callback = (editor) -> + callbackEditor = editor + count++ + rootView.eachEditor(callback) + expect(count).toBe 1 + expect(callbackEditor).toBe rootView.getActiveEditor() + + it "invokes the callback for new editor", -> + count = 0 + callbackEditor = null + callback = (editor) -> + callbackEditor = editor + count++ + + rootView.eachEditor(callback) + count = 0 + callbackEditor = null + rootView.getActiveEditor().splitRight() + expect(count).toBe 1 + expect(callbackEditor).toBe rootView.getActiveEditor() diff --git a/spec/extensions/editor-command-spec.coffee b/spec/extensions/editor-command-spec.coffee deleted file mode 100644 index 96b2f08ac..000000000 --- a/spec/extensions/editor-command-spec.coffee +++ /dev/null @@ -1,140 +0,0 @@ -EditorCommand = require 'editor-command' -LowerCaseCommand = require 'lowercase-command' -UpperCaseCommand = require 'uppercase-command' -RootView = require 'root-view' -fs = require 'fs' - -describe "EditorCommand", -> - [rootView, editor, path] = [] - - beforeEach -> - rootView = new RootView - rootView.open(require.resolve 'fixtures/sample.js') - - rootView.focus() - editor = rootView.getActiveEditor() - - afterEach -> - rootView.remove() - - describe "@replaceSelectedText()", -> - it "returns true when transformed text is non-empty", -> - transformed = false - edited = false - class CustomCommand extends EditorCommand - - @onEditor: (editor) -> - @register editor, 'meta-V', 'custom', => - edited = @replaceSelectedText editor, (text) -> - transformed = true - 'new' - - CustomCommand.activate(rootView) - editor.moveCursorToTop() - editor.selectToEndOfLine() - editor.trigger 'custom' - expect(transformed).toBe true - expect(edited).toBe true - - it "returns false when transformed text is null", -> - transformed = false - edited = false - class CustomCommand extends EditorCommand - - @onEditor: (editor) -> - @register editor, 'meta-V', 'custom', => - edited = @replaceSelectedText editor, (text) -> - transformed = true - null - - CustomCommand.activate(rootView) - editor.moveCursorToTop() - editor.selectToEndOfLine() - editor.trigger 'custom' - expect(transformed).toBe true - expect(edited).toBe false - - it "returns false when transformed text is undefined", -> - transformed = false - edited = false - class CustomCommand extends EditorCommand - - @onEditor: (editor) -> - @register editor, 'meta-V', 'custom', => - edited = @replaceSelectedText editor, (text) -> - transformed = true - undefined - - CustomCommand.activate(rootView) - editor.moveCursorToTop() - editor.selectToEndOfLine() - editor.trigger 'custom' - expect(transformed).toBe true - expect(edited).toBe false - - describe "custom sub-class", -> - it "removes vowels from selected text", -> - class VowelRemover extends EditorCommand - - @onEditor: (editor) -> - @register editor, 'meta-V', 'devowel', => - @replaceSelectedText editor, (text) -> - text.replace(/[aeiouy]/gi, '') - - VowelRemover.activate(rootView) - editor.moveCursorToTop() - editor.selectToEndOfLine() - editor.trigger 'devowel' - expect(editor.lineForBufferRow(0)).toBe 'vr qcksrt = fnctn () {' - expect(editor.getTextInRange(editor.getSelection().getBufferRange())).toBe 'vr qcksrt = fnctn () {' - expect(editor.getCursorBufferPosition()).toBe(editor.getSelection().getBufferRange().end) - - it "maintains reversed selections", -> - class VowelRemover extends EditorCommand - @onEditor: (editor) -> - @register editor, 'meta-V', 'devowel', => - @replaceSelectedText editor, (text) -> - text.replace(/[aeiouy]/gi, '') - - VowelRemover.activate(rootView) - editor.moveCursorToTop() - editor.moveCursorToEndOfLine() - editor.selectToBeginningOfLine() - editor.trigger 'devowel' - expect(editor.lineForBufferRow(0)).toBe 'vr qcksrt = fnctn () {' - expect(editor.getTextInRange(editor.getSelection().getBufferRange())).toBe 'vr qcksrt = fnctn () {' - expect(editor.getCursorBufferPosition()).toBe(editor.getSelection().getBufferRange().start) - - it "doesn't transform empty selections", -> - callbackCount = 0 - class CustomCommand extends EditorCommand - @onEditor: (editor) -> - @register editor, 'meta-V', 'custom', => - @replaceSelectedText editor, (text) -> - callbackCount++ - text - - CustomCommand.activate(rootView) - editor.moveCursorToTop() - editor.selectToEndOfLine() - editor.trigger 'custom' - expect(callbackCount).toBe 1 - editor.clearSelections() - editor.trigger 'custom' - expect(callbackCount).toBe 1 - - describe "LowerCaseCommand", -> - it "replaces the selected text with all lower case characters", -> - LowerCaseCommand.activate(rootView) - editor.setSelectedBufferRange([[11,14], [11,19]]) - expect(editor.getTextInRange(editor.getSelection().getBufferRange())).toBe 'Array' - editor.trigger 'lowercase' - expect(editor.getTextInRange(editor.getSelection().getBufferRange())).toBe 'array' - - describe "UpperCaseCommand", -> - it "replaces the selected text with all upper case characters", -> - UpperCaseCommand.activate(rootView) - editor.setSelectedBufferRange([[0,0], [0,3]]) - expect(editor.getTextInRange(editor.getSelection().getBufferRange())).toBe 'var' - editor.trigger 'uppercase' - expect(editor.getTextInRange(editor.getSelection().getBufferRange())).toBe 'VAR' diff --git a/spec/extensions/lowercase-command-spec.coffee b/spec/extensions/lowercase-command-spec.coffee new file mode 100644 index 000000000..120120f2d --- /dev/null +++ b/spec/extensions/lowercase-command-spec.coffee @@ -0,0 +1,23 @@ +LowerCaseCommand = require 'lowercase-command' +RootView = require 'root-view' +fs = require 'fs' + +describe "LowerCaseCommand", -> + [rootView, editor, path] = [] + + beforeEach -> + rootView = new RootView + rootView.open(require.resolve 'fixtures/sample.js') + + rootView.focus() + editor = rootView.getActiveEditor() + + afterEach -> + rootView.remove() + + it "replaces the selected text with all lower case characters", -> + LowerCaseCommand.activate(rootView) + editor.setSelectedBufferRange([[11,14], [11,19]]) + expect(editor.getTextInRange(editor.getSelection().getBufferRange())).toBe 'Array' + editor.trigger 'lowercase' + expect(editor.getTextInRange(editor.getSelection().getBufferRange())).toBe 'array' diff --git a/spec/extensions/uppercase-command-spec.coffee b/spec/extensions/uppercase-command-spec.coffee new file mode 100644 index 000000000..5ad77077e --- /dev/null +++ b/spec/extensions/uppercase-command-spec.coffee @@ -0,0 +1,23 @@ +UpperCaseCommand = require 'uppercase-command' +RootView = require 'root-view' +fs = require 'fs' + +describe "UpperCaseCommand", -> + [rootView, editor, path] = [] + + beforeEach -> + rootView = new RootView + rootView.open(require.resolve 'fixtures/sample.js') + + rootView.focus() + editor = rootView.getActiveEditor() + + afterEach -> + rootView.remove() + + it "replaces the selected text with all upper case characters", -> + UpperCaseCommand.activate(rootView) + editor.setSelectedBufferRange([[0,0], [0,3]]) + expect(editor.getTextInRange(editor.getSelection().getBufferRange())).toBe 'var' + editor.trigger 'uppercase' + expect(editor.getTextInRange(editor.getSelection().getBufferRange())).toBe 'VAR' diff --git a/src/app/editor.coffee b/src/app/editor.coffee index 41c69752d..35b752ad3 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -965,3 +965,20 @@ class Editor extends View @find('pre.line.cursor-line').removeClass('cursor-line') if @getSelection().isSingleScreenLine() @find("pre.line:eq(#{screenRow})").addClass('cursor-line') + + bindToKeyedEvent: (key, event, callback) -> + binding = {} + binding[key] = event + window.keymap.bindKeys '.editor', binding + @on event, => + callback(this, event) + + replaceSelectedText: (replaceFn) -> + selection = @getSelection() + return false if selection.isEmpty() + + text = replaceFn(@getTextInRange(selection.getBufferRange())) + return false if text is null or text is undefined + + @insertText(text, select: true) + true diff --git a/src/app/root-view.coffee b/src/app/root-view.coffee index 38cb4f1e9..4b9479329 100644 --- a/src/app/root-view.coffee +++ b/src/app/root-view.coffee @@ -232,3 +232,10 @@ class RootView extends View saveAll: -> editor.save() for editor in @getEditors() + + eachEditor: (callback) -> + for editor in @getEditors() + callback(editor) + + @on 'editor-open', (e, editor) -> + callback(editor) diff --git a/src/extensions/editor-command.coffee b/src/extensions/editor-command.coffee deleted file mode 100644 index e4a649d07..000000000 --- a/src/extensions/editor-command.coffee +++ /dev/null @@ -1,26 +0,0 @@ -module.exports = -class EditorCommand - - @activate: (rootView) -> - for editor in rootView.getEditors() - @onEditor(editor) - - rootView.on 'editor-open', (e, editor) => - @onEditor(editor) - - @register: (editor, key, event, callback) -> - binding = {} - binding[key] = event - window.keymap.bindKeys '.editor', binding - editor.on event, => - callback(editor, event) - - @replaceSelectedText: (editor, replace) -> - selection = editor.getSelection() - return false if selection.isEmpty() - - text = replace(editor.getTextInRange(selection.getBufferRange())) - return false if text is null or text is undefined - - editor.insertText(text, select: true) - true diff --git a/src/extensions/lowercase-command.coffee b/src/extensions/lowercase-command.coffee index 7254fb72d..e9fa2c3f4 100644 --- a/src/extensions/lowercase-command.coffee +++ b/src/extensions/lowercase-command.coffee @@ -1,9 +1,10 @@ -EditorCommand = require 'editor-command' - module.exports = -class LowerCaseCommand extends EditorCommand +class LowerCaseCommand + + @activate: (rootView) -> + rootView.eachEditor(@onEditor) @onEditor: (editor) -> - @register editor, 'meta-Y', 'lowercase', => - @replaceSelectedText editor, (text) -> + editor.bindToKeyedEvent 'meta-Y', 'lowercase', => + editor.replaceSelectedText (text) -> text.toLowerCase() diff --git a/src/extensions/uppercase-command.coffee b/src/extensions/uppercase-command.coffee index 2096a91c9..840ee848b 100644 --- a/src/extensions/uppercase-command.coffee +++ b/src/extensions/uppercase-command.coffee @@ -1,9 +1,10 @@ -EditorCommand = require 'editor-command' - module.exports = -class UpperCaseCommand extends EditorCommand +class UpperCaseCommand + + @activate: (rootView) -> + rootView.eachEditor(@onEditor) @onEditor: (editor) -> - @register editor, 'meta-X', 'uppercase', => - @replaceSelectedText editor, (text) -> + editor.bindToKeyedEvent 'meta-X', 'uppercase', => + editor.replaceSelectedText (text) -> text.toUpperCase() From e2c7bca3cc4991917930a1fd82a96d15bf8b0b4d Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 10 Oct 2012 18:43:56 -0700 Subject: [PATCH 08/11] De-f describe --- spec/app/root-view-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/root-view-spec.coffee b/spec/app/root-view-spec.coffee index 3b56766a5..bc0d1ec14 100644 --- a/spec/app/root-view-spec.coffee +++ b/spec/app/root-view-spec.coffee @@ -691,7 +691,7 @@ describe "RootView", -> expect(buffer2.isModified()).toBe(false) expect(fs.read(buffer2.getPath())).toBe("edited2") - fdescribe ".eachEditor(callback)", -> + describe ".eachEditor(callback)", -> beforeEach -> rootView.attachToDom() From 50b1814308211f00218c91ca5e112798c18c850e Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 10 Oct 2012 18:45:36 -0700 Subject: [PATCH 09/11] Change => to -> --- src/extensions/lowercase-command.coffee | 2 +- src/extensions/uppercase-command.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/extensions/lowercase-command.coffee b/src/extensions/lowercase-command.coffee index e9fa2c3f4..1b822b1bc 100644 --- a/src/extensions/lowercase-command.coffee +++ b/src/extensions/lowercase-command.coffee @@ -5,6 +5,6 @@ class LowerCaseCommand rootView.eachEditor(@onEditor) @onEditor: (editor) -> - editor.bindToKeyedEvent 'meta-Y', 'lowercase', => + editor.bindToKeyedEvent 'meta-Y', 'lowercase', -> editor.replaceSelectedText (text) -> text.toLowerCase() diff --git a/src/extensions/uppercase-command.coffee b/src/extensions/uppercase-command.coffee index 840ee848b..7d3872a29 100644 --- a/src/extensions/uppercase-command.coffee +++ b/src/extensions/uppercase-command.coffee @@ -5,6 +5,6 @@ class UpperCaseCommand rootView.eachEditor(@onEditor) @onEditor: (editor) -> - editor.bindToKeyedEvent 'meta-X', 'uppercase', => + editor.bindToKeyedEvent 'meta-X', 'uppercase', -> editor.replaceSelectedText (text) -> text.toUpperCase() From 24777da703b1df165fa7b1060214f8e6dffe33d7 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 10 Oct 2012 19:16:57 -0700 Subject: [PATCH 10/11] Remove unneeded onEditor method --- src/extensions/lowercase-command.coffee | 10 ++++------ src/extensions/uppercase-command.coffee | 10 ++++------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/extensions/lowercase-command.coffee b/src/extensions/lowercase-command.coffee index 1b822b1bc..00f04158a 100644 --- a/src/extensions/lowercase-command.coffee +++ b/src/extensions/lowercase-command.coffee @@ -2,9 +2,7 @@ module.exports = class LowerCaseCommand @activate: (rootView) -> - rootView.eachEditor(@onEditor) - - @onEditor: (editor) -> - editor.bindToKeyedEvent 'meta-Y', 'lowercase', -> - editor.replaceSelectedText (text) -> - text.toLowerCase() + rootView.eachEditor (editor) -> + editor.bindToKeyedEvent 'meta-Y', 'lowercase', -> + editor.replaceSelectedText (text) -> + text.toLowerCase() diff --git a/src/extensions/uppercase-command.coffee b/src/extensions/uppercase-command.coffee index 7d3872a29..c7cc02222 100644 --- a/src/extensions/uppercase-command.coffee +++ b/src/extensions/uppercase-command.coffee @@ -2,9 +2,7 @@ module.exports = class UpperCaseCommand @activate: (rootView) -> - rootView.eachEditor(@onEditor) - - @onEditor: (editor) -> - editor.bindToKeyedEvent 'meta-X', 'uppercase', -> - editor.replaceSelectedText (text) -> - text.toUpperCase() + rootView.eachEditor (editor) -> + editor.bindToKeyedEvent 'meta-X', 'uppercase', -> + editor.replaceSelectedText (text) -> + text.toUpperCase() From 220044c8bde971afd51f36de4e237f8a7e9ccba1 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 12 Oct 2012 09:16:46 -0700 Subject: [PATCH 11/11] Add eachBuffer helper to RootView This allows extensions to bind a callback to all current and future buffers. --- spec/app/root-view-spec.coffee | 28 +++++++++++++++++++ src/app/root-view.coffee | 7 +++++ .../strip-trailing-whitespace.coffee | 14 +++------- 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/spec/app/root-view-spec.coffee b/spec/app/root-view-spec.coffee index bc0d1ec14..6cb214e3f 100644 --- a/spec/app/root-view-spec.coffee +++ b/spec/app/root-view-spec.coffee @@ -718,3 +718,31 @@ describe "RootView", -> rootView.getActiveEditor().splitRight() expect(count).toBe 1 expect(callbackEditor).toBe rootView.getActiveEditor() + + describe ".eachBuffer(callback)", -> + beforeEach -> + rootView.attachToDom() + + it "invokes the callback for existing buffer", -> + count = 0 + callbackBuffer = null + callback = (buffer) -> + callbackBuffer = buffer + count++ + rootView.eachBuffer(callback) + expect(count).toBe 1 + expect(callbackBuffer).toBe rootView.getActiveEditor().getBuffer() + + it "invokes the callback for new buffer", -> + count = 0 + callbackBuffer = null + callback = (buffer) -> + callbackBuffer = buffer + count++ + + rootView.eachBuffer(callback) + count = 0 + callbackBuffer = null + rootView.open(require.resolve('fixtures/sample.txt')) + expect(count).toBe 1 + expect(callbackBuffer).toBe rootView.getActiveEditor().getBuffer() diff --git a/src/app/root-view.coffee b/src/app/root-view.coffee index 4b9479329..32a29702c 100644 --- a/src/app/root-view.coffee +++ b/src/app/root-view.coffee @@ -239,3 +239,10 @@ class RootView extends View @on 'editor-open', (e, editor) -> callback(editor) + + eachBuffer: (callback) -> + for buffer in @project.getBuffers() + callback(buffer) + + @project.on 'new-buffer', (buffer) -> + callback(buffer) diff --git a/src/extensions/strip-trailing-whitespace.coffee b/src/extensions/strip-trailing-whitespace.coffee index d33eafe87..cb27068c0 100644 --- a/src/extensions/strip-trailing-whitespace.coffee +++ b/src/extensions/strip-trailing-whitespace.coffee @@ -2,13 +2,7 @@ module.exports = name: "strip trailing whitespace" activate: (rootView) -> - for buffer in rootView.project.getBuffers() - @stripTrailingWhitespaceBeforeSave(buffer) - - rootView.project.on 'new-buffer', (buffer) => - @stripTrailingWhitespaceBeforeSave(buffer) - - stripTrailingWhitespaceBeforeSave: (buffer) -> - buffer.on 'before-save', -> - buffer.scan /[ \t]+$/g, (match, range, { replace }) -> - replace('') + rootView.eachBuffer (buffer) -> + buffer.on 'before-save', -> + buffer.scan /[ \t]+$/g, (match, range, { replace }) -> + replace('')