diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index ec4337d76..ad79d4e18 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -2136,3 +2136,55 @@ describe "Editor", -> expect(editor.reloadGrammar()).toBeFalsy() expect(editor.updateDisplay).not.toHaveBeenCalled() expect(editor.getGrammar().name).toBe 'JavaScript' + + 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 e7660f854..baad07109 100644 --- a/spec/app/root-view-spec.coffee +++ b/spec/app/root-view-spec.coffee @@ -710,3 +710,59 @@ describe "RootView", -> lowerRightEditor = rightEditor.splitDown() expect(lowerRightEditor.find(".line:first").text()).toBe " " + + describe ".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() + + 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/editor.coffee b/src/app/editor.coffee index 90d473597..5a67ef28e 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -1127,3 +1127,20 @@ class Editor extends View @clearRenderedLines() @updateDisplay() grammarChanged + + 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 9184d49f1..c62bc7c90 100644 --- a/src/app/root-view.coffee +++ b/src/app/root-view.coffee @@ -258,3 +258,17 @@ class RootView extends View saveAll: -> editor.save() for editor in @getEditors() + + eachEditor: (callback) -> + for editor in @getEditors() + callback(editor) + + @on 'editor:attached', (e, editor) -> + callback(editor) + + eachBuffer: (callback) -> + for buffer in @project.getBuffers() + callback(buffer) + + @project.on 'buffer-created', (buffer) -> + callback(buffer) diff --git a/src/app/selection.coffee b/src/app/selection.coffee index b3aaf9bed..e7f7c23db 100644 --- a/src/app/selection.coffee +++ b/src/app/selection.coffee @@ -174,7 +174,10 @@ class Selection text = @normalizeIndent(text, options) if options.normalizeIndent @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 if @editSession.autoIndent and options.autoIndent if text == '\n' diff --git a/src/extensions/strip-trailing-whitespace.coffee b/src/extensions/strip-trailing-whitespace.coffee new file mode 100644 index 000000000..cb27068c0 --- /dev/null +++ b/src/extensions/strip-trailing-whitespace.coffee @@ -0,0 +1,8 @@ +module.exports = + name: "strip trailing whitespace" + + activate: (rootView) -> + rootView.eachBuffer (buffer) -> + buffer.on 'before-save', -> + buffer.scan /[ \t]+$/g, (match, range, { replace }) -> + replace('') diff --git a/src/packages/lowercase-command/index.coffee b/src/packages/lowercase-command/index.coffee new file mode 100644 index 000000000..18f22efe6 --- /dev/null +++ b/src/packages/lowercase-command/index.coffee @@ -0,0 +1 @@ +module.exports = require "./src/lowercase-command" diff --git a/src/packages/lowercase-command/spec/lowercase-command-spec.coffee b/src/packages/lowercase-command/spec/lowercase-command-spec.coffee new file mode 100644 index 000000000..120120f2d --- /dev/null +++ b/src/packages/lowercase-command/spec/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/src/packages/lowercase-command/src/lowercase-command.coffee b/src/packages/lowercase-command/src/lowercase-command.coffee new file mode 100644 index 000000000..00f04158a --- /dev/null +++ b/src/packages/lowercase-command/src/lowercase-command.coffee @@ -0,0 +1,8 @@ +module.exports = +class LowerCaseCommand + + @activate: (rootView) -> + rootView.eachEditor (editor) -> + editor.bindToKeyedEvent 'meta-Y', 'lowercase', -> + editor.replaceSelectedText (text) -> + text.toLowerCase() diff --git a/src/packages/strip-trailing-whitespace/src/strip-trailing-whitespace.coffee b/src/packages/strip-trailing-whitespace/src/strip-trailing-whitespace.coffee index f0f784c1b..661a22986 100644 --- a/src/packages/strip-trailing-whitespace/src/strip-trailing-whitespace.coffee +++ b/src/packages/strip-trailing-whitespace/src/strip-trailing-whitespace.coffee @@ -2,11 +2,7 @@ module.exports = name: "strip trailing whitespace" activate: (rootView) -> - for buffer in rootView.project.getBuffers() - @stripTrailingWhitespaceBeforeSave(buffer) - - rootView.project.on 'buffer-created', (buffer) => - @stripTrailingWhitespaceBeforeSave(buffer) + rootView.eachBuffer (buffer) => @stripTrailingWhitespaceBeforeSave(buffer) stripTrailingWhitespaceBeforeSave: (buffer) -> buffer.on 'will-be-saved', -> diff --git a/src/packages/uppercase-command/index.coffee b/src/packages/uppercase-command/index.coffee new file mode 100644 index 000000000..55beed3cb --- /dev/null +++ b/src/packages/uppercase-command/index.coffee @@ -0,0 +1 @@ +module.exports = require "./src/uppercase-command" diff --git a/src/packages/uppercase-command/spec/uppercase-command-spec.coffee b/src/packages/uppercase-command/spec/uppercase-command-spec.coffee new file mode 100644 index 000000000..5ad77077e --- /dev/null +++ b/src/packages/uppercase-command/spec/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/packages/uppercase-command/src/uppercase-command.coffee b/src/packages/uppercase-command/src/uppercase-command.coffee new file mode 100644 index 000000000..c7cc02222 --- /dev/null +++ b/src/packages/uppercase-command/src/uppercase-command.coffee @@ -0,0 +1,8 @@ +module.exports = +class UpperCaseCommand + + @activate: (rootView) -> + rootView.eachEditor (editor) -> + editor.bindToKeyedEvent 'meta-X', 'uppercase', -> + editor.replaceSelectedText (text) -> + text.toUpperCase()