diff --git a/spec/editor-component-spec.coffee b/spec/editor-component-spec.coffee index 8b44153ab..f61163df3 100644 --- a/spec/editor-component-spec.coffee +++ b/spec/editor-component-spec.coffee @@ -1375,6 +1375,18 @@ describe "EditorComponent", -> expect(editor.getLineHeightInPixels()).not.toBe initialLineHeightInPixels expect(editor.getDefaultCharWidth()).not.toBe initialCharWidth + it "does not re-measure character widths until the editor is shown again", -> + wrapperView.hide() + + component.setFontSize(22) + + wrapperView.show() + editor.setCursorBufferPosition([0, Infinity]) + + cursorLeft = node.querySelector('.cursor').getBoundingClientRect().left + line0Right = node.querySelector('.line').getBoundingClientRect().right + expect(cursorLeft).toBe line0Right + describe "when the fontFamily changes while the editor is hidden", -> it "does not attempt to measure the defaultCharWidth until the editor becomes visible again", -> wrapperView.hide() @@ -1387,6 +1399,18 @@ describe "EditorComponent", -> wrapperView.show() expect(editor.getDefaultCharWidth()).not.toBe initialCharWidth + it "does not re-measure character widths until the editor is shown again", -> + wrapperView.hide() + + component.setFontFamily('sans-serif') + + wrapperView.show() + editor.setCursorBufferPosition([0, Infinity]) + + cursorLeft = node.querySelector('.cursor').getBoundingClientRect().left + line0Right = node.querySelector('.line').getBoundingClientRect().right + expect(cursorLeft).toBe line0Right + describe "when lines are changed while the editor is hidden", -> it "does not measure new characters until the editor is shown again", -> editor.setText('') diff --git a/src/cursors-component.coffee b/src/cursors-component.coffee index eb1cc14b6..49ebec37a 100644 --- a/src/cursors-component.coffee +++ b/src/cursors-component.coffee @@ -34,7 +34,10 @@ CursorsComponent = React.createClass shouldComponentUpdate: (newProps, newState) -> not newState.blinkOff is @state.blinkOff or - not isEqualForProperties(newProps, @props, 'cursorScreenRanges', 'scrollTop', 'scrollLeft', 'lineHeightInPixels', 'defaultCharWidth') + not isEqualForProperties(newProps, @props, + 'cursorScreenRanges', 'scrollTop', 'scrollLeft', 'lineHeightInPixels', + 'defaultCharWidth', 'scopedCharacterWidthsChangeCount' + ) componentWillUpdate: (newProps) -> @pauseCursorBlinking() if @props.cursorScreenRanges and not isEqual(newProps.cursorScreenRanges, @props.cursorScreenRanges) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 24bd8c0ca..7a24d359d 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -34,6 +34,7 @@ class DisplayBuffer extends Model horizontalScrollMargin: 6 horizontalScrollbarHeight: 15 verticalScrollbarWidth: 15 + scopedCharacterWidthsChangeCount: 0 constructor: ({tabLength, @editorWidthInChars, @tokenizedBuffer, buffer}={}) -> super @@ -222,9 +223,11 @@ class DisplayBuffer extends Model setScopedCharWidth: (scopeNames, char, width) -> @getScopedCharWidths(scopeNames)[char] = width + @emit 'character-widths-changed', @scopedCharacterWidthsChangeCount++ setScopedCharWidths: (scopeNames, charWidths) -> _.extend(@getScopedCharWidths(scopeNames), charWidths) + @emit 'character-widths-changed', @scopedCharacterWidthsChangeCount++ clearScopedCharWidths: -> @charWidthsByScope = {} diff --git a/src/editor-component.coffee b/src/editor-component.coffee index 9c5cfaa69..d520ad1dc 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -36,8 +36,10 @@ EditorComponent = React.createClass scrollSensitivity: 0.4 scrollViewMeasurementRequested: false measureLineHeightAndDefaultCharWidthWhenShown: false + remeasureCharacterWidthsWhenShown: false inputEnabled: true scrollViewMeasurementInterval: 100 + scopedCharacterWidthsChangeCount: null render: -> {focused, fontSize, lineHeight, fontFamily, showIndentGuide, showInvisibles, visible} = @state @@ -92,14 +94,14 @@ EditorComponent = React.createClass CursorsComponent { editor, scrollTop, scrollLeft, cursorScreenRanges, cursorBlinkPeriod, cursorBlinkResumeDelay, - lineHeightInPixels, defaultCharWidth + lineHeightInPixels, defaultCharWidth, @scopedCharacterWidthsChangeCount } LinesComponent { ref: 'lines', editor, lineHeightInPixels, defaultCharWidth, lineDecorations, highlightDecorations, showIndentGuide, renderedRowRange, @pendingChanges, scrollTop, scrollLeft, @scrollingVertically, scrollHeight, scrollWidth, mouseWheelScreenRow, invisibles, - visible, scrollViewHeight + visible, scrollViewHeight, @scopedCharacterWidthsChangeCount } ScrollbarComponent @@ -183,6 +185,7 @@ EditorComponent = React.createClass @updateParentViewFocusedClassIfNeeded(prevState) @measureScrollbars() if @measuringScrollbars @measureLineHeightAndCharWidthsIfNeeded(prevState) + @remeasureCharacterWidthsIfNeeded(prevState) @props.parentView.trigger 'editor:display-updated' requestUpdate: -> @@ -260,6 +263,7 @@ EditorComponent = React.createClass @subscribe editor, 'selection-added', @onSelectionAdded @subscribe editor, 'decoration-added', @onDecorationChanged @subscribe editor, 'decoration-removed', @onDecorationChanged + @subscribe editor, 'character-widths-changed', @onCharacterWidthsChanged @subscribe editor.$scrollTop.changes, @onScrollTopChanged @subscribe editor.$scrollLeft.changes, @requestUpdate @subscribe editor.$height.changes, @requestUpdate @@ -557,6 +561,9 @@ EditorComponent = React.createClass @requestUpdate() if @isMounted() @decorationChangedImmediate = null + onCharacterWidthsChanged: (@scopedCharacterWidthsChangeCount) -> + @requestUpdate() + selectToMousePositionUntilMouseUp: (event) -> {editor} = @props dragging = false @@ -624,19 +631,10 @@ EditorComponent = React.createClass measureLineHeightAndCharWidthsIfNeeded: (prevState) -> if not isEqualForProperties(prevState, @state, 'lineHeight', 'fontSize', 'fontFamily') - {editor} = @props - - editor.batchUpdates => - oldDefaultCharWidth = editor.getDefaultCharWidth() - - if @state.visible - @measureLineHeightAndDefaultCharWidth() - else - @measureLineHeightAndDefaultCharWidthWhenShown = true - - unless oldDefaultCharWidth is editor.getDefaultCharWidth() - @remeasureCharacterWidths() - + if @state.visible + @measureLineHeightAndDefaultCharWidth() + else + @measureLineHeightAndDefaultCharWidthWhenShown = true else if @measureLineHeightAndDefaultCharWidthWhenShown and @state.visible and not prevState.visible @measureLineHeightAndDefaultCharWidth() @@ -644,7 +642,17 @@ EditorComponent = React.createClass @measureLineHeightAndDefaultCharWidthWhenShown = false @refs.lines.measureLineHeightAndDefaultCharWidth() + remeasureCharacterWidthsIfNeeded: (prevState) -> + if not isEqualForProperties(prevState, @state, 'fontSize', 'fontFamily') + if @state.visible + @remeasureCharacterWidths() + else + @remeasureCharacterWidthsWhenShown = true + else if @remeasureCharacterWidthsWhenShown and @state.visible and not prevState.visible + @remeasureCharacterWidths() + remeasureCharacterWidths: -> + @remeasureCharacterWidthsWhenShown = false @refs.lines.remeasureCharacterWidths() onGutterWidthChanged: (@gutterWidth) -> diff --git a/src/editor.coffee b/src/editor.coffee index 7d613e4c3..f0df41850 100644 --- a/src/editor.coffee +++ b/src/editor.coffee @@ -217,6 +217,7 @@ class Editor extends Model @subscribe @displayBuffer, 'soft-wrap-changed', (args...) => @emit 'soft-wrap-changed', args... @subscribe @displayBuffer, "decoration-added", (args...) => @emit 'decoration-added', args... @subscribe @displayBuffer, "decoration-removed", (args...) => @emit 'decoration-removed', args... + @subscribe @displayBuffer, "character-widths-changed", (changeCount) => @emit 'character-widths-changed', changeCount getViewClass: -> if atom.config.get('core.useReactEditor') diff --git a/src/highlights-component.coffee b/src/highlights-component.coffee index 189c19129..292e94892 100644 --- a/src/highlights-component.coffee +++ b/src/highlights-component.coffee @@ -21,4 +21,4 @@ HighlightsComponent = React.createClass highlightComponents shouldComponentUpdate: (newProps) -> - not isEqualForProperties(newProps, @props, 'highlightDecorations', 'lineHeightInPixels', 'defaultCharWidth') + not isEqualForProperties(newProps, @props, 'highlightDecorations', 'lineHeightInPixels', 'defaultCharWidth', 'scopedCharacterWidthsChangeCount') diff --git a/src/lines-component.coffee b/src/lines-component.coffee index 2f73ad9dc..a7e61257f 100644 --- a/src/lines-component.coffee +++ b/src/lines-component.coffee @@ -15,7 +15,8 @@ LinesComponent = React.createClass render: -> if @isMounted() - {editor, highlightDecorations, scrollTop, scrollLeft, scrollHeight, scrollWidth, lineHeightInPixels, defaultCharWidth, scrollViewHeight} = @props + {editor, highlightDecorations, scrollTop, scrollLeft, scrollHeight, scrollWidth} = @props + {lineHeightInPixels, defaultCharWidth, scrollViewHeight, scopedCharacterWidthsChangeCount} = @props style = height: Math.max(scrollHeight, scrollViewHeight) width: scrollWidth @@ -24,7 +25,7 @@ LinesComponent = React.createClass # The lines div must have the 'editor-colors' class so it has an opaque # background to avoid sub-pixel anti-aliasing problems on the GPU div {className: 'lines editor-colors', style}, - HighlightsComponent({editor, highlightDecorations, lineHeightInPixels, defaultCharWidth}) if @isMounted() + HighlightsComponent({editor, highlightDecorations, lineHeightInPixels, defaultCharWidth, scopedCharacterWidthsChangeCount}) if @isMounted() componentWillMount: -> @measuredLines = new WeakSet @@ -36,7 +37,7 @@ LinesComponent = React.createClass return true unless isEqualForProperties(newProps, @props, 'renderedRowRange', 'highlightDecorations', 'lineHeightInPixels', 'defaultCharWidth', 'scrollTop', 'scrollLeft', 'showIndentGuide', 'scrollingVertically', 'invisibles', 'visible', - 'scrollViewHeight', 'mouseWheelScreenRow' + 'scrollViewHeight', 'mouseWheelScreenRow', 'scopedCharacterWidthsChangeCount' ) {renderedRowRange, pendingChanges} = newProps @@ -217,13 +218,15 @@ LinesComponent = React.createClass @measureCharactersInNewLines() measureCharactersInNewLines: -> + {editor} = @props [visibleStartRow, visibleEndRow] = @props.renderedRowRange node = @getDOMNode() - for tokenizedLine in @props.editor.linesForScreenRows(visibleStartRow, visibleEndRow - 1) - unless @measuredLines.has(tokenizedLine) - lineNode = @lineNodesByLineId[tokenizedLine.id] - @measureCharactersInLine(tokenizedLine, lineNode) + editor.batchUpdates => + for tokenizedLine in editor.linesForScreenRows(visibleStartRow, visibleEndRow - 1) + unless @measuredLines.has(tokenizedLine) + lineNode = @lineNodesByLineId[tokenizedLine.id] + @measureCharactersInLine(tokenizedLine, lineNode) measureCharactersInLine: (tokenizedLine, lineNode) -> {editor} = @props