diff --git a/package.json b/package.json index 0bd8c92a2..6f0598b16 100644 --- a/package.json +++ b/package.json @@ -72,9 +72,9 @@ "archive-view": "0.32.0", "autocomplete": "0.28.0", "autoflow": "0.17.0", - "autosave": "0.13.0", + "autosave": "0.14.0", "background-tips": "0.14.0", - "bookmarks": "0.23.0", + "bookmarks": "0.24.0", "bracket-matcher": "0.45.0", "command-palette": "0.22.0", "deprecation-cop": "0.6.0", @@ -83,7 +83,7 @@ "feedback": "0.33.0", "find-and-replace": "0.115.0", "fuzzy-finder": "0.54.0", - "git-diff": "0.29.0", + "git-diff": "0.30.0", "go-to-line": "0.22.0", "grammar-selector": "0.27.0", "image-view": "0.35.0", @@ -96,7 +96,7 @@ "release-notes": "0.32.0", "settings-view": "0.120.0", "snippets": "0.45.0", - "spell-check": "0.36.0", + "spell-check": "0.37.0", "status-bar": "0.40.0", "styleguide": "0.29.0", "symbols-view": "0.55.0", diff --git a/spec/editor-component-spec.coffee b/spec/editor-component-spec.coffee index b9cd3e3a0..b827622c1 100644 --- a/spec/editor-component-spec.coffee +++ b/spec/editor-component-spec.coffee @@ -1072,8 +1072,6 @@ describe "EditorComponent", -> expect(horizontalScrollbarNode.scrollWidth).toBe gutterNode.offsetWidth + editor.getScrollWidth() - describe "when a mousewheel event occurs on the editor", -> - describe "mousewheel events", -> it "updates the scrollLeft or scrollTop on mousewheel events depending on which delta is greater (x or y)", -> node.style.height = 4.5 * lineHeightInPixels + 'px' @@ -1184,6 +1182,12 @@ describe "EditorComponent", -> inputNode.dispatchEvent(new Event('input')) expect(editor.lineForBufferRow(0)).toBe 'üvar quicksort = function () {' + it "does not handle input events when input is disabled", -> + component.setInputEnabled(false) + inputNode.value = 'x' + inputNode.dispatchEvent(new Event('input')) + expect(editor.lineForBufferRow(0)).toBe 'var quicksort = function () {' + describe "commands", -> describe "editor:consolidate-selections", -> it "consolidates selections on the editor model, aborting the key binding if there is only one selection", -> diff --git a/spec/editor-view-spec.coffee b/spec/editor-view-spec.coffee index 990ca47a7..c6d043d64 100644 --- a/spec/editor-view-spec.coffee +++ b/spec/editor-view-spec.coffee @@ -10,6 +10,8 @@ describe "EditorView", -> [buffer, editorView, editor, cachedEditor, cachedLineHeight, cachedCharWidth, fart] = [] beforeEach -> + atom.config.set 'core.useReactEditor', false + waitsForPromise -> atom.workspace.open('sample.js').then (o) -> editor = o diff --git a/spec/pane-container-spec.coffee b/spec/pane-container-spec.coffee index 44f99a6b2..df9921e69 100644 --- a/spec/pane-container-spec.coffee +++ b/spec/pane-container-spec.coffee @@ -16,6 +16,7 @@ describe "PaneContainer", -> containerA = new PaneContainer(root: pane1A) pane2A = pane1A.splitRight(items: [new Item]) pane3A = pane2A.splitDown(items: [new Item]) + pane3A.focus() it "preserves the focused pane across serialization", -> expect(pane3A.focused).toBe true diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 7ff32a083..04b1c2474 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -439,10 +439,11 @@ describe "Pane", -> expect(column.orientation).toBe 'vertical' expect(column.children).toEqual [pane1, pane3, pane2] - it "sets up the new pane to be focused", -> - expect(pane1.focused).toBe false + it "activates the new pane", -> + expect(pane1.isActive()).toBe true pane2 = pane1.splitRight() - expect(pane2.focused).toBe true + expect(pane1.isActive()).toBe false + expect(pane2.isActive()).toBe true describe "::destroy()", -> [container, pane1, pane2] = [] diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index 8d3d3be27..9f73884b5 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -199,6 +199,7 @@ describe "PaneView", -> it "focuses the next pane", -> container.attachToDom() + pane2.activate() expect(pane.hasFocus()).toBe false expect(pane2.hasFocus()).toBe true pane2Model.destroy() diff --git a/spec/spec-helper.coffee b/spec/spec-helper.coffee index fcb052962..bf00f93a3 100644 --- a/spec/spec-helper.coffee +++ b/spec/spec-helper.coffee @@ -89,7 +89,7 @@ beforeEach -> config.set "editor.autoIndent", false config.set "core.disabledPackages", ["package-that-throws-an-exception", "package-with-broken-package-json", "package-with-broken-keymap"] - config.set "core.useReactEditor", false + config.set "core.useReactEditor", true config.save.reset() atom.config = config diff --git a/spec/workspace-view-spec.coffee b/spec/workspace-view-spec.coffee index e2b2dada9..3f7a32259 100644 --- a/spec/workspace-view-spec.coffee +++ b/spec/workspace-view-spec.coffee @@ -201,7 +201,9 @@ describe "WorkspaceView", -> expect(rightEditorView.find(".line:first").text()).toBe " " expect(leftEditorView.find(".line:first").text()).toBe " " - withInvisiblesShowing = "#{rightEditorView.invisibles.space}#{rightEditorView.invisibles.tab} #{rightEditorView.invisibles.space}#{rightEditorView.invisibles.eol}" + {invisibles} = rightEditorView.component.state + {space, tab, eol} = invisibles + withInvisiblesShowing = "#{space}#{tab} #{space}#{eol}" atom.workspaceView.trigger "window:toggle-invisibles" expect(rightEditorView.find(".line:first").text()).toBe withInvisiblesShowing diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 3ef72d8e8..0a128fb41 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -230,8 +230,7 @@ class DisplayBuffer extends Model @charWidthsByScope = {} getScrollHeight: -> - unless @getLineHeightInPixels() > 0 - throw new Error("You must assign lineHeightInPixels before calling ::getScrollHeight()") + return 0 unless @getLineHeightInPixels() > 0 @getLineCount() * @getLineHeightInPixels() @@ -239,8 +238,7 @@ class DisplayBuffer extends Model (@getMaxLineLength() * @getDefaultCharWidth()) + @getCursorWidth() getVisibleRowRange: -> - unless @getLineHeightInPixels() > 0 - throw new Error("You must assign a non-zero lineHeightInPixels before calling ::getVisibleRowRange()") + return [0, 0] unless @getLineHeightInPixels() > 0 heightInLines = Math.ceil(@getHeight() / @getLineHeightInPixels()) + 1 startRow = Math.floor(@getScrollTop() / @getLineHeightInPixels()) diff --git a/src/editor-component.coffee b/src/editor-component.coffee index b46a1dc5e..f682b878f 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -37,6 +37,7 @@ EditorComponent = React.createClass overflowChangedEventsPaused: false overflowChangedWhilePaused: false measureLineHeightAndDefaultCharWidthWhenShown: false + inputEnabled: true render: -> {focused, fontSize, lineHeight, fontFamily, showIndentGuide, showInvisibles, visible} = @state @@ -165,7 +166,9 @@ EditorComponent = React.createClass window.removeEventListener('resize', @onWindowResize) componentWillUpdate: -> - @props.parentView.trigger 'cursor:moved' if @cursorsMoved + if @props.editor.isAlive() + @props.parentView.trigger 'cursor:moved' if @cursorsMoved + @props.parentView.trigger 'selection:changed' if @selectionChanged componentDidUpdate: (prevProps, prevState) -> @pendingChanges.length = 0 @@ -457,6 +460,8 @@ EditorComponent = React.createClass scrollViewNode.scrollLeft = 0 onInput: (char, replaceLastCharacter) -> + return unless @inputEnabled + {editor} = @props if replaceLastCharacter @@ -531,6 +536,7 @@ EditorComponent = React.createClass onCursorsMoved: -> @cursorsMoved = true + @requestUpdate() onDecorationChanged: -> @decorationChangedImmediate ?= setImmediate => @@ -707,50 +713,21 @@ EditorComponent = React.createClass show: -> @setState(visible: true) - runScrollBenchmark: -> - unless process.env.NODE_ENV is 'production' - ReactPerf = require 'react-atom-fork/lib/ReactDefaultPerf' - ReactPerf.start() - - node = @getDOMNode() - - scroll = (delta, done) -> - dispatchMouseWheelEvent = -> - node.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: -0, wheelDeltaY: -delta)) - - stopScrolling = -> - clearInterval(interval) - done?() - - interval = setInterval(dispatchMouseWheelEvent, 10) - setTimeout(stopScrolling, 500) - - console.timeline('scroll') - scroll 50, -> - scroll 100, -> - scroll 200, -> - scroll 400, -> - scroll 800, -> - scroll 1600, -> - console.timelineEnd('scroll') - unless process.env.NODE_ENV is 'production' - ReactPerf.stop() - console.log "Inclusive" - ReactPerf.printInclusive() - console.log "Exclusive" - ReactPerf.printExclusive() - console.log "Wasted" - ReactPerf.printWasted() + getFontSize: -> + @state.fontSize setFontSize: (fontSize) -> @setState({fontSize}) - setLineHeight: (lineHeight) -> - @setState({lineHeight}) + getFontFamily: -> + @state.fontFamily setFontFamily: (fontFamily) -> @setState({fontFamily}) + setLineHeight: (lineHeight) -> + @setState({lineHeight}) + setShowIndentGuide: (showIndentGuide) -> @setState({showIndentGuide}) @@ -786,6 +763,48 @@ EditorComponent = React.createClass left = clientX - scrollViewClientRect.left + editor.getScrollLeft() {top, left} + getModel: -> + @props.editor + + isInputEnabled: -> @inputEnabled + + setInputEnabled: (@inputEnabled) -> @inputEnabled + updateParentViewFocusedClassIfNeeded: (prevState) -> if prevState.focused isnt @state.focused @props.parentView.toggleClass('is-focused', @props.focused) + + runScrollBenchmark: -> + unless process.env.NODE_ENV is 'production' + ReactPerf = require 'react-atom-fork/lib/ReactDefaultPerf' + ReactPerf.start() + + node = @getDOMNode() + + scroll = (delta, done) -> + dispatchMouseWheelEvent = -> + node.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: -0, wheelDeltaY: -delta)) + + stopScrolling = -> + clearInterval(interval) + done?() + + interval = setInterval(dispatchMouseWheelEvent, 10) + setTimeout(stopScrolling, 500) + + console.timeline('scroll') + scroll 50, -> + scroll 100, -> + scroll 200, -> + scroll 400, -> + scroll 800, -> + scroll 1600, -> + console.timelineEnd('scroll') + unless process.env.NODE_ENV is 'production' + ReactPerf.stop() + console.log "Inclusive" + ReactPerf.printInclusive() + console.log "Exclusive" + ReactPerf.printExclusive() + console.log "Wasted" + ReactPerf.printWasted() diff --git a/src/editor-view.coffee b/src/editor-view.coffee index 564a439ba..f4a5b0fb7 100644 --- a/src/editor-view.coffee +++ b/src/editor-view.coffee @@ -282,7 +282,7 @@ class EditorView extends View @editor.moveCursorUp(@getPageRows()) @scrollTop(newScrollTop, adjustVerticalScrollbar: true) - # Public: Gets the number of actual page rows existing in an editor. + # Gets the number of actual page rows existing in an editor. # # Returns a {Number}. getPageRows: -> diff --git a/src/gutter-component.coffee b/src/gutter-component.coffee index a4c249b58..c668f0b49 100644 --- a/src/gutter-component.coffee +++ b/src/gutter-component.coffee @@ -62,7 +62,7 @@ GutterComponent = React.createClass # since the real line numbers are absolutely positioned for performance reasons. appendDummyLineNumber: -> {maxLineNumberDigits} = @props - WrapperDiv.innerHTML = @buildLineNumberHTML(0, false, maxLineNumberDigits) + WrapperDiv.innerHTML = @buildLineNumberHTML(-1, false, maxLineNumberDigits) @dummyLineNumberNode = WrapperDiv.children[0] @refs.lineNumbers.getDOMNode().appendChild(@dummyLineNumberNode) @@ -140,7 +140,7 @@ GutterComponent = React.createClass if decorations? for decoration in decorations classes += decoration.class + ' ' if not softWrapped or softWrapped and decoration.softWrap - classes += 'line-number' + classes += "line-number line-number-#{bufferRow}" "
#{innerHTML}
" diff --git a/src/pane.coffee b/src/pane.coffee index e0c579257..f09bcc90f 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -212,7 +212,10 @@ class Pane extends Model @destroyItem(item) for item in @getItems() when item isnt @activeItem destroy: -> - super unless @container?.isAlive() and @container?.getPanes().length is 1 + if @container?.isAlive() and @container.getPanes().length is 1 + @destroyItems() + else + super # Called by model superclass. destroyed: -> @@ -331,7 +334,7 @@ class Pane extends Model if @parent.orientation isnt orientation @parent.replaceChild(this, new PaneAxis({@container, orientation, children: [this]})) - newPane = new @constructor(extend({focused: true}, params)) + newPane = new @constructor(params) switch side when 'before' then @parent.insertChildBefore(this, newPane) when 'after' then @parent.insertChildAfter(this, newPane) diff --git a/src/react-editor-view.coffee b/src/react-editor-view.coffee index 5bb530c44..69765190a 100644 --- a/src/react-editor-view.coffee +++ b/src/react-editor-view.coffee @@ -23,6 +23,41 @@ class ReactEditorView extends View Object.defineProperty @::, 'charWidth', get: -> @editor.getDefaultCharWidth() Object.defineProperty @::, 'firstRenderedScreenRow', get: -> @component.getRenderedRowRange()[0] Object.defineProperty @::, 'lastRenderedScreenRow', get: -> @component.getRenderedRowRange()[1] + Object.defineProperty @::, 'active', get: -> @is(@getPane().activeView) + Object.defineProperty @::, 'isFocused', get: -> @component?.state.focused + + afterAttach: (onDom) -> + return unless onDom + return if @attached + + @attached = true + props = defaults({@editor, parentView: this}, @props) + @component = React.renderComponent(EditorComponent(props), @element) + + node = @component.getDOMNode() + + @scrollView = $(node).find('.scroll-view') + @underlayer = $(node).find('.selections').addClass('underlayer') + @overlayer = $(node).find('.lines').addClass('overlayer') + @hiddenInput = $(node).find('.hidden-input') + + @gutter = $(node).find('.gutter') + @gutter.removeClassFromAllLines = (klass) => + Grim.deprecate 'You no longer need to manually add and remove classes. Use `Editor::removeDecorationFromBufferRow()` and related functions' + @gutter.find('.line-number').removeClass(klass) + + @gutter.getLineNumberElement = (bufferRow) => + @gutter.find("[data-buffer-row='#{bufferRow}']") + + @gutter.addClassToLine = (bufferRow, klass) => + Grim.deprecate 'You no longer need to manually add and remove classes. Use `Editor::addDecorationToBufferRow()` and related functions' + lines = @gutter.find("[data-buffer-row='#{bufferRow}']") + lines.addClass(klass) + lines.length > 0 + + @focus() if @focusOnAttach + + @trigger 'editor:attached', [this] scrollTop: (scrollTop) -> if scrollTop? @@ -36,36 +71,21 @@ class ReactEditorView extends View else @editor.getScrollLeft() + scrollToBottom: -> + @editor.setScrollBottom(Infinity) + scrollToScreenPosition: (screenPosition) -> @editor.scrollToScreenPosition(screenPosition) scrollToBufferPosition: (bufferPosition) -> @editor.scrollToBufferPosition(bufferPosition) - afterAttach: (onDom) -> - return unless onDom - @attached = true - props = defaults({@editor, parentView: this}, @props) - @component = React.renderComponent(EditorComponent(props), @element) + scrollToCursorPosition: -> + @editor.scrollToCursorPosition() - node = @component.getDOMNode() - - @underlayer = $(node).find('.selections') - - @gutter = $(node).find('.gutter') - @gutter.removeClassFromAllLines = (klass) => - Grim.deprecate 'You no longer need to manually add and remove classes. Use `Editor::removeDecorationFromBufferRow()` and related functions' - @gutter.find('.line-number').removeClass(klass) - - @gutter.addClassToLine = (bufferRow, klass) => - Grim.deprecate 'You no longer need to manually add and remove classes. Use `Editor::addDecorationToBufferRow()` and related functions' - lines = @gutter.find("[data-buffer-row='#{bufferRow}']") - lines.addClass(klass) - lines.length > 0 - - @focus() if @focusOnAttach - - @trigger 'editor:attached', [this] + scrollToPixelPosition: (pixelPosition) -> + screenPosition = screenPositionForPixelPosition(pixelPosition) + @editor.scrollToScreenPosition(screenPosition) pixelPositionForBufferPosition: (bufferPosition) -> @editor.pixelPositionForBufferPosition(bufferPosition) @@ -83,6 +103,26 @@ class ReactEditorView extends View @attached = false @trigger 'editor:detached', this + # Public: Split the editor view left. + splitLeft: -> + pane = @getPane() + pane?.splitLeft(pane?.copyActiveItem()).activeView + + # Public: Split the editor view right. + splitRight: -> + pane = @getPane() + pane?.splitRight(pane?.copyActiveItem()).activeView + + # Public: Split the editor view up. + splitUp: -> + pane = @getPane() + pane?.splitUp(pane?.copyActiveItem()).activeView + + # Public: Split the editor view down. + splitDown: -> + pane = @getPane() + pane?.splitDown(pane?.copyActiveItem()).activeView + getPane: -> @closest('.pane').view() @@ -94,10 +134,77 @@ class ReactEditorView extends View hide: -> super - @component.hide() + @component?.hide() show: -> super - @component.show() + @component?.show() + + pageDown: -> + @editor.pageDown() + + pageUp: -> + @editor.pageUp() + + getModel: -> + @component?.getModel() + + getFirstVisibleScreenRow: -> + @editor.getVisibleRowRange()[0] + + getLastVisibleScreenRow: -> + @editor.getVisibleRowRange()[1] + + getFontFamily: -> + @component?.getFontFamily() + + setFontFamily: (fontFamily)-> + @component?.setFontFamily(fontFamily) + + getFontSize: -> + @component?.getFontSize() + + setFontSize: (fontSize)-> + @component?.setFontSize(fontSize) + + setWidthInChars: (widthInChars) -> + @component.getDOMNode().style.width = (@editor.getDefaultCharWidth() * widthInChars) + 'px' + + setLineHeight: (lineHeight) -> + @component.setLineHeight(lineHeight) + + setShowIndentGuide: (showIndentGuide) -> + @component.setShowIndentGuide(showIndentGuide) + + setSoftWrap: (softWrap) -> + @editor.setSoftWrap(softWrap) + + setShowInvisibles: (showInvisibles) -> + @component.setShowInvisibles(showInvisibles) + + toggleSoftWrap: -> + @editor.toggleSoftWrap() + + toggleSoftTabs: -> + @editor.toggleSoftTabs() + + getText: -> + @editor.getText() + + setText: (text) -> + @editor.setText(text) + + insertText: (text) -> + @editor.insertText(text) + + isInputEnabled: -> + @component.isInputEnabled() + + setInputEnabled: (inputEnabled) -> + @component.setInputEnabled(inputEnabled) requestDisplayUpdate: -> # No-op shim for find-and-replace + + updateDisplay: -> # No-op shim for package specs + + redraw: -> # No-op shim