diff --git a/package.json b/package.json index 348b1f878..4f00610bb 100644 --- a/package.json +++ b/package.json @@ -111,7 +111,7 @@ "package-generator": "1.0.0", "settings-view": "0.241.2", "snippets": "1.0.2", - "spell-check": "0.67.1", + "spell-check": "0.68.0", "status-bar": "1.4.1", "styleguide": "0.47.0", "symbols-view": "0.113.1", diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 8abbb0ece..ac4d502b9 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -1119,6 +1119,20 @@ describe "Pane", -> newPane = Pane.deserialize(pane.serialize(), atom) expect(newPane.getActiveItem()).toEqual newPane.itemAtIndex(1) + it "restores the active item when it doesn't implement getURI()", -> + pane.items[1].getURI = null + pane.activateItemAtIndex(1) + newPane = Pane.deserialize(pane.serialize(), atom) + expect(newPane.getActiveItem()).toEqual newPane.itemAtIndex(1) + + it "restores the correct item when it doesn't implement getURI() and some items weren't deserialized", -> + unserializable = {} + pane.addItem(unserializable, {index: 0}) + pane.items[2].getURI = null + pane.activateItemAtIndex(2) + newPane = Pane.deserialize(pane.serialize(), atom) + expect(newPane.getActiveItem()).toEqual newPane.itemAtIndex(1) + it "does not include items that cannot be deserialized", -> spyOn(console, 'warn') unserializable = {} diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 9431673d6..ca0962300 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -3,6 +3,7 @@ path = require 'path' temp = require 'temp' clipboard = require '../src/safe-clipboard' TextEditor = require '../src/text-editor' +TextBuffer = require 'text-buffer' describe "TextEditor", -> [buffer, editor, lineLengths] = [] @@ -41,7 +42,7 @@ describe "TextEditor", -> it "restores the editor's layout configuration", -> editor.update({ softTabs: true - atomicSoftTabs: true + atomicSoftTabs: false tabLength: 12 softWrapped: true softWrapAtPreferredLineLength: true @@ -51,7 +52,16 @@ describe "TextEditor", -> editorWidthInChars: 120 }) - editor2 = TextEditor.deserialize(editor.serialize(), atom) + # Force buffer and display layer to be deserialized as well, rather than + # reusing the same buffer instance + editor2 = TextEditor.deserialize(editor.serialize(), { + assert: atom.assert, + clipboard: atom.clipboard, + textEditors: atom.textEditors, + project: { + bufferForIdSync: (id) -> TextBuffer.deserialize(editor.buffer.serialize()) + } + }) expect(editor2.getSoftTabs()).toBe(editor.getSoftTabs()) expect(editor2.hasAtomicSoftTabs()).toBe(editor.hasAtomicSoftTabs()) @@ -60,6 +70,7 @@ describe "TextEditor", -> expect(editor2.getSoftWrapHangingIndentLength()).toBe(editor.getSoftWrapHangingIndentLength()) expect(editor2.getInvisibles()).toEqual(editor.getInvisibles()) expect(editor2.getEditorWidthInChars()).toBe(editor.getEditorWidthInChars()) + expect(editor2.displayLayer.tabLength).toBe(editor2.getTabLength()) describe "when the editor is constructed with the largeFileMode option set to true", -> it "loads the editor but doesn't tokenize", -> diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index fefcafffb..a9d984049 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -432,9 +432,9 @@ describe "Workspace", -> runs -> expect(editor.largeFileMode).toBe true - describe "when the file is over 20MB", -> - it "prompts the user to make sure they want to open a file this big", -> - spyOn(fs, 'getSizeSync').andReturn 20 * 1048577 # 20MB + describe "when the file is over user-defined limit", -> + shouldPromptForFileOfSize = (size, shouldPrompt) -> + spyOn(fs, 'getSizeSync').andReturn size * 1048577 atom.applicationDelegate.confirm.andCallFake -> selectedButtonIndex atom.applicationDelegate.confirm() selectedButtonIndex = 1 # cancel @@ -442,20 +442,35 @@ describe "Workspace", -> editor = null waitsForPromise -> workspace.open('sample.js').then (e) -> editor = e + if shouldPrompt + runs -> + expect(editor).toBeUndefined() + expect(atom.applicationDelegate.confirm).toHaveBeenCalled() - runs -> - expect(editor).toBeUndefined() - expect(atom.applicationDelegate.confirm).toHaveBeenCalled() + atom.applicationDelegate.confirm.reset() + selectedButtonIndex = 0 # open the file - atom.applicationDelegate.confirm.reset() - selectedButtonIndex = 0 # open the file + waitsForPromise -> + workspace.open('sample.js').then (e) -> editor = e - waitsForPromise -> - workspace.open('sample.js').then (e) -> editor = e + runs -> + expect(atom.applicationDelegate.confirm).toHaveBeenCalled() + expect(editor.largeFileMode).toBe true + else + runs -> + expect(editor).not.toBeUndefined() - runs -> - expect(atom.applicationDelegate.confirm).toHaveBeenCalled() - expect(editor.largeFileMode).toBe true + it "prompts the user to make sure they want to open a file this big", -> + atom.config.set "core.warnOnLargeFileLimit", 20 + shouldPromptForFileOfSize 20, true + + it "doesn't prompt on files below the limit", -> + atom.config.set "core.warnOnLargeFileLimit", 30 + shouldPromptForFileOfSize 20, false + + it "prompts for smaller files with a lower limit", -> + atom.config.set "core.warnOnLargeFileLimit", 5 + shouldPromptForFileOfSize 10, true describe "when passed a path that matches a custom opener", -> it "returns the resource returned by the custom opener", -> diff --git a/src/config-schema.coffee b/src/config-schema.coffee index dd659687c..e240a6dce 100644 --- a/src/config-schema.coffee +++ b/src/config-schema.coffee @@ -122,6 +122,10 @@ module.exports = {value: 'no', description: 'Do not send any telemetry data'} {value: 'undecided', description: 'Undecided (Atom will ask again next time it is launched)'} ] + warnOnLargeFileLimit: + description: 'Warn before opening files larger than this number of megabytes.' + type: 'number' + default: 20 editor: type: 'object' properties: diff --git a/src/pane.coffee b/src/pane.coffee index add6a365b..f7487a6e1 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -21,13 +21,16 @@ class Pane extends Model focused: false @deserialize: (state, {deserializers, applicationDelegate, config, notifications}) -> - {items, itemStackIndices, activeItemURI, activeItemUri} = state + {items, itemStackIndices, activeItemIndex, activeItemURI, activeItemUri} = state activeItemURI ?= activeItemUri - state.items = compact(items.map (itemState) -> deserializers.deserialize(itemState)) - state.activeItem = find state.items, (item) -> - if typeof item.getURI is 'function' - itemURI = item.getURI() - itemURI is activeItemURI + items = items.map (itemState) -> deserializers.deserialize(itemState) + state.activeItem = items[activeItemIndex] + state.items = compact(items) + if activeItemURI? + state.activeItem ?= find state.items, (item) -> + if typeof item.getURI is 'function' + itemURI = item.getURI() + itemURI is activeItemURI new Pane(extend(state, { deserializerManager: deserializers, notificationManager: notifications, @@ -53,16 +56,15 @@ class Pane extends Model @setFlexScale(params?.flexScale ? 1) serialize: -> - if typeof @activeItem?.getURI is 'function' - activeItemURI = @activeItem.getURI() itemsToBeSerialized = compact(@items.map((item) -> item if typeof item.serialize is 'function')) itemStackIndices = (itemsToBeSerialized.indexOf(item) for item in @itemStack when typeof item.serialize is 'function') + activeItemIndex = itemsToBeSerialized.indexOf(@activeItem) deserializer: 'Pane' id: @id items: itemsToBeSerialized.map((item) -> item.serialize()) itemStackIndices: itemStackIndices - activeItemURI: activeItemURI + activeItemIndex: activeItemIndex focused: @focused flexScale: @flexScale diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 41dd483a5..4338008f8 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -102,6 +102,7 @@ class TextEditor extends Model try state.tokenizedBuffer = TokenizedBuffer.deserialize(state.tokenizedBuffer, atomEnvironment) + state.tabLength = state.tokenizedBuffer.getTabLength() catch error if error.syscall is 'read' return # Error reading the file, don't deserialize an editor for it @@ -128,7 +129,7 @@ class TextEditor extends Model @softWrapped, @decorationManager, @selectionsMarkerLayer, @buffer, suppressCursorCreation, @mini, @placeholderText, lineNumberGutterVisible, @largeFileMode, @clipboard, @assert, grammar, @showInvisibles, @autoHeight, @autoWidth, @scrollPastEnd, @editorWidthInChars, - @tokenizedBuffer, @displayLayer, @invisibles, @showIndentGuide, @softWrapHangingIndentLength, + @tokenizedBuffer, @displayLayer, @invisibles, @showIndentGuide, @softWrapped, @softWrapAtPreferredLineLength, @preferredLineLength } = params @@ -154,7 +155,6 @@ class TextEditor extends Model @undoGroupingInterval ?= 300 @nonWordCharacters ?= "/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-…" @softWrapped ?= false - @softWrapHangingIndentLength ?= 0 @softWrapAtPreferredLineLength ?= false @preferredLineLength ?= 80 @@ -162,17 +162,24 @@ class TextEditor extends Model @tokenizedBuffer ?= new TokenizedBuffer({ grammar, tabLength, @buffer, @largeFileMode, @assert }) - @displayLayer ?= @buffer.addDisplayLayer({ + + displayLayerParams = { invisibles: @getInvisibles(), softWrapColumn: @getSoftWrapColumn(), showIndentGuides: not @isMini() and @doesShowIndentGuide(), - atomicSoftTabs: @hasAtomicSoftTabs(), - tabLength: @getTabLength(), + atomicSoftTabs: params.atomicSoftTabs ? true, + tabLength: tabLength, ratioForCharacter: @ratioForCharacter.bind(this), isWrapBoundary: isWrapBoundary, foldCharacter: ZERO_WIDTH_NBSP, - softWrapHangingIndent: @getSoftWrapHangingIndentLength() - }) + softWrapHangingIndent: params.softWrapHangingIndentLength ? 0 + } + + if @displayLayer? + @displayLayer.reset(displayLayerParams) + else + @displayLayer = @buffer.addDisplayLayer(displayLayerParams) + @displayLayer.setTextDecorationLayer(@tokenizedBuffer) @defaultMarkerLayer = @displayLayer.addMarkerLayer() @selectionsMarkerLayer ?= @addMarkerLayer(maintainHistory: true, persistent: true) @@ -229,8 +236,7 @@ class TextEditor extends Model @softTabs = value when 'atomicSoftTabs' - if value isnt @atomicSoftTabs - @atomicSoftTabs = value + if value isnt @displayLayer.atomicSoftTabs displayLayerParams.atomicSoftTabs = value when 'tabLength' @@ -245,8 +251,7 @@ class TextEditor extends Model @emitter.emit 'did-change-soft-wrapped', @isSoftWrapped() when 'softWrapHangingIndentLength' - if value isnt @softWrapHangingIndentLength - @softWrapHangingIndentLength = value + if value isnt @displayLayer.softWrapHangingIndent displayLayerParams.softWrapHangingIndent = value when 'softWrapAtPreferredLineLength' @@ -351,11 +356,12 @@ class TextEditor extends Model firstVisibleScreenRow: @getFirstVisibleScreenRow() firstVisibleScreenColumn: @getFirstVisibleScreenColumn() - @id, @softTabs, @atomicSoftTabs, @tabLength, @softWrapped, - @softWrapHangingIndentLength, @softWrapAtPreferredLineLength, - @preferredLineLength, @mini, @editorWidthInChars, @width, @largeFileMode, - @registered, @invisibles, @showInvisibles, @showIndentGuide, @autoHeight, - @autoWidth + atomicSoftTabs: @displayLayer.atomicSoftTabs + softWrapHangingIndentLength: @displayLayer.softWrapHangingIndent + + @id, @softTabs, @softWrapped, @softWrapAtPreferredLineLength, + @preferredLineLength, @mini, @editorWidthInChars, @width, @largeFileMode, + @registered, @invisibles, @showInvisibles, @showIndentGuide, @autoHeight, @autoWidth } subscribeToBuffer: -> @@ -694,8 +700,9 @@ class TextEditor extends Model selectionsMarkerLayer = displayLayer.getMarkerLayer(@buffer.getMarkerLayer(@selectionsMarkerLayer.id).copy().id) softTabs = @getSoftTabs() new TextEditor({ - @buffer, selectionsMarkerLayer, @tabLength, softTabs, + @buffer, selectionsMarkerLayer, softTabs, suppressCursorCreation: true, + tabLength: @tokenizedBuffer.getTabLength(), @firstVisibleScreenRow, @firstVisibleScreenColumn, @clipboard, @assert, displayLayer, grammar: @getGrammar() }) @@ -2817,7 +2824,7 @@ class TextEditor extends Model setSoftTabs: (@softTabs) -> @update({softTabs}) # Returns a {Boolean} indicating whether atomic soft tabs are enabled for this editor. - hasAtomicSoftTabs: -> @atomicSoftTabs + hasAtomicSoftTabs: -> @displayLayer.atomicSoftTabs # Essential: Toggle soft tabs for this editor toggleSoftTabs: -> @setSoftTabs(not @getSoftTabs()) @@ -2844,7 +2851,7 @@ class TextEditor extends Model doesShowIndentGuide: -> @showIndentGuide and not @mini - getSoftWrapHangingIndentLength: -> @softWrapHangingIndentLength + getSoftWrapHangingIndentLength: -> @displayLayer.softWrapHangingIndent # Extended: Determine if the buffer uses hard or soft tabs. # diff --git a/src/workspace.coffee b/src/workspace.coffee index 7fb893ca5..aa2b4ae79 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -550,7 +550,7 @@ class Workspace extends Model fileSize = fs.getSizeSync(filePath) largeFileMode = fileSize >= 2 * 1048576 # 2MB - if fileSize >= 20 * 1048576 # 20MB + if fileSize >= @config.get('core.warnOnLargeFileLimit') * 1048576 # 20MB by default choice = @applicationDelegate.confirm message: 'Atom will be unresponsive during the loading of very large files.' detailedMessage: "Do you still want to load this file?"