diff --git a/package.json b/package.json index eb81767b9..112db7f5a 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "electronVersion": "1.3.6", "dependencies": { "async": "0.2.6", - "atom-keymap": "6.3.2", + "atom-keymap": "6.3.5", "atom-ui": "0.4.1", "babel-core": "5.8.38", "cached-run-in-this-context": "0.4.1", @@ -58,7 +58,7 @@ "sinon": "1.17.4", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "9.2.12", + "text-buffer": "9.3.0", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "winreg": "^1.2.1", @@ -75,8 +75,8 @@ "one-light-ui": "1.6.1", "one-dark-syntax": "1.4.0", "one-light-syntax": "1.4.0", - "solarized-dark-syntax": "1.0.2", - "solarized-light-syntax": "1.0.2", + "solarized-dark-syntax": "1.0.3", + "solarized-light-syntax": "1.0.3", "about": "1.7.0", "archive-view": "0.61.1", "autocomplete-atom-api": "0.10.0", @@ -109,9 +109,9 @@ "notifications": "0.65.1", "open-on-github": "1.2.1", "package-generator": "1.0.1", - "settings-view": "0.242.3", + "settings-view": "0.243.0", "snippets": "1.0.3", - "spell-check": "0.68.2", + "spell-check": "0.68.3", "status-bar": "1.4.1", "styleguide": "0.47.2", "symbols-view": "0.113.1", @@ -139,7 +139,7 @@ "language-make": "0.22.2", "language-mustache": "0.13.0", "language-objective-c": "0.15.1", - "language-perl": "0.36.0", + "language-perl": "0.37.0", "language-php": "0.37.2", "language-property-list": "0.8.0", "language-python": "0.45.0", diff --git a/spec/menu-manager-spec.coffee b/spec/menu-manager-spec.coffee index a2d76c1d0..5de5ecf92 100644 --- a/spec/menu-manager-spec.coffee +++ b/spec/menu-manager-spec.coffee @@ -53,6 +53,9 @@ describe "MenuManager", -> expect(menu.template[originalItemCount]).toEqual {label: "A", submenu: [{label: "B", command: "b"}]} describe "::update()", -> + originalPlatform = process.platform + afterEach -> Object.defineProperty process, 'platform', value: originalPlatform + it "sends the current menu template and associated key bindings to the browser process", -> spyOn(menu, 'sendToBrowserProcess') menu.add [{label: "A", submenu: [{label: "B", command: "b"}]}] @@ -75,6 +78,47 @@ describe "MenuManager", -> runs -> expect(menu.sendToBrowserProcess.argsForCall[0][1]['b']).toBeUndefined() + it "omits key bindings that could conflict with AltGraph characters on macOS", -> + spyOn(menu, 'sendToBrowserProcess') + menu.add [{label: "A", submenu: [ + {label: "B", command: "b"}, + {label: "C", command: "c"} + {label: "D", command: "d"} + ]}] + + atom.keymaps.add 'test', 'atom-workspace': + 'alt-b': 'b' + 'alt-shift-C': 'c' + 'alt-cmd-d': 'd' + + waits 50 + + runs -> + expect(menu.sendToBrowserProcess.argsForCall[0][1]['b']).toBeUndefined() + expect(menu.sendToBrowserProcess.argsForCall[0][1]['c']).toBeUndefined() + expect(menu.sendToBrowserProcess.argsForCall[0][1]['d']).toEqual(['alt-cmd-d']) + + it "omits key bindings that could conflict with AltGraph characters on Windows", -> + Object.defineProperty process, 'platform', value: 'win32' + spyOn(menu, 'sendToBrowserProcess') + menu.add [{label: "A", submenu: [ + {label: "B", command: "b"}, + {label: "C", command: "c"} + {label: "D", command: "d"} + ]}] + + atom.keymaps.add 'test', 'atom-workspace': + 'ctrl-alt-b': 'b' + 'ctrl-alt-shift-C': 'c' + 'ctrl-alt-cmd-d': 'd' + + waits 50 + + runs -> + expect(menu.sendToBrowserProcess.argsForCall[0][1]['b']).toBeUndefined() + expect(menu.sendToBrowserProcess.argsForCall[0][1]['c']).toBeUndefined() + expect(menu.sendToBrowserProcess.argsForCall[0][1]['d']).toEqual(['ctrl-alt-cmd-d']) + it "updates the application menu when a keymap is reloaded", -> spyOn(menu, 'update') keymapPath = path.join(__dirname, 'fixtures', 'packages', 'package-with-keymaps', 'keymaps', 'keymap-1.cson') diff --git a/src/application-delegate.coffee b/src/application-delegate.coffee index 18fb59f54..e174b2254 100644 --- a/src/application-delegate.coffee +++ b/src/application-delegate.coffee @@ -57,6 +57,9 @@ class ApplicationDelegate reloadWindow: -> ipcRenderer.send("call-window-method", "reload") + restartApplication: -> + ipcRenderer.send("restart-application") + minimizeWindow: -> ipcRenderer.send("call-window-method", "minimize") diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 4fcd92f20..e2cf7f3a2 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -545,6 +545,10 @@ class AtomEnvironment extends Model reload: -> @applicationDelegate.reloadWindow() + # Extended: Relaunch the entire application. + restartApplication: -> + @applicationDelegate.restartApplication() + # Extended: Returns a {Boolean} that is `true` if the current window is maximized. isMaximized: -> @applicationDelegate.isWindowMaximized() diff --git a/src/lines-yardstick.coffee b/src/lines-yardstick.coffee index 13da96fa1..098fc2288 100644 --- a/src/lines-yardstick.coffee +++ b/src/lines-yardstick.coffee @@ -91,34 +91,34 @@ class LinesYardstick lineNode = @lineNodesProvider.lineNodeForScreenRow(row) lineId = @lineNodesProvider.lineIdForScreenRow(row) - return 0 unless lineNode? - - if cachedPosition = @leftPixelPositionCache[lineId]?[column] - return cachedPosition - - textNodes = @lineNodesProvider.textNodesForScreenRow(row) - textNodeStartColumn = 0 - - for textNode in textNodes - textNodeEndColumn = textNodeStartColumn + textNode.textContent.length - if textNodeEndColumn > column - indexInTextNode = column - textNodeStartColumn - break + if lineNode? + if @leftPixelPositionCache[lineId]?[column]? + @leftPixelPositionCache[lineId][column] else - textNodeStartColumn = textNodeEndColumn + textNodes = @lineNodesProvider.textNodesForScreenRow(row) + textNodeStartColumn = 0 + for textNode in textNodes + textNodeEndColumn = textNodeStartColumn + textNode.textContent.length + if textNodeEndColumn > column + indexInTextNode = column - textNodeStartColumn + break + else + textNodeStartColumn = textNodeEndColumn - if textNode? - indexInTextNode ?= textNode.textContent.length - lineOffset = lineNode.getBoundingClientRect().left - if indexInTextNode is 0 - leftPixelPosition = @clientRectForRange(textNode, 0, 1).left - else - leftPixelPosition = @clientRectForRange(textNode, 0, indexInTextNode).right - leftPixelPosition -= lineOffset + if textNode? + indexInTextNode ?= textNode.textContent.length + lineOffset = lineNode.getBoundingClientRect().left + if indexInTextNode is 0 + leftPixelPosition = @clientRectForRange(textNode, 0, 1).left + else + leftPixelPosition = @clientRectForRange(textNode, 0, indexInTextNode).right + leftPixelPosition -= lineOffset - @leftPixelPositionCache[lineId] ?= {} - @leftPixelPositionCache[lineId][column] = leftPixelPosition - leftPixelPosition + @leftPixelPositionCache[lineId] ?= {} + @leftPixelPositionCache[lineId][column] = leftPixelPosition + leftPixelPosition + else + 0 else 0 diff --git a/src/main-process/atom-application.coffee b/src/main-process/atom-application.coffee index 8969a1763..b98ab1c5b 100644 --- a/src/main-process/atom-application.coffee +++ b/src/main-process/atom-application.coffee @@ -62,7 +62,7 @@ class AtomApplication exit: (status) -> app.exit(status) constructor: (options) -> - {@resourcePath, @devResourcePath, @version, @devMode, @safeMode, @socketPath, timeout, clearWindowState} = options + {@resourcePath, @devResourcePath, @version, @devMode, @safeMode, @socketPath, @logFile, @setPortable, @userDataDir, timeout, clearWindowState} = options @socketPath = null if options.test @pidsToOpenWindows = {} @windows = [] @@ -83,7 +83,7 @@ class AtomApplication initialize: (options) -> global.atomApplication = this - @config.onDidChange 'core.useCustomTitleBar', @promptForRelaunch + @config.onDidChange 'core.useCustomTitleBar', @promptForRestart @autoUpdateManager = new AutoUpdateManager(@version, options.test, @resourcePath, @config) @applicationMenu = new ApplicationMenu(@version, @autoUpdateManager) @@ -254,6 +254,9 @@ class AtomApplication event?.preventDefault() @emit('application:new-window') + @disposable.add ipcHelpers.on ipcMain, 'restart-application', => + @restart() + # A request from the associated render process to open a new render process. @disposable.add ipcHelpers.on ipcMain, 'open', (event, options) => window = @windowForEvent(event) @@ -731,13 +734,24 @@ class AtomApplication dialog.showOpenDialog(parentWindow, openOptions, callback) - promptForRelaunch: -> + promptForRestart: -> chosen = dialog.showMessageBox BrowserWindow.getFocusedWindow(), type: 'warning' - title: 'Relaunch required' - message: "You will need to relaunch Atom for this change to take effect." - buttons: ['Quit Atom', 'Cancel'] + title: 'Restart required' + message: "You will need to restart Atom for this change to take effect." + buttons: ['Restart Atom', 'Cancel'] if chosen is 0 - # once we're using electron v.1.2.2 - # app.relaunch() - app.quit() + @restart() + + restart: -> + args = [] + args.push("--safe") if @safeMode + args.push("--portable") if @setPortable + args.push("--log-file=#{@logFile}") if @logFile? + args.push("--socket-path=#{@socketPath}") if @socketPath? + args.push("--user-data-dir=#{@userDataDir}") if @userDataDir? + if @devMode + args.push('--dev') + args.push("--resource-path=#{@resourcePath}") + app.relaunch({args}) + app.quit() diff --git a/src/menu-manager.coffee b/src/menu-manager.coffee index ebebfa3f3..b6ed7fd1a 100644 --- a/src/menu-manager.coffee +++ b/src/menu-manager.coffee @@ -143,17 +143,20 @@ class MenuManager # Public: Refreshes the currently visible menu. update: -> clearImmediate(@pendingUpdateOperation) if @pendingUpdateOperation? - @pendingUpdateOperation = setImmediate => - includedBindings = [] - unsetKeystrokes = new Set - for binding in @keymapManager.getKeyBindings() when @includeSelector(binding.selector) - includedBindings.push(binding) + @pendingUpdateOperation = setImmediate => + unsetKeystrokes = new Set + for binding in @keymapManager.getKeyBindings() if binding.command is 'unset!' unsetKeystrokes.add(binding.keystrokes) keystrokesByCommand = {} - for binding in includedBindings when not unsetKeystrokes.has(binding.keystrokes) + for binding in @keymapManager.getKeyBindings() + continue unless @includeSelector(binding.selector) + continue if unsetKeystrokes.has(binding.keystrokes) + continue if binding.keystrokes.includes(' ') + continue if process.platform is 'darwin' and /^alt-(shift-)?.$/.test(binding.keystrokes) + continue if process.platform is 'win32' and /^ctrl-alt-(shift-)?.$/.test(binding.keystrokes) keystrokesByCommand[binding.command] ?= [] keystrokesByCommand[binding.command].unshift binding.keystrokes @@ -176,21 +179,7 @@ class MenuManager unmerge: (menu, item) -> MenuHelpers.unmerge(menu, item) - # macOS can't handle displaying accelerators for multiple keystrokes. - # If they are sent across, it will stop processing accelerators for the rest - # of the menu items. - filterMultipleKeystroke: (keystrokesByCommand) -> - filtered = {} - for key, bindings of keystrokesByCommand - for binding in bindings - continue if binding.indexOf(' ') isnt -1 - - filtered[key] ?= [] - filtered[key].push(binding) - filtered - sendToBrowserProcess: (template, keystrokesByCommand) -> - keystrokesByCommand = @filterMultipleKeystroke(keystrokesByCommand) ipcRenderer.send 'update-application-menu', template, keystrokesByCommand # Get an {Array} of {String} classes for the given element. diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index ca1017617..6780bd404 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -195,6 +195,9 @@ class TextEditorComponent becameVisible: -> @updatesPaused = true + if @invalidateMeasurementsWhenVisible + @invalidateMeasurements() + @invalidateMeasurementsWhenVisible = false @measureScrollbars() if @measureScrollbarsWhenShown @sampleFontStyling() @sampleBackgroundColors() @@ -934,8 +937,11 @@ class TextEditorComponent @invalidateMeasurements() invalidateMeasurements: -> - @linesYardstick.invalidateCache() - @presenter.measurementsChanged() + if @isVisible() + @linesYardstick.invalidateCache() + @presenter.measurementsChanged() + else + @invalidateMeasurementsWhenVisible = true screenPositionForMouseEvent: (event, linesClientRect) -> pixelPosition = @pixelPositionForMouseEvent(event, linesClientRect)