From 2289e2b8286fa28404469976599db6db26c07054 Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Thu, 19 Oct 2017 08:42:20 -0400 Subject: [PATCH 01/97] Decaffeinate src/window-event-handler.coffee --- src/window-event-handler.coffee | 189 ------------------------ src/window-event-handler.js | 253 ++++++++++++++++++++++++++++++++ 2 files changed, 253 insertions(+), 189 deletions(-) delete mode 100644 src/window-event-handler.coffee create mode 100644 src/window-event-handler.js diff --git a/src/window-event-handler.coffee b/src/window-event-handler.coffee deleted file mode 100644 index 6a277b612..000000000 --- a/src/window-event-handler.coffee +++ /dev/null @@ -1,189 +0,0 @@ -{Disposable, CompositeDisposable} = require 'event-kit' -listen = require './delegated-listener' - -# Handles low-level events related to the @window. -module.exports = -class WindowEventHandler - constructor: ({@atomEnvironment, @applicationDelegate}) -> - @reloadRequested = false - @subscriptions = new CompositeDisposable - - @handleNativeKeybindings() - - initialize: (@window, @document) -> - @subscriptions.add @atomEnvironment.commands.add @window, - 'window:toggle-full-screen': @handleWindowToggleFullScreen - 'window:close': @handleWindowClose - 'window:reload': @handleWindowReload - 'window:toggle-dev-tools': @handleWindowToggleDevTools - - if process.platform in ['win32', 'linux'] - @subscriptions.add @atomEnvironment.commands.add @window, - 'window:toggle-menu-bar': @handleWindowToggleMenuBar - - @subscriptions.add @atomEnvironment.commands.add @document, - 'core:focus-next': @handleFocusNext - 'core:focus-previous': @handleFocusPrevious - - @addEventListener(@window, 'beforeunload', @handleWindowBeforeunload) - @addEventListener(@window, 'focus', @handleWindowFocus) - @addEventListener(@window, 'blur', @handleWindowBlur) - - @addEventListener(@document, 'keyup', @handleDocumentKeyEvent) - @addEventListener(@document, 'keydown', @handleDocumentKeyEvent) - @addEventListener(@document, 'drop', @handleDocumentDrop) - @addEventListener(@document, 'dragover', @handleDocumentDragover) - @addEventListener(@document, 'contextmenu', @handleDocumentContextmenu) - @subscriptions.add listen(@document, 'click', 'a', @handleLinkClick) - @subscriptions.add listen(@document, 'submit', 'form', @handleFormSubmit) - - @subscriptions.add(@applicationDelegate.onDidEnterFullScreen(@handleEnterFullScreen)) - @subscriptions.add(@applicationDelegate.onDidLeaveFullScreen(@handleLeaveFullScreen)) - - # Wire commands that should be handled by Chromium for elements with the - # `.native-key-bindings` class. - handleNativeKeybindings: -> - bindCommandToAction = (command, action) => - @subscriptions.add @atomEnvironment.commands.add( - '.native-key-bindings', - command, - ((event) => @applicationDelegate.getCurrentWindow().webContents[action]()), - false - ) - - bindCommandToAction('core:copy', 'copy') - bindCommandToAction('core:paste', 'paste') - bindCommandToAction('core:undo', 'undo') - bindCommandToAction('core:redo', 'redo') - bindCommandToAction('core:select-all', 'selectAll') - bindCommandToAction('core:cut', 'cut') - - unsubscribe: -> - @subscriptions.dispose() - - on: (target, eventName, handler) -> - target.on(eventName, handler) - @subscriptions.add(new Disposable -> - target.removeListener(eventName, handler) - ) - - addEventListener: (target, eventName, handler) -> - target.addEventListener(eventName, handler) - @subscriptions.add(new Disposable(-> target.removeEventListener(eventName, handler))) - - handleDocumentKeyEvent: (event) => - @atomEnvironment.keymaps.handleKeyboardEvent(event) - event.stopImmediatePropagation() - - handleDrop: (event) -> - event.preventDefault() - event.stopPropagation() - - handleDragover: (event) -> - event.preventDefault() - event.stopPropagation() - event.dataTransfer.dropEffect = 'none' - - eachTabIndexedElement: (callback) -> - for element in @document.querySelectorAll('[tabindex]') - continue if element.disabled - continue unless element.tabIndex >= 0 - callback(element, element.tabIndex) - return - - handleFocusNext: => - focusedTabIndex = @document.activeElement.tabIndex ? -Infinity - - nextElement = null - nextTabIndex = Infinity - lowestElement = null - lowestTabIndex = Infinity - @eachTabIndexedElement (element, tabIndex) -> - if tabIndex < lowestTabIndex - lowestTabIndex = tabIndex - lowestElement = element - - if focusedTabIndex < tabIndex < nextTabIndex - nextTabIndex = tabIndex - nextElement = element - - if nextElement? - nextElement.focus() - else if lowestElement? - lowestElement.focus() - - handleFocusPrevious: => - focusedTabIndex = @document.activeElement.tabIndex ? Infinity - - previousElement = null - previousTabIndex = -Infinity - highestElement = null - highestTabIndex = -Infinity - @eachTabIndexedElement (element, tabIndex) -> - if tabIndex > highestTabIndex - highestTabIndex = tabIndex - highestElement = element - - if focusedTabIndex > tabIndex > previousTabIndex - previousTabIndex = tabIndex - previousElement = element - - if previousElement? - previousElement.focus() - else if highestElement? - highestElement.focus() - - handleWindowFocus: -> - @document.body.classList.remove('is-blurred') - - handleWindowBlur: => - @document.body.classList.add('is-blurred') - @atomEnvironment.storeWindowDimensions() - - handleEnterFullScreen: => - @document.body.classList.add("fullscreen") - - handleLeaveFullScreen: => - @document.body.classList.remove("fullscreen") - - handleWindowBeforeunload: (event) => - if not @reloadRequested and not @atomEnvironment.inSpecMode() and @atomEnvironment.getCurrentWindow().isWebViewFocused() - @atomEnvironment.hide() - @reloadRequested = false - @atomEnvironment.storeWindowDimensions() - @atomEnvironment.unloadEditorWindow() - @atomEnvironment.destroy() - - handleWindowToggleFullScreen: => - @atomEnvironment.toggleFullScreen() - - handleWindowClose: => - @atomEnvironment.close() - - handleWindowReload: => - @reloadRequested = true - @atomEnvironment.reload() - - handleWindowToggleDevTools: => - @atomEnvironment.toggleDevTools() - - handleWindowToggleMenuBar: => - @atomEnvironment.config.set('core.autoHideMenuBar', not @atomEnvironment.config.get('core.autoHideMenuBar')) - - if @atomEnvironment.config.get('core.autoHideMenuBar') - detail = "To toggle, press the Alt key or execute the window:toggle-menu-bar command" - @atomEnvironment.notifications.addInfo('Menu bar hidden', {detail}) - - handleLinkClick: (event) => - event.preventDefault() - uri = event.currentTarget?.getAttribute('href') - if uri and uri[0] isnt '#' and /^https?:\/\//.test(uri) - @applicationDelegate.openExternal(uri) - - handleFormSubmit: (event) -> - # Prevent form submits from changing the current window's URL - event.preventDefault() - - handleDocumentContextmenu: (event) => - event.preventDefault() - @atomEnvironment.contextMenu.showForEvent(event) diff --git a/src/window-event-handler.js b/src/window-event-handler.js new file mode 100644 index 000000000..6d380819b --- /dev/null +++ b/src/window-event-handler.js @@ -0,0 +1,253 @@ +const {Disposable, CompositeDisposable} = require('event-kit') +const listen = require('./delegated-listener') + +// Handles low-level events related to the `window`. +module.exports = +class WindowEventHandler { + constructor ({atomEnvironment, applicationDelegate}) { + this.handleDocumentKeyEvent = this.handleDocumentKeyEvent.bind(this) + this.handleFocusNext = this.handleFocusNext.bind(this) + this.handleFocusPrevious = this.handleFocusPrevious.bind(this) + this.handleWindowBlur = this.handleWindowBlur.bind(this) + this.handleEnterFullScreen = this.handleEnterFullScreen.bind(this) + this.handleLeaveFullScreen = this.handleLeaveFullScreen.bind(this) + this.handleWindowBeforeunload = this.handleWindowBeforeunload.bind(this) + this.handleWindowToggleFullScreen = this.handleWindowToggleFullScreen.bind(this) + this.handleWindowClose = this.handleWindowClose.bind(this) + this.handleWindowReload = this.handleWindowReload.bind(this) + this.handleWindowToggleDevTools = this.handleWindowToggleDevTools.bind(this) + this.handleWindowToggleMenuBar = this.handleWindowToggleMenuBar.bind(this) + this.handleLinkClick = this.handleLinkClick.bind(this) + this.handleDocumentContextmenu = this.handleDocumentContextmenu.bind(this) + this.atomEnvironment = atomEnvironment + this.applicationDelegate = applicationDelegate + this.reloadRequested = false + this.subscriptions = new CompositeDisposable() + + this.handleNativeKeybindings() + } + + initialize (window, document) { + this.window = window + this.document = document + this.subscriptions.add(this.atomEnvironment.commands.add(this.window, { + 'window:toggle-full-screen': this.handleWindowToggleFullScreen, + 'window:close': this.handleWindowClose, + 'window:reload': this.handleWindowReload, + 'window:toggle-dev-tools': this.handleWindowToggleDevTools + })) + + if (['win32', 'linux'].includes(process.platform)) { + this.subscriptions.add(this.atomEnvironment.commands.add(this.window, + {'window:toggle-menu-bar': this.handleWindowToggleMenuBar}) + ) + } + + this.subscriptions.add(this.atomEnvironment.commands.add(this.document, { + 'core:focus-next': this.handleFocusNext, + 'core:focus-previous': this.handleFocusPrevious + })) + + this.addEventListener(this.window, 'beforeunload', this.handleWindowBeforeunload) + this.addEventListener(this.window, 'focus', this.handleWindowFocus) + this.addEventListener(this.window, 'blur', this.handleWindowBlur) + + this.addEventListener(this.document, 'keyup', this.handleDocumentKeyEvent) + this.addEventListener(this.document, 'keydown', this.handleDocumentKeyEvent) + this.addEventListener(this.document, 'drop', this.handleDocumentDrop) + this.addEventListener(this.document, 'dragover', this.handleDocumentDragover) + this.addEventListener(this.document, 'contextmenu', this.handleDocumentContextmenu) + this.subscriptions.add(listen(this.document, 'click', 'a', this.handleLinkClick)) + this.subscriptions.add(listen(this.document, 'submit', 'form', this.handleFormSubmit)) + + this.subscriptions.add(this.applicationDelegate.onDidEnterFullScreen(this.handleEnterFullScreen)) + this.subscriptions.add(this.applicationDelegate.onDidLeaveFullScreen(this.handleLeaveFullScreen)) + } + + // Wire commands that should be handled by Chromium for elements with the + // `.native-key-bindings` class. + handleNativeKeybindings () { + const bindCommandToAction = (command, action) => { + this.subscriptions.add( + this.atomEnvironment.commands.add( + '.native-key-bindings', + command, + event => this.applicationDelegate.getCurrentWindow().webContents[action](), + false + ) + ) + } + + bindCommandToAction('core:copy', 'copy') + bindCommandToAction('core:paste', 'paste') + bindCommandToAction('core:undo', 'undo') + bindCommandToAction('core:redo', 'redo') + bindCommandToAction('core:select-all', 'selectAll') + bindCommandToAction('core:cut', 'cut') + } + + unsubscribe () { + this.subscriptions.dispose() + } + + on (target, eventName, handler) { + target.on(eventName, handler) + this.subscriptions.add(new Disposable(function () { + target.removeListener(eventName, handler) + })) + } + + addEventListener (target, eventName, handler) { + target.addEventListener(eventName, handler) + this.subscriptions.add(new Disposable(function () { + target.removeEventListener(eventName, handler) + })) + } + + handleDocumentKeyEvent (event) { + this.atomEnvironment.keymaps.handleKeyboardEvent(event) + event.stopImmediatePropagation() + } + + handleDrop (event) { + event.preventDefault() + event.stopPropagation() + } + + handleDragover (event) { + event.preventDefault() + event.stopPropagation() + event.dataTransfer.dropEffect = 'none' + } + + eachTabIndexedElement (callback) { + for (let element of this.document.querySelectorAll('[tabindex]')) { + if (element.disabled) { continue } + if (!(element.tabIndex >= 0)) { continue } + callback(element, element.tabIndex) + } + } + + handleFocusNext () { + const focusedTabIndex = this.document.activeElement.tabIndex != null ? this.document.activeElement.tabIndex : -Infinity + + let nextElement = null + let nextTabIndex = Infinity + let lowestElement = null + let lowestTabIndex = Infinity + this.eachTabIndexedElement(function (element, tabIndex) { + if (tabIndex < lowestTabIndex) { + lowestTabIndex = tabIndex + lowestElement = element + } + + if (focusedTabIndex < tabIndex && tabIndex < nextTabIndex) { + nextTabIndex = tabIndex + nextElement = element + } + }) + + if (nextElement != null) { + nextElement.focus() + } else if (lowestElement != null) { + lowestElement.focus() + } + } + + handleFocusPrevious () { + const focusedTabIndex = this.document.activeElement.tabIndex != null ? this.document.activeElement.tabIndex : Infinity + + let previousElement = null + let previousTabIndex = -Infinity + let highestElement = null + let highestTabIndex = -Infinity + this.eachTabIndexedElement(function (element, tabIndex) { + if (tabIndex > highestTabIndex) { + highestTabIndex = tabIndex + highestElement = element + } + + if (focusedTabIndex > tabIndex && tabIndex > previousTabIndex) { + previousTabIndex = tabIndex + previousElement = element + } + }) + + if (previousElement != null) { + previousElement.focus() + } else if (highestElement != null) { + highestElement.focus() + } + } + + handleWindowFocus () { + this.document.body.classList.remove('is-blurred') + } + + handleWindowBlur () { + this.document.body.classList.add('is-blurred') + this.atomEnvironment.storeWindowDimensions() + } + + handleEnterFullScreen () { + this.document.body.classList.add('fullscreen') + } + + handleLeaveFullScreen () { + this.document.body.classList.remove('fullscreen') + } + + handleWindowBeforeunload (event) { + if (!this.reloadRequested && !this.atomEnvironment.inSpecMode() && this.atomEnvironment.getCurrentWindow().isWebViewFocused()) { + this.atomEnvironment.hide() + } + this.reloadRequested = false + this.atomEnvironment.storeWindowDimensions() + this.atomEnvironment.unloadEditorWindow() + this.atomEnvironment.destroy() + } + + handleWindowToggleFullScreen () { + this.atomEnvironment.toggleFullScreen() + } + + handleWindowClose () { + this.atomEnvironment.close() + } + + handleWindowReload () { + this.reloadRequested = true + this.atomEnvironment.reload() + } + + handleWindowToggleDevTools () { + this.atomEnvironment.toggleDevTools() + } + + handleWindowToggleMenuBar () { + this.atomEnvironment.config.set('core.autoHideMenuBar', !this.atomEnvironment.config.get('core.autoHideMenuBar')) + + if (this.atomEnvironment.config.get('core.autoHideMenuBar')) { + const detail = 'To toggle, press the Alt key or execute the window:toggle-menu-bar command' + this.atomEnvironment.notifications.addInfo('Menu bar hidden', {detail}) + } + } + + handleLinkClick (event) { + event.preventDefault() + const uri = event.currentTarget && event.currentTarget.getAttribute('href') + if (uri && (uri[0] !== '#') && /^https?:\/\//.test(uri)) { + this.applicationDelegate.openExternal(uri) + } + } + + handleFormSubmit (event) { + // Prevent form submits from changing the current window's URL + event.preventDefault() + } + + handleDocumentContextmenu (event) { + event.preventDefault() + this.atomEnvironment.contextMenu.showForEvent(event) + } +} From 9fcc6a9bce21c2404bdce269e02a81f0b7a51682 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Fri, 20 Oct 2017 01:27:15 +0200 Subject: [PATCH 02/97] Use endsWith to match modules to exclude from the snapshot --- script/lib/generate-startup-snapshot.js | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/script/lib/generate-startup-snapshot.js b/script/lib/generate-startup-snapshot.js index 2905bca1b..333acdc0a 100644 --- a/script/lib/generate-startup-snapshot.js +++ b/script/lib/generate-startup-snapshot.js @@ -27,47 +27,37 @@ module.exports = function (packagedAppPath) { coreModules.has(modulePath) || (relativePath.startsWith(path.join('..', 'src')) && relativePath.endsWith('-element.js')) || relativePath.startsWith(path.join('..', 'node_modules', 'dugite')) || + relativePath.endsWith(path.join('node_modules', 'coffee-script', 'lib', 'coffee-script', 'register.js')) || + relativePath.endsWith(path.join('node_modules', 'fs-extra', 'lib', 'index.js')) || + relativePath.endsWith(path.join('node_modules', 'graceful-fs', 'graceful-fs.js')) || + relativePath.endsWith(path.join('node_modules', 'htmlparser2', 'lib', 'index.js')) || + relativePath.endsWith(path.join('node_modules', 'minimatch', 'minimatch.js')) || relativePath === path.join('..', 'exports', 'atom.js') || relativePath === path.join('..', 'src', 'electron-shims.js') || relativePath === path.join('..', 'src', 'safe-clipboard.js') || relativePath === path.join('..', 'node_modules', 'atom-keymap', 'lib', 'command-event.js') || relativePath === path.join('..', 'node_modules', 'babel-core', 'index.js') || relativePath === path.join('..', 'node_modules', 'cached-run-in-this-context', 'lib', 'main.js') || - relativePath === path.join('..', 'node_modules', 'coffee-script', 'lib', 'coffee-script', 'register.js') || - relativePath === path.join('..', 'node_modules', 'cson-parser', 'node_modules', 'coffee-script', 'lib', 'coffee-script', 'register.js') || relativePath === path.join('..', 'node_modules', 'decompress-zip', 'lib', 'decompress-zip.js') || relativePath === path.join('..', 'node_modules', 'debug', 'node.js') || - relativePath === path.join('..', 'node_modules', 'fs-extra', 'lib', 'index.js') || - relativePath === path.join('..', 'node_modules', 'github', 'node_modules', 'fs-extra', 'lib', 'index.js') || relativePath === path.join('..', 'node_modules', 'git-utils', 'src', 'git.js') || relativePath === path.join('..', 'node_modules', 'glob', 'glob.js') || - relativePath === path.join('..', 'node_modules', 'graceful-fs', 'graceful-fs.js') || - relativePath === path.join('..', 'node_modules', 'htmlparser2', 'lib', 'index.js') || - relativePath === path.join('..', 'node_modules', 'markdown-preview', 'node_modules', 'htmlparser2', 'lib', 'index.js') || - relativePath === path.join('..', 'node_modules', 'roaster', 'node_modules', 'htmlparser2', 'lib', 'index.js') || - relativePath === path.join('..', 'node_modules', 'task-lists', 'node_modules', 'htmlparser2', 'lib', 'index.js') || relativePath === path.join('..', 'node_modules', 'iconv-lite', 'lib', 'index.js') || relativePath === path.join('..', 'node_modules', 'less', 'index.js') || relativePath === path.join('..', 'node_modules', 'less', 'lib', 'less', 'fs.js') || relativePath === path.join('..', 'node_modules', 'less', 'lib', 'less-node', 'index.js') || - relativePath === path.join('..', 'node_modules', 'less', 'node_modules', 'graceful-fs', 'graceful-fs.js') || - relativePath === path.join('..', 'node_modules', 'minimatch', 'minimatch.js') || relativePath === path.join('..', 'node_modules', 'node-fetch', 'lib', 'fetch-error.js') || - relativePath === path.join('..', 'node_modules', 'nsfw', 'node_modules', 'fs-extra', 'lib', 'index.js') || relativePath === path.join('..', 'node_modules', 'superstring', 'index.js') || relativePath === path.join('..', 'node_modules', 'oniguruma', 'src', 'oniguruma.js') || relativePath === path.join('..', 'node_modules', 'request', 'index.js') || relativePath === path.join('..', 'node_modules', 'resolve', 'index.js') || relativePath === path.join('..', 'node_modules', 'resolve', 'lib', 'core.js') || - relativePath === path.join('..', 'node_modules', 'scandal', 'node_modules', 'minimatch', 'minimatch.js') || relativePath === path.join('..', 'node_modules', 'settings-view', 'node_modules', 'glob', 'glob.js') || - relativePath === path.join('..', 'node_modules', 'settings-view', 'node_modules', 'minimatch', 'minimatch.js') || relativePath === path.join('..', 'node_modules', 'spellchecker', 'lib', 'spellchecker.js') || relativePath === path.join('..', 'node_modules', 'spelling-manager', 'node_modules', 'natural', 'lib', 'natural', 'index.js') || relativePath === path.join('..', 'node_modules', 'tar', 'tar.js') || relativePath === path.join('..', 'node_modules', 'temp', 'lib', 'temp.js') || - relativePath === path.join('..', 'node_modules', 'tmp', 'lib', 'tmp.js') || - relativePath === path.join('..', 'node_modules', 'tree-view', 'node_modules', 'minimatch', 'minimatch.js') + relativePath === path.join('..', 'node_modules', 'tmp', 'lib', 'tmp.js') ) } }).then((snapshotScript) => { From 31cc7251383e50d31d291a0882394eafd783a94f Mon Sep 17 00:00:00 2001 From: Indrek Ardel Date: Fri, 14 Apr 2017 16:01:12 +0300 Subject: [PATCH 03/97] :arrow_up: coffee-script --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e5541ff0e..18dff266f 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "chai": "3.5.0", "chart.js": "^2.3.0", "clear-cut": "^2.0.2", - "coffee-script": "1.11.1", + "coffee-script": "1.12.7", "color": "^0.7.3", "dedent": "^0.6.0", "devtron": "1.3.0", From 0f89211d55cbcc5f4fdbfa6f76ed6cb709c98783 Mon Sep 17 00:00:00 2001 From: Indrek Ardel Date: Fri, 20 Oct 2017 13:34:15 +0300 Subject: [PATCH 04/97] Prioritize first line matches over bundled/non bundled cirteria --- .../packages/package-with-rb-filetype/grammars/rb.cson | 1 + spec/grammars-spec.coffee | 2 ++ src/grammar-registry.js | 4 ++-- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/spec/fixtures/packages/package-with-rb-filetype/grammars/rb.cson b/spec/fixtures/packages/package-with-rb-filetype/grammars/rb.cson index 8b4d85412..37aac3d4d 100644 --- a/spec/fixtures/packages/package-with-rb-filetype/grammars/rb.cson +++ b/spec/fixtures/packages/package-with-rb-filetype/grammars/rb.cson @@ -1,5 +1,6 @@ 'name': 'Test Ruby' 'scopeName': 'test.rb' +'firstLineMatch': '^\\#!.*(?:\\s|\\/)(?:testruby)(?:$|\\s)' 'fileTypes': [ 'rb' ] diff --git a/spec/grammars-spec.coffee b/spec/grammars-spec.coffee index 7b70797ba..db716528d 100644 --- a/spec/grammars-spec.coffee +++ b/spec/grammars-spec.coffee @@ -120,6 +120,8 @@ describe "the `grammars` global", -> atom.grammars.grammarForScopeName('source.ruby').bundledPackage = true atom.grammars.grammarForScopeName('test.rb').bundledPackage = false + expect(atom.grammars.selectGrammar('test.rb', '#!/usr/bin/env ruby').scopeName).toBe 'source.ruby' + expect(atom.grammars.selectGrammar('test.rb', '#!/usr/bin/env testruby').scopeName).toBe 'test.rb' expect(atom.grammars.selectGrammar('test.rb').scopeName).toBe 'test.rb' describe "when there is no file path", -> diff --git a/src/grammar-registry.js b/src/grammar-registry.js index b1de16ba1..f2994acf1 100644 --- a/src/grammar-registry.js +++ b/src/grammar-registry.js @@ -58,10 +58,10 @@ class GrammarRegistry extends FirstMate.GrammarRegistry { let score = this.getGrammarPathScore(grammar, filePath) if ((score > 0) && !grammar.bundledPackage) { - score += 0.25 + score += 0.125 } if (this.grammarMatchesContents(grammar, contents)) { - score += 0.125 + score += 0.25 } return score } From d23510fce97012efd1a7fa5db1a95406e8bbd4d5 Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Fri, 20 Oct 2017 08:20:40 -0400 Subject: [PATCH 05/97] =?UTF-8?q?=E2=98=A0=E2=98=95=EF=B8=8F=20Decaffeinat?= =?UTF-8?q?e=20spec/window-event-handler-spec.coffee?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spec/window-event-handler-spec.coffee | 209 ----------------------- spec/window-event-handler-spec.js | 228 ++++++++++++++++++++++++++ 2 files changed, 228 insertions(+), 209 deletions(-) delete mode 100644 spec/window-event-handler-spec.coffee create mode 100644 spec/window-event-handler-spec.js diff --git a/spec/window-event-handler-spec.coffee b/spec/window-event-handler-spec.coffee deleted file mode 100644 index 9c9f4a098..000000000 --- a/spec/window-event-handler-spec.coffee +++ /dev/null @@ -1,209 +0,0 @@ -KeymapManager = require 'atom-keymap' -TextEditor = require '../src/text-editor' -WindowEventHandler = require '../src/window-event-handler' -{ipcRenderer} = require 'electron' - -describe "WindowEventHandler", -> - [windowEventHandler] = [] - - beforeEach -> - atom.uninstallWindowEventHandler() - spyOn(atom, 'hide') - initialPath = atom.project.getPaths()[0] - spyOn(atom, 'getLoadSettings').andCallFake -> - loadSettings = atom.getLoadSettings.originalValue.call(atom) - loadSettings.initialPath = initialPath - loadSettings - atom.project.destroy() - windowEventHandler = new WindowEventHandler({atomEnvironment: atom, applicationDelegate: atom.applicationDelegate}) - windowEventHandler.initialize(window, document) - - afterEach -> - windowEventHandler.unsubscribe() - atom.installWindowEventHandler() - - describe "when the window is loaded", -> - it "doesn't have .is-blurred on the body tag", -> - return if process.platform is 'win32' #Win32TestFailures - can not steal focus - expect(document.body.className).not.toMatch("is-blurred") - - describe "when the window is blurred", -> - beforeEach -> - window.dispatchEvent(new CustomEvent('blur')) - - afterEach -> - document.body.classList.remove('is-blurred') - - it "adds the .is-blurred class on the body", -> - expect(document.body.className).toMatch("is-blurred") - - describe "when the window is focused again", -> - it "removes the .is-blurred class from the body", -> - window.dispatchEvent(new CustomEvent('focus')) - expect(document.body.className).not.toMatch("is-blurred") - - describe "window:close event", -> - it "closes the window", -> - spyOn(atom, 'close') - window.dispatchEvent(new CustomEvent('window:close')) - expect(atom.close).toHaveBeenCalled() - - describe "when a link is clicked", -> - it "opens the http/https links in an external application", -> - {shell} = require 'electron' - spyOn(shell, 'openExternal') - - link = document.createElement('a') - linkChild = document.createElement('span') - link.appendChild(linkChild) - link.href = 'http://github.com' - jasmine.attachToDOM(link) - fakeEvent = {target: linkChild, currentTarget: link, preventDefault: (->)} - - windowEventHandler.handleLinkClick(fakeEvent) - expect(shell.openExternal).toHaveBeenCalled() - expect(shell.openExternal.argsForCall[0][0]).toBe "http://github.com" - shell.openExternal.reset() - - link.href = 'https://github.com' - windowEventHandler.handleLinkClick(fakeEvent) - expect(shell.openExternal).toHaveBeenCalled() - expect(shell.openExternal.argsForCall[0][0]).toBe "https://github.com" - shell.openExternal.reset() - - link.href = '' - windowEventHandler.handleLinkClick(fakeEvent) - expect(shell.openExternal).not.toHaveBeenCalled() - shell.openExternal.reset() - - link.href = '#scroll-me' - windowEventHandler.handleLinkClick(fakeEvent) - expect(shell.openExternal).not.toHaveBeenCalled() - - describe "when a form is submitted", -> - it "prevents the default so that the window's URL isn't changed", -> - form = document.createElement('form') - jasmine.attachToDOM(form) - - defaultPrevented = false - event = new CustomEvent('submit', bubbles: true) - event.preventDefault = -> defaultPrevented = true - form.dispatchEvent(event) - expect(defaultPrevented).toBe(true) - - describe "core:focus-next and core:focus-previous", -> - describe "when there is no currently focused element", -> - it "focuses the element with the lowest/highest tabindex", -> - wrapperDiv = document.createElement('div') - wrapperDiv.innerHTML = """ -
- - -
- """ - elements = wrapperDiv.firstChild - jasmine.attachToDOM(elements) - - elements.dispatchEvent(new CustomEvent("core:focus-next", bubbles: true)) - expect(document.activeElement.tabIndex).toBe 1 - - document.body.focus() - elements.dispatchEvent(new CustomEvent("core:focus-previous", bubbles: true)) - expect(document.activeElement.tabIndex).toBe 2 - - describe "when a tabindex is set on the currently focused element", -> - it "focuses the element with the next highest/lowest tabindex, skipping disabled elements", -> - wrapperDiv = document.createElement('div') - wrapperDiv.innerHTML = """ -
- - - - - - - -
- """ - elements = wrapperDiv.firstChild - jasmine.attachToDOM(elements) - - elements.querySelector('[tabindex="1"]').focus() - - elements.dispatchEvent(new CustomEvent("core:focus-next", bubbles: true)) - expect(document.activeElement.tabIndex).toBe 2 - - elements.dispatchEvent(new CustomEvent("core:focus-next", bubbles: true)) - expect(document.activeElement.tabIndex).toBe 3 - - elements.dispatchEvent(new CustomEvent("core:focus-next", bubbles: true)) - expect(document.activeElement.tabIndex).toBe 5 - - elements.dispatchEvent(new CustomEvent("core:focus-next", bubbles: true)) - expect(document.activeElement.tabIndex).toBe 7 - - elements.dispatchEvent(new CustomEvent("core:focus-next", bubbles: true)) - expect(document.activeElement.tabIndex).toBe 1 - - elements.dispatchEvent(new CustomEvent("core:focus-previous", bubbles: true)) - expect(document.activeElement.tabIndex).toBe 7 - - elements.dispatchEvent(new CustomEvent("core:focus-previous", bubbles: true)) - expect(document.activeElement.tabIndex).toBe 5 - - elements.dispatchEvent(new CustomEvent("core:focus-previous", bubbles: true)) - expect(document.activeElement.tabIndex).toBe 3 - - elements.dispatchEvent(new CustomEvent("core:focus-previous", bubbles: true)) - expect(document.activeElement.tabIndex).toBe 2 - - elements.dispatchEvent(new CustomEvent("core:focus-previous", bubbles: true)) - expect(document.activeElement.tabIndex).toBe 1 - - elements.dispatchEvent(new CustomEvent("core:focus-previous", bubbles: true)) - expect(document.activeElement.tabIndex).toBe 7 - - describe "when keydown events occur on the document", -> - it "dispatches the event via the KeymapManager and CommandRegistry", -> - dispatchedCommands = [] - atom.commands.onWillDispatch (command) -> dispatchedCommands.push(command) - atom.commands.add '*', 'foo-command': -> - atom.keymaps.add 'source-name', '*': {'x': 'foo-command'} - - event = KeymapManager.buildKeydownEvent('x', target: document.createElement('div')) - document.dispatchEvent(event) - - expect(dispatchedCommands.length).toBe 1 - expect(dispatchedCommands[0].type).toBe 'foo-command' - - describe "native key bindings", -> - it "correctly dispatches them to active elements with the '.native-key-bindings' class", -> - webContentsSpy = jasmine.createSpyObj("webContents", ["copy", "paste"]) - spyOn(atom.applicationDelegate, "getCurrentWindow").andReturn({ - webContents: webContentsSpy - on: -> - }) - - nativeKeyBindingsInput = document.createElement("input") - nativeKeyBindingsInput.classList.add("native-key-bindings") - jasmine.attachToDOM(nativeKeyBindingsInput) - nativeKeyBindingsInput.focus() - - atom.dispatchApplicationMenuCommand("core:copy") - atom.dispatchApplicationMenuCommand("core:paste") - - expect(webContentsSpy.copy).toHaveBeenCalled() - expect(webContentsSpy.paste).toHaveBeenCalled() - - webContentsSpy.copy.reset() - webContentsSpy.paste.reset() - - normalInput = document.createElement("input") - jasmine.attachToDOM(normalInput) - normalInput.focus() - - atom.dispatchApplicationMenuCommand("core:copy") - atom.dispatchApplicationMenuCommand("core:paste") - - expect(webContentsSpy.copy).not.toHaveBeenCalled() - expect(webContentsSpy.paste).not.toHaveBeenCalled() diff --git a/spec/window-event-handler-spec.js b/spec/window-event-handler-spec.js new file mode 100644 index 000000000..a03e168fa --- /dev/null +++ b/spec/window-event-handler-spec.js @@ -0,0 +1,228 @@ +const KeymapManager = require('atom-keymap') +const WindowEventHandler = require('../src/window-event-handler') + +describe('WindowEventHandler', () => { + let windowEventHandler + + beforeEach(() => { + atom.uninstallWindowEventHandler() + spyOn(atom, 'hide') + const initialPath = atom.project.getPaths()[0] + spyOn(atom, 'getLoadSettings').andCallFake(() => { + const loadSettings = atom.getLoadSettings.originalValue.call(atom) + loadSettings.initialPath = initialPath + return loadSettings + }) + atom.project.destroy() + windowEventHandler = new WindowEventHandler({atomEnvironment: atom, applicationDelegate: atom.applicationDelegate}) + windowEventHandler.initialize(window, document) + }) + + afterEach(() => { + windowEventHandler.unsubscribe() + atom.installWindowEventHandler() + }) + + describe('when the window is loaded', () => + it("doesn't have .is-blurred on the body tag", () => { + if (process.platform === 'win32') { return } // Win32TestFailures - can not steal focus + expect(document.body.className).not.toMatch('is-blurred') + }) + ) + + describe('when the window is blurred', () => { + beforeEach(() => window.dispatchEvent(new CustomEvent('blur'))) + + afterEach(() => document.body.classList.remove('is-blurred')) + + it('adds the .is-blurred class on the body', () => expect(document.body.className).toMatch('is-blurred')) + + describe('when the window is focused again', () => + it('removes the .is-blurred class from the body', () => { + window.dispatchEvent(new CustomEvent('focus')) + expect(document.body.className).not.toMatch('is-blurred') + }) + ) + }) + + describe('window:close event', () => + it('closes the window', () => { + spyOn(atom, 'close') + window.dispatchEvent(new CustomEvent('window:close')) + expect(atom.close).toHaveBeenCalled() + }) + ) + + describe('when a link is clicked', () => + it('opens the http/https links in an external application', () => { + const {shell} = require('electron') + spyOn(shell, 'openExternal') + + const link = document.createElement('a') + const linkChild = document.createElement('span') + link.appendChild(linkChild) + link.href = 'http://github.com' + jasmine.attachToDOM(link) + const fakeEvent = {target: linkChild, currentTarget: link, preventDefault: () => {}} + + windowEventHandler.handleLinkClick(fakeEvent) + expect(shell.openExternal).toHaveBeenCalled() + expect(shell.openExternal.argsForCall[0][0]).toBe('http://github.com') + shell.openExternal.reset() + + link.href = 'https://github.com' + windowEventHandler.handleLinkClick(fakeEvent) + expect(shell.openExternal).toHaveBeenCalled() + expect(shell.openExternal.argsForCall[0][0]).toBe('https://github.com') + shell.openExternal.reset() + + link.href = '' + windowEventHandler.handleLinkClick(fakeEvent) + expect(shell.openExternal).not.toHaveBeenCalled() + shell.openExternal.reset() + + link.href = '#scroll-me' + windowEventHandler.handleLinkClick(fakeEvent) + expect(shell.openExternal).not.toHaveBeenCalled() + }) + ) + + describe('when a form is submitted', () => + it("prevents the default so that the window's URL isn't changed", () => { + const form = document.createElement('form') + jasmine.attachToDOM(form) + + let defaultPrevented = false + const event = new CustomEvent('submit', {bubbles: true}) + event.preventDefault = () => { defaultPrevented = true } + form.dispatchEvent(event) + expect(defaultPrevented).toBe(true) + }) + ) + + describe('core:focus-next and core:focus-previous', () => { + describe('when there is no currently focused element', () => + it('focuses the element with the lowest/highest tabindex', () => { + const wrapperDiv = document.createElement('div') + wrapperDiv.innerHTML = ` +
+ + +
+ `.trim() + const elements = wrapperDiv.firstChild + jasmine.attachToDOM(elements) + + elements.dispatchEvent(new CustomEvent('core:focus-next', {bubbles: true})) + expect(document.activeElement.tabIndex).toBe(1) + + document.body.focus() + elements.dispatchEvent(new CustomEvent('core:focus-previous', {bubbles: true})) + expect(document.activeElement.tabIndex).toBe(2) + }) + ) + + describe('when a tabindex is set on the currently focused element', () => + it('focuses the element with the next highest/lowest tabindex, skipping disabled elements', () => { + const wrapperDiv = document.createElement('div') + wrapperDiv.innerHTML = ` +
+ + + + + + + +
+ `.trim() + const elements = wrapperDiv.firstChild + jasmine.attachToDOM(elements) + + elements.querySelector('[tabindex="1"]').focus() + + elements.dispatchEvent(new CustomEvent('core:focus-next', {bubbles: true})) + expect(document.activeElement.tabIndex).toBe(2) + + elements.dispatchEvent(new CustomEvent('core:focus-next', {bubbles: true})) + expect(document.activeElement.tabIndex).toBe(3) + + elements.dispatchEvent(new CustomEvent('core:focus-next', {bubbles: true})) + expect(document.activeElement.tabIndex).toBe(5) + + elements.dispatchEvent(new CustomEvent('core:focus-next', {bubbles: true})) + expect(document.activeElement.tabIndex).toBe(7) + + elements.dispatchEvent(new CustomEvent('core:focus-next', {bubbles: true})) + expect(document.activeElement.tabIndex).toBe(1) + + elements.dispatchEvent(new CustomEvent('core:focus-previous', {bubbles: true})) + expect(document.activeElement.tabIndex).toBe(7) + + elements.dispatchEvent(new CustomEvent('core:focus-previous', {bubbles: true})) + expect(document.activeElement.tabIndex).toBe(5) + + elements.dispatchEvent(new CustomEvent('core:focus-previous', {bubbles: true})) + expect(document.activeElement.tabIndex).toBe(3) + + elements.dispatchEvent(new CustomEvent('core:focus-previous', {bubbles: true})) + expect(document.activeElement.tabIndex).toBe(2) + + elements.dispatchEvent(new CustomEvent('core:focus-previous', {bubbles: true})) + expect(document.activeElement.tabIndex).toBe(1) + + elements.dispatchEvent(new CustomEvent('core:focus-previous', {bubbles: true})) + expect(document.activeElement.tabIndex).toBe(7) + }) + ) + }) + + describe('when keydown events occur on the document', () => + it('dispatches the event via the KeymapManager and CommandRegistry', () => { + const dispatchedCommands = [] + atom.commands.onWillDispatch(command => dispatchedCommands.push(command)) + atom.commands.add('*', {'foo-command': () => {}}) + atom.keymaps.add('source-name', {'*': {'x': 'foo-command'}}) + + const event = KeymapManager.buildKeydownEvent('x', {target: document.createElement('div')}) + document.dispatchEvent(event) + + expect(dispatchedCommands.length).toBe(1) + expect(dispatchedCommands[0].type).toBe('foo-command') + }) + ) + + describe('native key bindings', () => + it("correctly dispatches them to active elements with the '.native-key-bindings' class", () => { + const webContentsSpy = jasmine.createSpyObj('webContents', ['copy', 'paste']) + spyOn(atom.applicationDelegate, 'getCurrentWindow').andReturn({ + webContents: webContentsSpy, + on: () => {} + }) + + const nativeKeyBindingsInput = document.createElement('input') + nativeKeyBindingsInput.classList.add('native-key-bindings') + jasmine.attachToDOM(nativeKeyBindingsInput) + nativeKeyBindingsInput.focus() + + atom.dispatchApplicationMenuCommand('core:copy') + atom.dispatchApplicationMenuCommand('core:paste') + + expect(webContentsSpy.copy).toHaveBeenCalled() + expect(webContentsSpy.paste).toHaveBeenCalled() + + webContentsSpy.copy.reset() + webContentsSpy.paste.reset() + + const normalInput = document.createElement('input') + jasmine.attachToDOM(normalInput) + normalInput.focus() + + atom.dispatchApplicationMenuCommand('core:copy') + atom.dispatchApplicationMenuCommand('core:paste') + + expect(webContentsSpy.copy).not.toHaveBeenCalled() + expect(webContentsSpy.paste).not.toHaveBeenCalled() + }) + ) +}) From d0bdbb861ba8b0234135460bd86cc968316a3e14 Mon Sep 17 00:00:00 2001 From: Justin Ratner Date: Fri, 20 Oct 2017 11:30:50 -0600 Subject: [PATCH 06/97] update overlay itself instead of text editor when resize occurs --- src/text-editor-component.js | 76 ++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 33 deletions(-) diff --git a/src/text-editor-component.js b/src/text-editor-component.js index 5ff96eec5..18f53e945 100644 --- a/src/text-editor-component.js +++ b/src/text-editor-component.js @@ -804,7 +804,12 @@ class TextEditorComponent { key: overlayProps.element, overlayComponents: this.overlayComponents, measuredDimensions: this.overlayDimensionsByElement.get(overlayProps.element), - didResize: () => { this.updateSync() } + didResize: (overlayComponent) => { + this.updateOverlayToRender(overlayProps) + overlayComponent.update({ + measuredDimensions: this.overlayDimensionsByElement.get(overlayProps.element) + }) + } }, overlayProps )) @@ -1339,42 +1344,47 @@ class TextEditorComponent { }) } + updateOverlayToRender (decoration) { + const windowInnerHeight = this.getWindowInnerHeight() + const windowInnerWidth = this.getWindowInnerWidth() + const contentClientRect = this.refs.content.getBoundingClientRect() + + const {element, screenPosition, avoidOverflow} = decoration + const {row, column} = screenPosition + let wrapperTop = contentClientRect.top + this.pixelPositionAfterBlocksForRow(row) + this.getLineHeight() + let wrapperLeft = contentClientRect.left + this.pixelLeftForRowAndColumn(row, column) + const clientRect = element.getBoundingClientRect() + this.overlayDimensionsByElement.set(element, clientRect) + + if (avoidOverflow !== false) { + const computedStyle = window.getComputedStyle(element) + const elementTop = wrapperTop + parseInt(computedStyle.marginTop) + const elementBottom = elementTop + clientRect.height + const flippedElementTop = wrapperTop - this.getLineHeight() - clientRect.height - parseInt(computedStyle.marginBottom) + const elementLeft = wrapperLeft + parseInt(computedStyle.marginLeft) + const elementRight = elementLeft + clientRect.width + + if (elementBottom > windowInnerHeight && flippedElementTop >= 0) { + wrapperTop -= (elementTop - flippedElementTop) + } + if (elementLeft < 0) { + wrapperLeft -= elementLeft + } else if (elementRight > windowInnerWidth) { + wrapperLeft -= (elementRight - windowInnerWidth) + } + } + + decoration.pixelTop = Math.round(wrapperTop) + decoration.pixelLeft = Math.round(wrapperLeft) + } + updateOverlaysToRender () { const overlayCount = this.decorationsToRender.overlays.length if (overlayCount === 0) return null - const windowInnerHeight = this.getWindowInnerHeight() - const windowInnerWidth = this.getWindowInnerWidth() - const contentClientRect = this.refs.content.getBoundingClientRect() for (let i = 0; i < overlayCount; i++) { const decoration = this.decorationsToRender.overlays[i] - const {element, screenPosition, avoidOverflow} = decoration - const {row, column} = screenPosition - let wrapperTop = contentClientRect.top + this.pixelPositionAfterBlocksForRow(row) + this.getLineHeight() - let wrapperLeft = contentClientRect.left + this.pixelLeftForRowAndColumn(row, column) - const clientRect = element.getBoundingClientRect() - this.overlayDimensionsByElement.set(element, clientRect) - - if (avoidOverflow !== false) { - const computedStyle = window.getComputedStyle(element) - const elementTop = wrapperTop + parseInt(computedStyle.marginTop) - const elementBottom = elementTop + clientRect.height - const flippedElementTop = wrapperTop - this.getLineHeight() - clientRect.height - parseInt(computedStyle.marginBottom) - const elementLeft = wrapperLeft + parseInt(computedStyle.marginLeft) - const elementRight = elementLeft + clientRect.width - - if (elementBottom > windowInnerHeight && flippedElementTop >= 0) { - wrapperTop -= (elementTop - flippedElementTop) - } - if (elementLeft < 0) { - wrapperLeft -= elementLeft - } else if (elementRight > windowInnerWidth) { - wrapperLeft -= (elementRight - windowInnerWidth) - } - } - - decoration.pixelTop = Math.round(wrapperTop) - decoration.pixelLeft = Math.round(wrapperLeft) + this.updateOverlayToRender(decoration) } } @@ -4202,7 +4212,7 @@ class OverlayComponent { const {contentRect} = entries[0] if (contentRect.width !== this.props.measuredDimensions.width || contentRect.height !== this.props.measuredDimensions.height) { this.resizeObserver.disconnect() - this.props.didResize() + this.props.didResize(this) process.nextTick(() => { this.resizeObserver.observe(this.props.element) }) } }) @@ -4217,7 +4227,7 @@ class OverlayComponent { update (newProps) { const oldProps = this.props - this.props = newProps + this.props = Object.assign({}, oldProps, newProps) if (this.props.pixelTop != null) this.element.style.top = this.props.pixelTop + 'px' if (this.props.pixelLeft != null) this.element.style.left = this.props.pixelLeft + 'px' if (newProps.className !== oldProps.className) { From 089717cbd3a8743387ee897bc4edea9afafd5db9 Mon Sep 17 00:00:00 2001 From: Justin Ratner Date: Fri, 20 Oct 2017 15:46:27 -0600 Subject: [PATCH 07/97] fix failing test --- spec/text-editor-component-spec.js | 7 +++++-- src/text-editor-component.js | 24 +++++++++++++++++++++--- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index 41d770212..d46748d91 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -1896,6 +1896,9 @@ describe('TextEditorComponent', () => { const decoration = editor.decorateMarker(marker, {type: 'overlay', item: overlayElement, class: 'a'}) await component.getNextUpdatePromise() + let overlayComponent + component.overlayComponents.forEach(c => overlayComponent = c) + const overlayWrapper = overlayElement.parentElement expect(overlayWrapper.classList.contains('a')).toBe(true) expect(overlayWrapper.getBoundingClientRect().top).toBe(clientTopForLine(component, 5)) @@ -1926,12 +1929,12 @@ describe('TextEditorComponent', () => { await setScrollTop(component, 20) expect(overlayWrapper.getBoundingClientRect().top).toBe(clientTopForLine(component, 5)) overlayElement.style.height = 60 + 'px' - await component.getNextUpdatePromise() + await overlayComponent.getNextUpdatePromise() expect(overlayWrapper.getBoundingClientRect().bottom).toBe(clientTopForLine(component, 4)) // Does not flip the overlay vertically if it would overflow the top of the window overlayElement.style.height = 80 + 'px' - await component.getNextUpdatePromise() + await overlayComponent.getNextUpdatePromise() expect(overlayWrapper.getBoundingClientRect().top).toBe(clientTopForLine(component, 5)) // Can update overlay wrapper class diff --git a/src/text-editor-component.js b/src/text-editor-component.js index 18f53e945..641cdad02 100644 --- a/src/text-editor-component.js +++ b/src/text-editor-component.js @@ -806,9 +806,12 @@ class TextEditorComponent { measuredDimensions: this.overlayDimensionsByElement.get(overlayProps.element), didResize: (overlayComponent) => { this.updateOverlayToRender(overlayProps) - overlayComponent.update({ - measuredDimensions: this.overlayDimensionsByElement.get(overlayProps.element) - }) + overlayComponent.update(Object.assign( + { + measuredDimensions: this.overlayDimensionsByElement.get(overlayProps.element) + }, + overlayProps + )) } }, overlayProps @@ -4225,6 +4228,19 @@ class OverlayComponent { this.didDetach() } + getNextUpdatePromise () { + if (!this.nextUpdatePromise) { + this.nextUpdatePromise = new Promise((resolve) => { + this.resolveNextUpdatePromise = () => { + this.nextUpdatePromise = null + this.resolveNextUpdatePromise = null + resolve() + } + }) + } + return this.nextUpdatePromise + } + update (newProps) { const oldProps = this.props this.props = Object.assign({}, oldProps, newProps) @@ -4234,6 +4250,8 @@ class OverlayComponent { if (oldProps.className != null) this.element.classList.remove(oldProps.className) if (newProps.className != null) this.element.classList.add(newProps.className) } + + if (this.resolveNextUpdatePromise) this.resolveNextUpdatePromise() } didAttach () { From cdf3be846be712d79ad901de5a531cc79e8a0bcc Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Fri, 20 Oct 2017 20:35:40 -0400 Subject: [PATCH 08/97] =?UTF-8?q?=E2=98=A0=E2=98=95=20Decaffeinate=20src/v?= =?UTF-8?q?iew-registry.coffee?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/view-registry.coffee | 201 ------------------------------- src/view-registry.js | 253 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 253 insertions(+), 201 deletions(-) delete mode 100644 src/view-registry.coffee create mode 100644 src/view-registry.js diff --git a/src/view-registry.coffee b/src/view-registry.coffee deleted file mode 100644 index f300cc031..000000000 --- a/src/view-registry.coffee +++ /dev/null @@ -1,201 +0,0 @@ -Grim = require 'grim' -{Disposable} = require 'event-kit' -_ = require 'underscore-plus' - -AnyConstructor = Symbol('any-constructor') - -# Essential: `ViewRegistry` handles the association between model and view -# types in Atom. We call this association a View Provider. As in, for a given -# model, this class can provide a view via {::getView}, as long as the -# model/view association was registered via {::addViewProvider} -# -# If you're adding your own kind of pane item, a good strategy for all but the -# simplest items is to separate the model and the view. The model handles -# application logic and is the primary point of API interaction. The view -# just handles presentation. -# -# Note: Models can be any object, but must implement a `getTitle()` function -# if they are to be displayed in a {Pane} -# -# View providers inform the workspace how your model objects should be -# presented in the DOM. A view provider must always return a DOM node, which -# makes [HTML 5 custom elements](http://www.html5rocks.com/en/tutorials/webcomponents/customelements/) -# an ideal tool for implementing views in Atom. -# -# You can access the `ViewRegistry` object via `atom.views`. -module.exports = -class ViewRegistry - animationFrameRequest: null - documentReadInProgress: false - - constructor: (@atomEnvironment) -> - @clear() - - clear: -> - @views = new WeakMap - @providers = [] - @clearDocumentRequests() - - # Essential: Add a provider that will be used to construct views in the - # workspace's view layer based on model objects in its model layer. - # - # ## Examples - # - # Text editors are divided into a model and a view layer, so when you interact - # with methods like `atom.workspace.getActiveTextEditor()` you're only going - # to get the model object. We display text editors on screen by teaching the - # workspace what view constructor it should use to represent them: - # - # ```coffee - # atom.views.addViewProvider TextEditor, (textEditor) -> - # textEditorElement = new TextEditorElement - # textEditorElement.initialize(textEditor) - # textEditorElement - # ``` - # - # * `modelConstructor` (optional) Constructor {Function} for your model. If - # a constructor is given, the `createView` function will only be used - # for model objects inheriting from that constructor. Otherwise, it will - # will be called for any object. - # * `createView` Factory {Function} that is passed an instance of your model - # and must return a subclass of `HTMLElement` or `undefined`. If it returns - # `undefined`, then the registry will continue to search for other view - # providers. - # - # Returns a {Disposable} on which `.dispose()` can be called to remove the - # added provider. - addViewProvider: (modelConstructor, createView) -> - if arguments.length is 1 - switch typeof modelConstructor - when 'function' - provider = {createView: modelConstructor, modelConstructor: AnyConstructor} - when 'object' - Grim.deprecate("atom.views.addViewProvider now takes 2 arguments: a model constructor and a createView function. See docs for details.") - provider = modelConstructor - else - throw new TypeError("Arguments to addViewProvider must be functions") - else - provider = {modelConstructor, createView} - - @providers.push(provider) - new Disposable => - @providers = @providers.filter (p) -> p isnt provider - - getViewProviderCount: -> - @providers.length - - # Essential: Get the view associated with an object in the workspace. - # - # If you're just *using* the workspace, you shouldn't need to access the view - # layer, but view layer access may be necessary if you want to perform DOM - # manipulation that isn't supported via the model API. - # - # ## View Resolution Algorithm - # - # The view associated with the object is resolved using the following - # sequence - # - # 1. Is the object an instance of `HTMLElement`? If true, return the object. - # 2. Does the object have a method named `getElement` that returns an - # instance of `HTMLElement`? If true, return that value. - # 3. Does the object have a property named `element` with a value which is - # an instance of `HTMLElement`? If true, return the property value. - # 4. Is the object a jQuery object, indicated by the presence of a `jquery` - # property? If true, return the root DOM element (i.e. `object[0]`). - # 5. Has a view provider been registered for the object? If true, use the - # provider to create a view associated with the object, and return the - # view. - # - # If no associated view is returned by the sequence an error is thrown. - # - # Returns a DOM element. - getView: (object) -> - return unless object? - - if view = @views.get(object) - view - else - view = @createView(object) - @views.set(object, view) - view - - createView: (object) -> - if object instanceof HTMLElement - return object - - if typeof object?.getElement is 'function' - element = object.getElement() - if element instanceof HTMLElement - return element - - if object?.element instanceof HTMLElement - return object.element - - if object?.jquery - return object[0] - - for provider in @providers - if provider.modelConstructor is AnyConstructor - if element = provider.createView(object, @atomEnvironment) - return element - continue - - if object instanceof provider.modelConstructor - if element = provider.createView?(object, @atomEnvironment) - return element - - if viewConstructor = provider.viewConstructor - element = new viewConstructor - element.initialize?(object) ? element.setModel?(object) - return element - - if viewConstructor = object?.getViewClass?() - view = new viewConstructor(object) - return view[0] - - throw new Error("Can't create a view for #{object.constructor.name} instance. Please register a view provider.") - - updateDocument: (fn) -> - @documentWriters.push(fn) - @requestDocumentUpdate() unless @documentReadInProgress - new Disposable => - @documentWriters = @documentWriters.filter (writer) -> writer isnt fn - - readDocument: (fn) -> - @documentReaders.push(fn) - @requestDocumentUpdate() - new Disposable => - @documentReaders = @documentReaders.filter (reader) -> reader isnt fn - - getNextUpdatePromise: -> - @nextUpdatePromise ?= new Promise (resolve) => - @resolveNextUpdatePromise = resolve - - clearDocumentRequests: -> - @documentReaders = [] - @documentWriters = [] - @nextUpdatePromise = null - @resolveNextUpdatePromise = null - if @animationFrameRequest? - cancelAnimationFrame(@animationFrameRequest) - @animationFrameRequest = null - - requestDocumentUpdate: -> - @animationFrameRequest ?= requestAnimationFrame(@performDocumentUpdate) - - performDocumentUpdate: => - resolveNextUpdatePromise = @resolveNextUpdatePromise - @animationFrameRequest = null - @nextUpdatePromise = null - @resolveNextUpdatePromise = null - - writer() while writer = @documentWriters.shift() - - @documentReadInProgress = true - reader() while reader = @documentReaders.shift() - @documentReadInProgress = false - - # process updates requested as a result of reads - writer() while writer = @documentWriters.shift() - - resolveNextUpdatePromise?() diff --git a/src/view-registry.js b/src/view-registry.js new file mode 100644 index 000000000..d3167cdc1 --- /dev/null +++ b/src/view-registry.js @@ -0,0 +1,253 @@ +const Grim = require('grim') +const {Disposable} = require('event-kit') + +const AnyConstructor = Symbol('any-constructor') + +// Essential: `ViewRegistry` handles the association between model and view +// types in Atom. We call this association a View Provider. As in, for a given +// model, this class can provide a view via {::getView}, as long as the +// model/view association was registered via {::addViewProvider} +// +// If you're adding your own kind of pane item, a good strategy for all but the +// simplest items is to separate the model and the view. The model handles +// application logic and is the primary point of API interaction. The view +// just handles presentation. +// +// Note: Models can be any object, but must implement a `getTitle()` function +// if they are to be displayed in a {Pane} +// +// View providers inform the workspace how your model objects should be +// presented in the DOM. A view provider must always return a DOM node, which +// makes [HTML 5 custom elements](http://www.html5rocks.com/en/tutorials/webcomponents/customelements/) +// an ideal tool for implementing views in Atom. +// +// You can access the `ViewRegistry` object via `atom.views`. +module.exports = +class ViewRegistry { + constructor (atomEnvironment) { + this.animationFrameRequest = null + this.documentReadInProgress = false + this.performDocumentUpdate = this.performDocumentUpdate.bind(this) + this.atomEnvironment = atomEnvironment + this.clear() + } + + clear () { + this.views = new WeakMap() + this.providers = [] + this.clearDocumentRequests() + } + + // Essential: Add a provider that will be used to construct views in the + // workspace's view layer based on model objects in its model layer. + // + // ## Examples + // + // Text editors are divided into a model and a view layer, so when you interact + // with methods like `atom.workspace.getActiveTextEditor()` you're only going + // to get the model object. We display text editors on screen by teaching the + // workspace what view constructor it should use to represent them: + // + // ```coffee + // atom.views.addViewProvider TextEditor, (textEditor) -> + // textEditorElement = new TextEditorElement + // textEditorElement.initialize(textEditor) + // textEditorElement + // ``` + // + // * `modelConstructor` (optional) Constructor {Function} for your model. If + // a constructor is given, the `createView` function will only be used + // for model objects inheriting from that constructor. Otherwise, it will + // will be called for any object. + // * `createView` Factory {Function} that is passed an instance of your model + // and must return a subclass of `HTMLElement` or `undefined`. If it returns + // `undefined`, then the registry will continue to search for other view + // providers. + // + // Returns a {Disposable} on which `.dispose()` can be called to remove the + // added provider. + addViewProvider (modelConstructor, createView) { + let provider + if (arguments.length === 1) { + switch (typeof modelConstructor) { + case 'function': + provider = {createView: modelConstructor, modelConstructor: AnyConstructor} + break + case 'object': + Grim.deprecate('atom.views.addViewProvider now takes 2 arguments: a model constructor and a createView function. See docs for details.') + provider = modelConstructor + break + default: + throw new TypeError('Arguments to addViewProvider must be functions') + } + } else { + provider = {modelConstructor, createView} + } + + this.providers.push(provider) + return new Disposable(() => { + this.providers = this.providers.filter(p => p !== provider) + }) + } + + getViewProviderCount () { + return this.providers.length + } + + // Essential: Get the view associated with an object in the workspace. + // + // If you're just *using* the workspace, you shouldn't need to access the view + // layer, but view layer access may be necessary if you want to perform DOM + // manipulation that isn't supported via the model API. + // + // ## View Resolution Algorithm + // + // The view associated with the object is resolved using the following + // sequence + // + // 1. Is the object an instance of `HTMLElement`? If true, return the object. + // 2. Does the object have a method named `getElement` that returns an + // instance of `HTMLElement`? If true, return that value. + // 3. Does the object have a property named `element` with a value which is + // an instance of `HTMLElement`? If true, return the property value. + // 4. Is the object a jQuery object, indicated by the presence of a `jquery` + // property? If true, return the root DOM element (i.e. `object[0]`). + // 5. Has a view provider been registered for the object? If true, use the + // provider to create a view associated with the object, and return the + // view. + // + // If no associated view is returned by the sequence an error is thrown. + // + // Returns a DOM element. + getView (object) { + if (object == null) { return } + + let view + if (view = this.views.get(object)) { + return view + } else { + view = this.createView(object) + this.views.set(object, view) + return view + } + } + + createView (object) { + if (object instanceof HTMLElement) { return object } + + let element + if (object && (typeof object.getElement === 'function')) { + element = object.getElement() + if (element instanceof HTMLElement) { + return element + } + } + + if (object && object.element instanceof HTMLElement) { + return object.element + } + + if (object && object.jquery) { + return object[0] + } + + let viewConstructor + for (let provider of this.providers) { + if (provider.modelConstructor === AnyConstructor) { + if (element = provider.createView(object, this.atomEnvironment)) { + return element + } + continue + } + + if (object instanceof provider.modelConstructor) { + if (element = provider.createView && provider.createView(object, this.atomEnvironment)) { + return element + } + + if (viewConstructor = provider.viewConstructor) { + element = new viewConstructor() + if (element.initialize) { + element.initialize(object) + } else if (element.setModel) { + element.setModel(object) + } + return element + } + } + } + + if (object && object.getViewClass) { + viewConstructor = object.getViewClass() + if (viewConstructor) { + const view = new viewConstructor(object) + return view[0] + } + } + + throw new Error(`Can't create a view for ${object.constructor.name} instance. Please register a view provider.`) + } + + updateDocument (fn) { + this.documentWriters.push(fn) + if (!this.documentReadInProgress) { this.requestDocumentUpdate() } + return new Disposable(() => { + this.documentWriters = this.documentWriters.filter(writer => writer !== fn) + }) + } + + readDocument (fn) { + this.documentReaders.push(fn) + this.requestDocumentUpdate() + return new Disposable(() => { + this.documentReaders = this.documentReaders.filter(reader => reader !== fn) + }) + } + + getNextUpdatePromise () { + if (this.nextUpdatePromise == null) { + this.nextUpdatePromise = new Promise(resolve => { + this.resolveNextUpdatePromise = resolve + }) + } + + return this.nextUpdatePromise + } + + clearDocumentRequests () { + this.documentReaders = [] + this.documentWriters = [] + this.nextUpdatePromise = null + this.resolveNextUpdatePromise = null + if (this.animationFrameRequest != null) { + cancelAnimationFrame(this.animationFrameRequest) + this.animationFrameRequest = null + } + } + + requestDocumentUpdate () { + if (this.animationFrameRequest == null) { + this.animationFrameRequest = requestAnimationFrame(this.performDocumentUpdate) + } + } + + performDocumentUpdate () { + const { resolveNextUpdatePromise } = this + this.animationFrameRequest = null + this.nextUpdatePromise = null + this.resolveNextUpdatePromise = null + + let writer + while ((writer = this.documentWriters.shift())) { writer() } + + let reader + this.documentReadInProgress = true + while ((reader = this.documentReaders.shift())) { reader() } + this.documentReadInProgress = false + + // process updates requested as a result of reads + while ((writer = this.documentWriters.shift())) { writer() } + + if (resolveNextUpdatePromise) { resolveNextUpdatePromise() } + } +} From a67272e6fff6094167b0d7bf474973db92bf0e4e Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Fri, 20 Oct 2017 21:12:53 -0400 Subject: [PATCH 09/97] =?UTF-8?q?=F0=9F=91=94=20Fix=20"Expected=20a=20cond?= =?UTF-8?q?itional=20expression=20&=20instead=20saw=20an=20assignment"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/view-registry.js | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/view-registry.js b/src/view-registry.js index d3167cdc1..37849f999 100644 --- a/src/view-registry.js +++ b/src/view-registry.js @@ -122,14 +122,12 @@ class ViewRegistry { getView (object) { if (object == null) { return } - let view - if (view = this.views.get(object)) { - return view - } else { + let view = this.views.get(object) + if (!view) { view = this.createView(object) this.views.set(object, view) - return view } + return view } createView (object) { @@ -154,18 +152,17 @@ class ViewRegistry { let viewConstructor for (let provider of this.providers) { if (provider.modelConstructor === AnyConstructor) { - if (element = provider.createView(object, this.atomEnvironment)) { - return element - } + element = provider.createView(object, this.atomEnvironment) + if (element) { return element } continue } if (object instanceof provider.modelConstructor) { - if (element = provider.createView && provider.createView(object, this.atomEnvironment)) { - return element - } + element = provider.createView && provider.createView(object, this.atomEnvironment) + if (element) { return element } - if (viewConstructor = provider.viewConstructor) { + viewConstructor = provider.viewConstructor + if (viewConstructor) { element = new viewConstructor() if (element.initialize) { element.initialize(object) From dfd1332a016a8af542d2a4f75a14f40341e533db Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Fri, 20 Oct 2017 21:16:28 -0400 Subject: [PATCH 10/97] =?UTF-8?q?=F0=9F=91=94=20Fix=20"A=20constructor=20n?= =?UTF-8?q?ame=20should=20not=20start=20with=20a=20lowercase=20letter"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/view-registry.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/view-registry.js b/src/view-registry.js index 37849f999..dcc1624fc 100644 --- a/src/view-registry.js +++ b/src/view-registry.js @@ -149,7 +149,6 @@ class ViewRegistry { return object[0] } - let viewConstructor for (let provider of this.providers) { if (provider.modelConstructor === AnyConstructor) { element = provider.createView(object, this.atomEnvironment) @@ -161,9 +160,9 @@ class ViewRegistry { element = provider.createView && provider.createView(object, this.atomEnvironment) if (element) { return element } - viewConstructor = provider.viewConstructor - if (viewConstructor) { - element = new viewConstructor() + let ViewConstructor = provider.viewConstructor + if (ViewConstructor) { + element = new ViewConstructor() if (element.initialize) { element.initialize(object) } else if (element.setModel) { @@ -175,9 +174,9 @@ class ViewRegistry { } if (object && object.getViewClass) { - viewConstructor = object.getViewClass() - if (viewConstructor) { - const view = new viewConstructor(object) + let ViewConstructor = object.getViewClass() + if (ViewConstructor) { + const view = new ViewConstructor(object) return view[0] } } From c6d438c5092eb42eae45ca50dbba9dfb5cb950a1 Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Sat, 21 Oct 2017 09:52:59 -0400 Subject: [PATCH 11/97] =?UTF-8?q?=E2=98=A0=E2=98=95=20Decaffeinate=20spec/?= =?UTF-8?q?view-registry-spec.coffee?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spec/view-registry-spec.coffee | 163 ------------------------ spec/view-registry-spec.js | 218 +++++++++++++++++++++++++++++++++ 2 files changed, 218 insertions(+), 163 deletions(-) delete mode 100644 spec/view-registry-spec.coffee create mode 100644 spec/view-registry-spec.js diff --git a/spec/view-registry-spec.coffee b/spec/view-registry-spec.coffee deleted file mode 100644 index 4bae1d811..000000000 --- a/spec/view-registry-spec.coffee +++ /dev/null @@ -1,163 +0,0 @@ -ViewRegistry = require '../src/view-registry' - -describe "ViewRegistry", -> - registry = null - - beforeEach -> - registry = new ViewRegistry - - afterEach -> - registry.clearDocumentRequests() - - describe "::getView(object)", -> - describe "when passed a DOM node", -> - it "returns the given DOM node", -> - node = document.createElement('div') - expect(registry.getView(node)).toBe node - - describe "when passed an object with an element property", -> - it "returns the element property if it's an instance of HTMLElement", -> - class TestComponent - constructor: -> @element = document.createElement('div') - - component = new TestComponent - expect(registry.getView(component)).toBe component.element - - describe "when passed an object with a getElement function", -> - it "returns the return value of getElement if it's an instance of HTMLElement", -> - class TestComponent - getElement: -> - @myElement ?= document.createElement('div') - - component = new TestComponent - expect(registry.getView(component)).toBe component.myElement - - describe "when passed a model object", -> - describe "when a view provider is registered matching the object's constructor", -> - it "constructs a view element and assigns the model on it", -> - class TestModel - - class TestModelSubclass extends TestModel - - class TestView - initialize: (@model) -> this - - model = new TestModel - - registry.addViewProvider TestModel, (model) -> - new TestView().initialize(model) - - view = registry.getView(model) - expect(view instanceof TestView).toBe true - expect(view.model).toBe model - - subclassModel = new TestModelSubclass - view2 = registry.getView(subclassModel) - expect(view2 instanceof TestView).toBe true - expect(view2.model).toBe subclassModel - - describe "when a view provider is registered generically, and works with the object", -> - it "constructs a view element and assigns the model on it", -> - model = {a: 'b'} - - registry.addViewProvider (model) -> - if model.a is 'b' - element = document.createElement('div') - element.className = 'test-element' - element - - view = registry.getView({a: 'b'}) - expect(view.className).toBe 'test-element' - - expect(-> registry.getView({a: 'c'})).toThrow() - - describe "when no view provider is registered for the object's constructor", -> - it "throws an exception", -> - expect(-> registry.getView(new Object)).toThrow() - - describe "::addViewProvider(providerSpec)", -> - it "returns a disposable that can be used to remove the provider", -> - class TestModel - class TestView - initialize: (@model) -> this - - disposable = registry.addViewProvider TestModel, (model) -> - new TestView().initialize(model) - - expect(registry.getView(new TestModel) instanceof TestView).toBe true - disposable.dispose() - expect(-> registry.getView(new TestModel)).toThrow() - - describe "::updateDocument(fn) and ::readDocument(fn)", -> - frameRequests = null - - beforeEach -> - frameRequests = [] - spyOn(window, 'requestAnimationFrame').andCallFake (fn) -> frameRequests.push(fn) - - it "performs all pending writes before all pending reads on the next animation frame", -> - events = [] - - registry.updateDocument -> events.push('write 1') - registry.readDocument -> events.push('read 1') - registry.readDocument -> events.push('read 2') - registry.updateDocument -> events.push('write 2') - - expect(events).toEqual [] - - expect(frameRequests.length).toBe 1 - frameRequests[0]() - expect(events).toEqual ['write 1', 'write 2', 'read 1', 'read 2'] - - frameRequests = [] - events = [] - disposable = registry.updateDocument -> events.push('write 3') - registry.updateDocument -> events.push('write 4') - registry.readDocument -> events.push('read 3') - - disposable.dispose() - - expect(frameRequests.length).toBe 1 - frameRequests[0]() - expect(events).toEqual ['write 4', 'read 3'] - - it "performs writes requested from read callbacks in the same animation frame", -> - spyOn(window, 'setInterval').andCallFake(fakeSetInterval) - spyOn(window, 'clearInterval').andCallFake(fakeClearInterval) - events = [] - - registry.updateDocument -> events.push('write 1') - registry.readDocument -> - registry.updateDocument -> events.push('write from read 1') - events.push('read 1') - registry.readDocument -> - registry.updateDocument -> events.push('write from read 2') - events.push('read 2') - registry.updateDocument -> events.push('write 2') - - expect(frameRequests.length).toBe 1 - frameRequests[0]() - expect(frameRequests.length).toBe 1 - - expect(events).toEqual [ - 'write 1' - 'write 2' - 'read 1' - 'read 2' - 'write from read 1' - 'write from read 2' - ] - - describe "::getNextUpdatePromise()", -> - it "returns a promise that resolves at the end of the next update cycle", -> - updateCalled = false - readCalled = false - - waitsFor 'getNextUpdatePromise to resolve', (done) -> - registry.getNextUpdatePromise().then -> - expect(updateCalled).toBe true - expect(readCalled).toBe true - done() - - registry.updateDocument -> updateCalled = true - registry.readDocument -> readCalled = true diff --git a/spec/view-registry-spec.js b/spec/view-registry-spec.js new file mode 100644 index 000000000..984d30718 --- /dev/null +++ b/spec/view-registry-spec.js @@ -0,0 +1,218 @@ +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const ViewRegistry = require('../src/view-registry') + +describe('ViewRegistry', () => { + let registry = null + + beforeEach(() => { + registry = new ViewRegistry() + }) + + afterEach(() => { + registry.clearDocumentRequests() + }) + + describe('::getView(object)', () => { + describe('when passed a DOM node', () => + it('returns the given DOM node', () => { + const node = document.createElement('div') + expect(registry.getView(node)).toBe(node) + }) + ) + + describe('when passed an object with an element property', () => + it("returns the element property if it's an instance of HTMLElement", () => { + class TestComponent { + constructor () { + this.element = document.createElement('div') + } + } + + const component = new TestComponent() + expect(registry.getView(component)).toBe(component.element) + }) + ) + + describe('when passed an object with a getElement function', () => + it("returns the return value of getElement if it's an instance of HTMLElement", () => { + class TestComponent { + getElement () { + if (this.myElement == null) { + this.myElement = document.createElement('div') + } + return this.myElement + } + } + + const component = new TestComponent() + expect(registry.getView(component)).toBe(component.myElement) + }) + ) + + describe('when passed a model object', () => { + describe("when a view provider is registered matching the object's constructor", () => + it('constructs a view element and assigns the model on it', () => { + class TestModel {} + + class TestModelSubclass extends TestModel {} + + class TestView { + initialize (model) { + this.model = model + return this + } + } + + const model = new TestModel() + + registry.addViewProvider(TestModel, (model) => + new TestView().initialize(model) + ) + + const view = registry.getView(model) + expect(view instanceof TestView).toBe(true) + expect(view.model).toBe(model) + + const subclassModel = new TestModelSubclass() + const view2 = registry.getView(subclassModel) + expect(view2 instanceof TestView).toBe(true) + expect(view2.model).toBe(subclassModel) + }) + ) + + describe('when a view provider is registered generically, and works with the object', () => + it('constructs a view element and assigns the model on it', () => { + const model = {a: 'b'} + + registry.addViewProvider((model) => { + if (model.a === 'b') { + const element = document.createElement('div') + element.className = 'test-element' + return element + } + }) + + const view = registry.getView({a: 'b'}) + expect(view.className).toBe('test-element') + + expect(() => registry.getView({a: 'c'})).toThrow() + }) + ) + + describe("when no view provider is registered for the object's constructor", () => + it('throws an exception', () => { + expect(() => registry.getView(new Object())).toThrow() + }) + ) + }) + }) + + describe('::addViewProvider(providerSpec)', () => + it('returns a disposable that can be used to remove the provider', () => { + class TestModel {} + class TestView { + initialize (model) { + this.model = model + return this + } + } + + const disposable = registry.addViewProvider(TestModel, (model) => + new TestView().initialize(model) + ) + + expect(registry.getView(new TestModel()) instanceof TestView).toBe(true) + disposable.dispose() + expect(() => registry.getView(new TestModel())).toThrow() + }) + ) + + describe('::updateDocument(fn) and ::readDocument(fn)', () => { + let frameRequests = null + + beforeEach(() => { + frameRequests = [] + spyOn(window, 'requestAnimationFrame').andCallFake(fn => frameRequests.push(fn)) + }) + + it('performs all pending writes before all pending reads on the next animation frame', () => { + let events = [] + + registry.updateDocument(() => events.push('write 1')) + registry.readDocument(() => events.push('read 1')) + registry.readDocument(() => events.push('read 2')) + registry.updateDocument(() => events.push('write 2')) + + expect(events).toEqual([]) + + expect(frameRequests.length).toBe(1) + frameRequests[0]() + expect(events).toEqual(['write 1', 'write 2', 'read 1', 'read 2']) + + frameRequests = [] + events = [] + const disposable = registry.updateDocument(() => events.push('write 3')) + registry.updateDocument(() => events.push('write 4')) + registry.readDocument(() => events.push('read 3')) + + disposable.dispose() + + expect(frameRequests.length).toBe(1) + frameRequests[0]() + expect(events).toEqual(['write 4', 'read 3']) + }) + + it('performs writes requested from read callbacks in the same animation frame', () => { + spyOn(window, 'setInterval').andCallFake(fakeSetInterval) + spyOn(window, 'clearInterval').andCallFake(fakeClearInterval) + const events = [] + + registry.updateDocument(() => events.push('write 1')) + registry.readDocument(() => { + registry.updateDocument(() => events.push('write from read 1')) + events.push('read 1') + }) + registry.readDocument(() => { + registry.updateDocument(() => events.push('write from read 2')) + events.push('read 2') + }) + registry.updateDocument(() => events.push('write 2')) + + expect(frameRequests.length).toBe(1) + frameRequests[0]() + expect(frameRequests.length).toBe(1) + + expect(events).toEqual([ + 'write 1', + 'write 2', + 'read 1', + 'read 2', + 'write from read 1', + 'write from read 2' + ]) + }) + }) + + describe('::getNextUpdatePromise()', () => + it('returns a promise that resolves at the end of the next update cycle', () => { + let updateCalled = false + let readCalled = false + + waitsFor('getNextUpdatePromise to resolve', (done) => { + registry.getNextUpdatePromise().then(() => { + expect(updateCalled).toBe(true) + expect(readCalled).toBe(true) + done() + }) + + registry.updateDocument(() => updateCalled = true) + registry.readDocument(() => readCalled = true) + }) + }) + ) +}) From 9a6f4b1647a6237c587bdaeb72585a204305bbe2 Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Sat, 21 Oct 2017 10:05:57 -0400 Subject: [PATCH 12/97] =?UTF-8?q?=F0=9F=91=94=20Fix=20"'model'=20is=20assi?= =?UTF-8?q?gned=20a=20value=20but=20never=20used"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spec/view-registry-spec.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/view-registry-spec.js b/spec/view-registry-spec.js index 984d30718..4459af10c 100644 --- a/spec/view-registry-spec.js +++ b/spec/view-registry-spec.js @@ -87,8 +87,6 @@ describe('ViewRegistry', () => { describe('when a view provider is registered generically, and works with the object', () => it('constructs a view element and assigns the model on it', () => { - const model = {a: 'b'} - registry.addViewProvider((model) => { if (model.a === 'b') { const element = document.createElement('div') From 33aea760588e5126d9c5982c70569b9fc8b85f50 Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Sat, 21 Oct 2017 10:07:13 -0400 Subject: [PATCH 13/97] =?UTF-8?q?=F0=9F=91=94=20Fix=20"The=20object=20lite?= =?UTF-8?q?ral=20notation=20{}=20is=20preferrable"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spec/view-registry-spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/view-registry-spec.js b/spec/view-registry-spec.js index 4459af10c..d29c627bd 100644 --- a/spec/view-registry-spec.js +++ b/spec/view-registry-spec.js @@ -104,7 +104,7 @@ describe('ViewRegistry', () => { describe("when no view provider is registered for the object's constructor", () => it('throws an exception', () => { - expect(() => registry.getView(new Object())).toThrow() + expect(() => registry.getView({})).toThrow() }) ) }) From 01e7faa988761581392f134ebc64d9c1793b321c Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Sat, 21 Oct 2017 10:10:06 -0400 Subject: [PATCH 14/97] =?UTF-8?q?=F0=9F=91=94=20Fix=20"Arrow=20function=20?= =?UTF-8?q?should=20not=20return=20assignment"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spec/view-registry-spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/view-registry-spec.js b/spec/view-registry-spec.js index d29c627bd..db8b077f1 100644 --- a/spec/view-registry-spec.js +++ b/spec/view-registry-spec.js @@ -208,8 +208,8 @@ describe('ViewRegistry', () => { done() }) - registry.updateDocument(() => updateCalled = true) - registry.readDocument(() => readCalled = true) + registry.updateDocument(() => { updateCalled = true }) + registry.readDocument(() => { readCalled = true }) }) }) ) From 0511c0ae4a5f18b81b3c64ad31f15dbfd8ba3a38 Mon Sep 17 00:00:00 2001 From: Indrek Ardel Date: Mon, 23 Oct 2017 04:11:23 +0300 Subject: [PATCH 15/97] Remove unused argument --- src/text-editor-registry.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/text-editor-registry.js b/src/text-editor-registry.js index 2cbf3093c..d891a5868 100644 --- a/src/text-editor-registry.js +++ b/src/text-editor-registry.js @@ -288,7 +288,7 @@ export default class TextEditorRegistry { let currentScore = this.editorGrammarScores.get(editor) if (currentScore == null || score > currentScore) { - editor.setGrammar(grammar, score) + editor.setGrammar(grammar) this.editorGrammarScores.set(editor, score) } } From 7b76ee3f2593e48ab86fca7e1c602332b4bc8cf7 Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Mon, 23 Oct 2017 08:47:30 -0400 Subject: [PATCH 16/97] =?UTF-8?q?=E2=98=A0=E2=98=95=20Decaffeinate=20src/t?= =?UTF-8?q?ooltip-manager.coffee?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apply results of running: $ decaffeinate --keep-commonjs --prefer-const --loose-default-params --loose-for-expressions --loose-for-of --loose-includes src/tooltip-manager.coffee src/tooltip-manager.coffee → src/tooltip-manager.js $ standard --fix src/tooltip-manager.js src/tooltip-manager.js:210:25: Unnecessary escape character: \". src/tooltip-manager.js:210:36: Unnecessary escape character: \". --- src/tooltip-manager.coffee | 176 ------------------------------ src/tooltip-manager.js | 212 +++++++++++++++++++++++++++++++++++++ 2 files changed, 212 insertions(+), 176 deletions(-) delete mode 100644 src/tooltip-manager.coffee create mode 100644 src/tooltip-manager.js diff --git a/src/tooltip-manager.coffee b/src/tooltip-manager.coffee deleted file mode 100644 index 1a9b6fe44..000000000 --- a/src/tooltip-manager.coffee +++ /dev/null @@ -1,176 +0,0 @@ -_ = require 'underscore-plus' -{Disposable, CompositeDisposable} = require 'event-kit' -Tooltip = null - -# Essential: Associates tooltips with HTML elements. -# -# You can get the `TooltipManager` via `atom.tooltips`. -# -# ## Examples -# -# The essence of displaying a tooltip -# -# ```coffee -# # display it -# disposable = atom.tooltips.add(div, {title: 'This is a tooltip'}) -# -# # remove it -# disposable.dispose() -# ``` -# -# In practice there are usually multiple tooltips. So we add them to a -# CompositeDisposable -# -# ```coffee -# {CompositeDisposable} = require 'atom' -# subscriptions = new CompositeDisposable -# -# div1 = document.createElement('div') -# div2 = document.createElement('div') -# subscriptions.add atom.tooltips.add(div1, {title: 'This is a tooltip'}) -# subscriptions.add atom.tooltips.add(div2, {title: 'Another tooltip'}) -# -# # remove them all -# subscriptions.dispose() -# ``` -# -# You can display a key binding in the tooltip as well with the -# `keyBindingCommand` option. -# -# ```coffee -# disposable = atom.tooltips.add @caseOptionButton, -# title: "Match Case" -# keyBindingCommand: 'find-and-replace:toggle-case-option' -# keyBindingTarget: @findEditor.element -# ``` -module.exports = -class TooltipManager - defaults: - trigger: 'hover' - container: 'body' - html: true - placement: 'auto top' - viewportPadding: 2 - - hoverDefaults: - {delay: {show: 1000, hide: 100}} - - constructor: ({@keymapManager, @viewRegistry}) -> - @tooltips = new Map() - - # Essential: Add a tooltip to the given element. - # - # * `target` An `HTMLElement` - # * `options` An object with one or more of the following options: - # * `title` A {String} or {Function} to use for the text in the tip. If - # a function is passed, `this` will be set to the `target` element. This - # option is mutually exclusive with the `item` option. - # * `html` A {Boolean} affecting the interpretation of the `title` option. - # If `true` (the default), the `title` string will be interpreted as HTML. - # Otherwise it will be interpreted as plain text. - # * `item` A view (object with an `.element` property) or a DOM element - # containing custom content for the tooltip. This option is mutually - # exclusive with the `title` option. - # * `class` A {String} with a class to apply to the tooltip element to - # enable custom styling. - # * `placement` A {String} or {Function} returning a string to indicate - # the position of the tooltip relative to `element`. Can be `'top'`, - # `'bottom'`, `'left'`, `'right'`, or `'auto'`. When `'auto'` is - # specified, it will dynamically reorient the tooltip. For example, if - # placement is `'auto left'`, the tooltip will display to the left when - # possible, otherwise it will display right. - # When a function is used to determine the placement, it is called with - # the tooltip DOM node as its first argument and the triggering element - # DOM node as its second. The `this` context is set to the tooltip - # instance. - # * `trigger` A {String} indicating how the tooltip should be displayed. - # Choose from one of the following options: - # * `'hover'` Show the tooltip when the mouse hovers over the element. - # This is the default. - # * `'click'` Show the tooltip when the element is clicked. The tooltip - # will be hidden after clicking the element again or anywhere else - # outside of the tooltip itself. - # * `'focus'` Show the tooltip when the element is focused. - # * `'manual'` Show the tooltip immediately and only hide it when the - # returned disposable is disposed. - # * `delay` An object specifying the show and hide delay in milliseconds. - # Defaults to `{show: 1000, hide: 100}` if the `trigger` is `hover` and - # otherwise defaults to `0` for both values. - # * `keyBindingCommand` A {String} containing a command name. If you specify - # this option and a key binding exists that matches the command, it will - # be appended to the title or rendered alone if no title is specified. - # * `keyBindingTarget` An `HTMLElement` on which to look up the key binding. - # If this option is not supplied, the first of all matching key bindings - # for the given command will be rendered. - # - # Returns a {Disposable} on which `.dispose()` can be called to remove the - # tooltip. - add: (target, options) -> - if target.jquery - disposable = new CompositeDisposable - disposable.add @add(element, options) for element in target - return disposable - - Tooltip ?= require './tooltip' - - {keyBindingCommand, keyBindingTarget} = options - - if keyBindingCommand? - bindings = @keymapManager.findKeyBindings(command: keyBindingCommand, target: keyBindingTarget) - keystroke = getKeystroke(bindings) - if options.title? and keystroke? - options.title += " " + getKeystroke(bindings) - else if keystroke? - options.title = getKeystroke(bindings) - - delete options.selector - options = _.defaults(options, @defaults) - if options.trigger is 'hover' - options = _.defaults(options, @hoverDefaults) - - tooltip = new Tooltip(target, options, @viewRegistry) - - if not @tooltips.has(target) - @tooltips.set(target, []) - @tooltips.get(target).push(tooltip) - - hideTooltip = -> - tooltip.leave(currentTarget: target) - tooltip.hide() - - window.addEventListener('resize', hideTooltip) - - disposable = new Disposable => - window.removeEventListener('resize', hideTooltip) - hideTooltip() - tooltip.destroy() - - if @tooltips.has(target) - tooltipsForTarget = @tooltips.get(target) - index = tooltipsForTarget.indexOf(tooltip) - if index isnt -1 - tooltipsForTarget.splice(index, 1) - if tooltipsForTarget.length is 0 - @tooltips.delete(target) - - disposable - - # Extended: Find the tooltips that have been applied to the given element. - # - # * `target` The `HTMLElement` to find tooltips on. - # - # Returns an {Array} of `Tooltip` objects that match the `target`. - findTooltips: (target) -> - if @tooltips.has(target) - @tooltips.get(target).slice() - else - [] - -humanizeKeystrokes = (keystroke) -> - keystrokes = keystroke.split(' ') - keystrokes = (_.humanizeKeystroke(stroke) for stroke in keystrokes) - keystrokes.join(' ') - -getKeystroke = (bindings) -> - if bindings?.length - "#{humanizeKeystrokes(bindings[0].keystrokes)}" diff --git a/src/tooltip-manager.js b/src/tooltip-manager.js new file mode 100644 index 000000000..c838b6dbc --- /dev/null +++ b/src/tooltip-manager.js @@ -0,0 +1,212 @@ +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS206: Consider reworking classes to avoid initClass + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +let TooltipManager +const _ = require('underscore-plus') +const {Disposable, CompositeDisposable} = require('event-kit') +let Tooltip = null + +// Essential: Associates tooltips with HTML elements. +// +// You can get the `TooltipManager` via `atom.tooltips`. +// +// ## Examples +// +// The essence of displaying a tooltip +// +// ```coffee +// # display it +// disposable = atom.tooltips.add(div, {title: 'This is a tooltip'}) +// +// # remove it +// disposable.dispose() +// ``` +// +// In practice there are usually multiple tooltips. So we add them to a +// CompositeDisposable +// +// ```coffee +// {CompositeDisposable} = require 'atom' +// subscriptions = new CompositeDisposable +// +// div1 = document.createElement('div') +// div2 = document.createElement('div') +// subscriptions.add atom.tooltips.add(div1, {title: 'This is a tooltip'}) +// subscriptions.add atom.tooltips.add(div2, {title: 'Another tooltip'}) +// +// # remove them all +// subscriptions.dispose() +// ``` +// +// You can display a key binding in the tooltip as well with the +// `keyBindingCommand` option. +// +// ```coffee +// disposable = atom.tooltips.add @caseOptionButton, +// title: "Match Case" +// keyBindingCommand: 'find-and-replace:toggle-case-option' +// keyBindingTarget: @findEditor.element +// ``` +module.exports = +(TooltipManager = (function () { + TooltipManager = class TooltipManager { + static initClass () { + this.prototype.defaults = { + trigger: 'hover', + container: 'body', + html: true, + placement: 'auto top', + viewportPadding: 2 + } + + this.prototype.hoverDefaults = + {delay: {show: 1000, hide: 100}} + } + + constructor ({keymapManager, viewRegistry}) { + this.keymapManager = keymapManager + this.viewRegistry = viewRegistry + this.tooltips = new Map() + } + + // Essential: Add a tooltip to the given element. + // + // * `target` An `HTMLElement` + // * `options` An object with one or more of the following options: + // * `title` A {String} or {Function} to use for the text in the tip. If + // a function is passed, `this` will be set to the `target` element. This + // option is mutually exclusive with the `item` option. + // * `html` A {Boolean} affecting the interpretation of the `title` option. + // If `true` (the default), the `title` string will be interpreted as HTML. + // Otherwise it will be interpreted as plain text. + // * `item` A view (object with an `.element` property) or a DOM element + // containing custom content for the tooltip. This option is mutually + // exclusive with the `title` option. + // * `class` A {String} with a class to apply to the tooltip element to + // enable custom styling. + // * `placement` A {String} or {Function} returning a string to indicate + // the position of the tooltip relative to `element`. Can be `'top'`, + // `'bottom'`, `'left'`, `'right'`, or `'auto'`. When `'auto'` is + // specified, it will dynamically reorient the tooltip. For example, if + // placement is `'auto left'`, the tooltip will display to the left when + // possible, otherwise it will display right. + // When a function is used to determine the placement, it is called with + // the tooltip DOM node as its first argument and the triggering element + // DOM node as its second. The `this` context is set to the tooltip + // instance. + // * `trigger` A {String} indicating how the tooltip should be displayed. + // Choose from one of the following options: + // * `'hover'` Show the tooltip when the mouse hovers over the element. + // This is the default. + // * `'click'` Show the tooltip when the element is clicked. The tooltip + // will be hidden after clicking the element again or anywhere else + // outside of the tooltip itself. + // * `'focus'` Show the tooltip when the element is focused. + // * `'manual'` Show the tooltip immediately and only hide it when the + // returned disposable is disposed. + // * `delay` An object specifying the show and hide delay in milliseconds. + // Defaults to `{show: 1000, hide: 100}` if the `trigger` is `hover` and + // otherwise defaults to `0` for both values. + // * `keyBindingCommand` A {String} containing a command name. If you specify + // this option and a key binding exists that matches the command, it will + // be appended to the title or rendered alone if no title is specified. + // * `keyBindingTarget` An `HTMLElement` on which to look up the key binding. + // If this option is not supplied, the first of all matching key bindings + // for the given command will be rendered. + // + // Returns a {Disposable} on which `.dispose()` can be called to remove the + // tooltip. + add (target, options) { + let disposable + if (target.jquery) { + disposable = new CompositeDisposable() + for (let element of target) { disposable.add(this.add(element, options)) } + return disposable + } + + if (Tooltip == null) { Tooltip = require('./tooltip') } + + const {keyBindingCommand, keyBindingTarget} = options + + if (keyBindingCommand != null) { + const bindings = this.keymapManager.findKeyBindings({command: keyBindingCommand, target: keyBindingTarget}) + const keystroke = getKeystroke(bindings) + if ((options.title != null) && (keystroke != null)) { + options.title += ` ${getKeystroke(bindings)}` + } else if (keystroke != null) { + options.title = getKeystroke(bindings) + } + } + + delete options.selector + options = _.defaults(options, this.defaults) + if (options.trigger === 'hover') { + options = _.defaults(options, this.hoverDefaults) + } + + const tooltip = new Tooltip(target, options, this.viewRegistry) + + if (!this.tooltips.has(target)) { + this.tooltips.set(target, []) + } + this.tooltips.get(target).push(tooltip) + + const hideTooltip = function () { + tooltip.leave({currentTarget: target}) + return tooltip.hide() + } + + window.addEventListener('resize', hideTooltip) + + disposable = new Disposable(() => { + window.removeEventListener('resize', hideTooltip) + hideTooltip() + tooltip.destroy() + + if (this.tooltips.has(target)) { + const tooltipsForTarget = this.tooltips.get(target) + const index = tooltipsForTarget.indexOf(tooltip) + if (index !== -1) { + tooltipsForTarget.splice(index, 1) + } + if (tooltipsForTarget.length === 0) { + return this.tooltips.delete(target) + } + } + }) + + return disposable + } + + // Extended: Find the tooltips that have been applied to the given element. + // + // * `target` The `HTMLElement` to find tooltips on. + // + // Returns an {Array} of `Tooltip` objects that match the `target`. + findTooltips (target) { + if (this.tooltips.has(target)) { + return this.tooltips.get(target).slice() + } else { + return [] + } + } + } + TooltipManager.initClass() + return TooltipManager +})()) + +const humanizeKeystrokes = function (keystroke) { + let keystrokes = keystroke.split(' ') + keystrokes = (keystrokes.map((stroke) => _.humanizeKeystroke(stroke))) + return keystrokes.join(' ') +} + +var getKeystroke = function (bindings) { + if (bindings != null ? bindings.length : undefined) { + return `${humanizeKeystrokes(bindings[0].keystrokes)}` + } +} From 034f003705f07e8a8b4d8354ae1f861dc851ab72 Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Mon, 23 Oct 2017 08:49:27 -0400 Subject: [PATCH 17/97] :shirt: Fix 'Unnecessary escape character: \"' --- src/tooltip-manager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tooltip-manager.js b/src/tooltip-manager.js index c838b6dbc..f127d3f44 100644 --- a/src/tooltip-manager.js +++ b/src/tooltip-manager.js @@ -207,6 +207,6 @@ const humanizeKeystrokes = function (keystroke) { var getKeystroke = function (bindings) { if (bindings != null ? bindings.length : undefined) { - return `${humanizeKeystrokes(bindings[0].keystrokes)}` + return `${humanizeKeystrokes(bindings[0].keystrokes)}` } } From 157c33b5471c6bf3dfb7b361decd6e64a56b8eba Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Mon, 23 Oct 2017 09:27:53 -0400 Subject: [PATCH 18/97] :art: DS206 Rework class to avoid initClass --- src/tooltip-manager.js | 267 ++++++++++++++++++++--------------------- 1 file changed, 130 insertions(+), 137 deletions(-) diff --git a/src/tooltip-manager.js b/src/tooltip-manager.js index f127d3f44..00e16e405 100644 --- a/src/tooltip-manager.js +++ b/src/tooltip-manager.js @@ -1,11 +1,9 @@ /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns - * DS206: Consider reworking classes to avoid initClass * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -let TooltipManager const _ = require('underscore-plus') const {Disposable, CompositeDisposable} = require('event-kit') let Tooltip = null @@ -52,152 +50,147 @@ let Tooltip = null // keyBindingTarget: @findEditor.element // ``` module.exports = -(TooltipManager = (function () { - TooltipManager = class TooltipManager { - static initClass () { - this.prototype.defaults = { - trigger: 'hover', - container: 'body', - html: true, - placement: 'auto top', - viewportPadding: 2 - } - - this.prototype.hoverDefaults = - {delay: {show: 1000, hide: 100}} +class TooltipManager { + constructor ({keymapManager, viewRegistry}) { + this.defaults = { + trigger: 'hover', + container: 'body', + html: true, + placement: 'auto top', + viewportPadding: 2 } - constructor ({keymapManager, viewRegistry}) { - this.keymapManager = keymapManager - this.viewRegistry = viewRegistry - this.tooltips = new Map() + this.hoverDefaults = { + delay: {show: 1000, hide: 100} } - // Essential: Add a tooltip to the given element. - // - // * `target` An `HTMLElement` - // * `options` An object with one or more of the following options: - // * `title` A {String} or {Function} to use for the text in the tip. If - // a function is passed, `this` will be set to the `target` element. This - // option is mutually exclusive with the `item` option. - // * `html` A {Boolean} affecting the interpretation of the `title` option. - // If `true` (the default), the `title` string will be interpreted as HTML. - // Otherwise it will be interpreted as plain text. - // * `item` A view (object with an `.element` property) or a DOM element - // containing custom content for the tooltip. This option is mutually - // exclusive with the `title` option. - // * `class` A {String} with a class to apply to the tooltip element to - // enable custom styling. - // * `placement` A {String} or {Function} returning a string to indicate - // the position of the tooltip relative to `element`. Can be `'top'`, - // `'bottom'`, `'left'`, `'right'`, or `'auto'`. When `'auto'` is - // specified, it will dynamically reorient the tooltip. For example, if - // placement is `'auto left'`, the tooltip will display to the left when - // possible, otherwise it will display right. - // When a function is used to determine the placement, it is called with - // the tooltip DOM node as its first argument and the triggering element - // DOM node as its second. The `this` context is set to the tooltip - // instance. - // * `trigger` A {String} indicating how the tooltip should be displayed. - // Choose from one of the following options: - // * `'hover'` Show the tooltip when the mouse hovers over the element. - // This is the default. - // * `'click'` Show the tooltip when the element is clicked. The tooltip - // will be hidden after clicking the element again or anywhere else - // outside of the tooltip itself. - // * `'focus'` Show the tooltip when the element is focused. - // * `'manual'` Show the tooltip immediately and only hide it when the - // returned disposable is disposed. - // * `delay` An object specifying the show and hide delay in milliseconds. - // Defaults to `{show: 1000, hide: 100}` if the `trigger` is `hover` and - // otherwise defaults to `0` for both values. - // * `keyBindingCommand` A {String} containing a command name. If you specify - // this option and a key binding exists that matches the command, it will - // be appended to the title or rendered alone if no title is specified. - // * `keyBindingTarget` An `HTMLElement` on which to look up the key binding. - // If this option is not supplied, the first of all matching key bindings - // for the given command will be rendered. - // - // Returns a {Disposable} on which `.dispose()` can be called to remove the - // tooltip. - add (target, options) { - let disposable - if (target.jquery) { - disposable = new CompositeDisposable() - for (let element of target) { disposable.add(this.add(element, options)) } - return disposable - } - - if (Tooltip == null) { Tooltip = require('./tooltip') } - - const {keyBindingCommand, keyBindingTarget} = options - - if (keyBindingCommand != null) { - const bindings = this.keymapManager.findKeyBindings({command: keyBindingCommand, target: keyBindingTarget}) - const keystroke = getKeystroke(bindings) - if ((options.title != null) && (keystroke != null)) { - options.title += ` ${getKeystroke(bindings)}` - } else if (keystroke != null) { - options.title = getKeystroke(bindings) - } - } - - delete options.selector - options = _.defaults(options, this.defaults) - if (options.trigger === 'hover') { - options = _.defaults(options, this.hoverDefaults) - } - - const tooltip = new Tooltip(target, options, this.viewRegistry) - - if (!this.tooltips.has(target)) { - this.tooltips.set(target, []) - } - this.tooltips.get(target).push(tooltip) - - const hideTooltip = function () { - tooltip.leave({currentTarget: target}) - return tooltip.hide() - } - - window.addEventListener('resize', hideTooltip) - - disposable = new Disposable(() => { - window.removeEventListener('resize', hideTooltip) - hideTooltip() - tooltip.destroy() - - if (this.tooltips.has(target)) { - const tooltipsForTarget = this.tooltips.get(target) - const index = tooltipsForTarget.indexOf(tooltip) - if (index !== -1) { - tooltipsForTarget.splice(index, 1) - } - if (tooltipsForTarget.length === 0) { - return this.tooltips.delete(target) - } - } - }) + this.keymapManager = keymapManager + this.viewRegistry = viewRegistry + this.tooltips = new Map() + } + // Essential: Add a tooltip to the given element. + // + // * `target` An `HTMLElement` + // * `options` An object with one or more of the following options: + // * `title` A {String} or {Function} to use for the text in the tip. If + // a function is passed, `this` will be set to the `target` element. This + // option is mutually exclusive with the `item` option. + // * `html` A {Boolean} affecting the interpretation of the `title` option. + // If `true` (the default), the `title` string will be interpreted as HTML. + // Otherwise it will be interpreted as plain text. + // * `item` A view (object with an `.element` property) or a DOM element + // containing custom content for the tooltip. This option is mutually + // exclusive with the `title` option. + // * `class` A {String} with a class to apply to the tooltip element to + // enable custom styling. + // * `placement` A {String} or {Function} returning a string to indicate + // the position of the tooltip relative to `element`. Can be `'top'`, + // `'bottom'`, `'left'`, `'right'`, or `'auto'`. When `'auto'` is + // specified, it will dynamically reorient the tooltip. For example, if + // placement is `'auto left'`, the tooltip will display to the left when + // possible, otherwise it will display right. + // When a function is used to determine the placement, it is called with + // the tooltip DOM node as its first argument and the triggering element + // DOM node as its second. The `this` context is set to the tooltip + // instance. + // * `trigger` A {String} indicating how the tooltip should be displayed. + // Choose from one of the following options: + // * `'hover'` Show the tooltip when the mouse hovers over the element. + // This is the default. + // * `'click'` Show the tooltip when the element is clicked. The tooltip + // will be hidden after clicking the element again or anywhere else + // outside of the tooltip itself. + // * `'focus'` Show the tooltip when the element is focused. + // * `'manual'` Show the tooltip immediately and only hide it when the + // returned disposable is disposed. + // * `delay` An object specifying the show and hide delay in milliseconds. + // Defaults to `{show: 1000, hide: 100}` if the `trigger` is `hover` and + // otherwise defaults to `0` for both values. + // * `keyBindingCommand` A {String} containing a command name. If you specify + // this option and a key binding exists that matches the command, it will + // be appended to the title or rendered alone if no title is specified. + // * `keyBindingTarget` An `HTMLElement` on which to look up the key binding. + // If this option is not supplied, the first of all matching key bindings + // for the given command will be rendered. + // + // Returns a {Disposable} on which `.dispose()` can be called to remove the + // tooltip. + add (target, options) { + let disposable + if (target.jquery) { + disposable = new CompositeDisposable() + for (let element of target) { disposable.add(this.add(element, options)) } return disposable } - // Extended: Find the tooltips that have been applied to the given element. - // - // * `target` The `HTMLElement` to find tooltips on. - // - // Returns an {Array} of `Tooltip` objects that match the `target`. - findTooltips (target) { - if (this.tooltips.has(target)) { - return this.tooltips.get(target).slice() - } else { - return [] + if (Tooltip == null) { Tooltip = require('./tooltip') } + + const {keyBindingCommand, keyBindingTarget} = options + + if (keyBindingCommand != null) { + const bindings = this.keymapManager.findKeyBindings({command: keyBindingCommand, target: keyBindingTarget}) + const keystroke = getKeystroke(bindings) + if ((options.title != null) && (keystroke != null)) { + options.title += ` ${getKeystroke(bindings)}` + } else if (keystroke != null) { + options.title = getKeystroke(bindings) } } + + delete options.selector + options = _.defaults(options, this.defaults) + if (options.trigger === 'hover') { + options = _.defaults(options, this.hoverDefaults) + } + + const tooltip = new Tooltip(target, options, this.viewRegistry) + + if (!this.tooltips.has(target)) { + this.tooltips.set(target, []) + } + this.tooltips.get(target).push(tooltip) + + const hideTooltip = function () { + tooltip.leave({currentTarget: target}) + return tooltip.hide() + } + + window.addEventListener('resize', hideTooltip) + + disposable = new Disposable(() => { + window.removeEventListener('resize', hideTooltip) + hideTooltip() + tooltip.destroy() + + if (this.tooltips.has(target)) { + const tooltipsForTarget = this.tooltips.get(target) + const index = tooltipsForTarget.indexOf(tooltip) + if (index !== -1) { + tooltipsForTarget.splice(index, 1) + } + if (tooltipsForTarget.length === 0) { + return this.tooltips.delete(target) + } + } + }) + + return disposable } - TooltipManager.initClass() - return TooltipManager -})()) + + // Extended: Find the tooltips that have been applied to the given element. + // + // * `target` The `HTMLElement` to find tooltips on. + // + // Returns an {Array} of `Tooltip` objects that match the `target`. + findTooltips (target) { + if (this.tooltips.has(target)) { + return this.tooltips.get(target).slice() + } else { + return [] + } + } +} const humanizeKeystrokes = function (keystroke) { let keystrokes = keystroke.split(' ') From 028d419ce778651124c504470f9515671c58a88c Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Mon, 23 Oct 2017 09:31:32 -0400 Subject: [PATCH 19/97] :art: DS102 Remove unnecessary code created because of implicit returns --- src/tooltip-manager.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/tooltip-manager.js b/src/tooltip-manager.js index 00e16e405..89849020c 100644 --- a/src/tooltip-manager.js +++ b/src/tooltip-manager.js @@ -1,6 +1,5 @@ /* * decaffeinate suggestions: - * DS102: Remove unnecessary code created because of implicit returns * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ @@ -153,7 +152,7 @@ class TooltipManager { const hideTooltip = function () { tooltip.leave({currentTarget: target}) - return tooltip.hide() + tooltip.hide() } window.addEventListener('resize', hideTooltip) @@ -170,7 +169,7 @@ class TooltipManager { tooltipsForTarget.splice(index, 1) } if (tooltipsForTarget.length === 0) { - return this.tooltips.delete(target) + this.tooltips.delete(target) } } }) From 4179b11cb9142ef69d4b0d4464fda06f0a992641 Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Mon, 23 Oct 2017 09:32:20 -0400 Subject: [PATCH 20/97] :art: DS207 Use shorter variations of null checks --- src/tooltip-manager.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/tooltip-manager.js b/src/tooltip-manager.js index 89849020c..a27b860b0 100644 --- a/src/tooltip-manager.js +++ b/src/tooltip-manager.js @@ -1,8 +1,3 @@ -/* - * decaffeinate suggestions: - * DS207: Consider shorter variations of null checks - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ const _ = require('underscore-plus') const {Disposable, CompositeDisposable} = require('event-kit') let Tooltip = null @@ -198,7 +193,7 @@ const humanizeKeystrokes = function (keystroke) { } var getKeystroke = function (bindings) { - if (bindings != null ? bindings.length : undefined) { + if (bindings && bindings.length) { return `${humanizeKeystrokes(bindings[0].keystrokes)}` } } From 74137446e79d74afac2aa7ca4b9e45581180496c Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Mon, 23 Oct 2017 09:34:20 -0400 Subject: [PATCH 21/97] =?UTF-8?q?:memo:=E2=98=A0=E2=98=95=20Decaffeinate?= =?UTF-8?q?=20TooltipManager=20API=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/tooltip-manager.js | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/tooltip-manager.js b/src/tooltip-manager.js index a27b860b0..73a58d1d6 100644 --- a/src/tooltip-manager.js +++ b/src/tooltip-manager.js @@ -10,38 +10,39 @@ let Tooltip = null // // The essence of displaying a tooltip // -// ```coffee -// # display it -// disposable = atom.tooltips.add(div, {title: 'This is a tooltip'}) +// ```javascript +// // display it +// const disposable = atom.tooltips.add(div, {title: 'This is a tooltip'}) // -// # remove it +// // remove it // disposable.dispose() // ``` // // In practice there are usually multiple tooltips. So we add them to a // CompositeDisposable // -// ```coffee -// {CompositeDisposable} = require 'atom' -// subscriptions = new CompositeDisposable +// ```javascript +// const {CompositeDisposable} = require('atom') +// const subscriptions = new CompositeDisposable() // -// div1 = document.createElement('div') -// div2 = document.createElement('div') -// subscriptions.add atom.tooltips.add(div1, {title: 'This is a tooltip'}) -// subscriptions.add atom.tooltips.add(div2, {title: 'Another tooltip'}) +// const div1 = document.createElement('div') +// const div2 = document.createElement('div') +// subscriptions.add(atom.tooltips.add(div1, {title: 'This is a tooltip'})) +// subscriptions.add(atom.tooltips.add(div2, {title: 'Another tooltip'})) // -// # remove them all +// // remove them all // subscriptions.dispose() // ``` // // You can display a key binding in the tooltip as well with the // `keyBindingCommand` option. // -// ```coffee -// disposable = atom.tooltips.add @caseOptionButton, -// title: "Match Case" -// keyBindingCommand: 'find-and-replace:toggle-case-option' -// keyBindingTarget: @findEditor.element +// ```javascript +// disposable = atom.tooltips.add(this.caseOptionButton, { +// title: 'Match Case', +// keyBindingCommand: 'find-and-replace:toggle-case-option', +// keyBindingTarget: this.findEditor.element +// }) // ``` module.exports = class TooltipManager { From 5e587e88a982e81ac8b96421fef75078e722579f Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Mon, 23 Oct 2017 09:34:35 -0400 Subject: [PATCH 22/97] :art: --- src/tooltip-manager.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/tooltip-manager.js b/src/tooltip-manager.js index 73a58d1d6..937f831d1 100644 --- a/src/tooltip-manager.js +++ b/src/tooltip-manager.js @@ -112,10 +112,9 @@ class TooltipManager { // Returns a {Disposable} on which `.dispose()` can be called to remove the // tooltip. add (target, options) { - let disposable if (target.jquery) { - disposable = new CompositeDisposable() - for (let element of target) { disposable.add(this.add(element, options)) } + const disposable = new CompositeDisposable() + for (const element of target) { disposable.add(this.add(element, options)) } return disposable } @@ -153,7 +152,7 @@ class TooltipManager { window.addEventListener('resize', hideTooltip) - disposable = new Disposable(() => { + const disposable = new Disposable(() => { window.removeEventListener('resize', hideTooltip) hideTooltip() tooltip.destroy() @@ -187,13 +186,13 @@ class TooltipManager { } } -const humanizeKeystrokes = function (keystroke) { +function humanizeKeystrokes (keystroke) { let keystrokes = keystroke.split(' ') keystrokes = (keystrokes.map((stroke) => _.humanizeKeystroke(stroke))) return keystrokes.join(' ') } -var getKeystroke = function (bindings) { +function getKeystroke (bindings) { if (bindings && bindings.length) { return `${humanizeKeystrokes(bindings[0].keystrokes)}` } From 8d532e77806703dae7ec4b80f84bc4e970b0b4fd Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 23 Oct 2017 10:20:45 -0700 Subject: [PATCH 23/97] Fix exception when trying to fold non-foldable row --- spec/text-editor-spec.js | 20 ++++++++++++++++++++ src/text-editor.coffee | 4 ++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/spec/text-editor-spec.js b/spec/text-editor-spec.js index c81df8089..b766a8ac9 100644 --- a/spec/text-editor-spec.js +++ b/spec/text-editor-spec.js @@ -173,6 +173,26 @@ describe('TextEditor', () => { }) }) + describe('.foldCurrentRow()', () => { + it('creates a fold at the location of the last cursor', async () => { + editor = await atom.workspace.open() + editor.setText('\nif (x) {\n y()\n}') + editor.setCursorBufferPosition([1, 0]) + expect(editor.getScreenLineCount()).toBe(4) + editor.foldCurrentRow() + expect(editor.getScreenLineCount()).toBe(3) + }) + + it('does nothing when the current row cannot be folded', async () => { + editor = await atom.workspace.open() + editor.setText('var x;\nx++\nx++') + editor.setCursorBufferPosition([0, 0]) + expect(editor.getScreenLineCount()).toBe(3) + editor.foldCurrentRow() + expect(editor.getScreenLineCount()).toBe(3) + }) + }) + describe('.foldAllAtIndentLevel(indentLevel)', () => { it('folds blocks of text at the given indentation level', async () => { editor = await atom.workspace.open('sample.js', {autoIndent: false}) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index c00508f09..6700af089 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -3310,8 +3310,8 @@ class TextEditor extends Model # level. foldCurrentRow: -> {row} = @getCursorBufferPosition() - range = @tokenizedBuffer.getFoldableRangeContainingPoint(Point(row, Infinity)) - @displayLayer.foldBufferRange(range) + if range = @tokenizedBuffer.getFoldableRangeContainingPoint(Point(row, Infinity)) + @displayLayer.foldBufferRange(range) # Essential: Unfold the most recent cursor's row by one level. unfoldCurrentRow: -> From 6ccc807aebbdbfea1a3b147d4d1bfc09a4362e9f Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Mon, 23 Oct 2017 10:45:08 -0700 Subject: [PATCH 24/97] :arrow_up: season --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e5541ff0e..2fba03420 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "scandal": "^3.1.0", "scoped-property-store": "^0.17.0", "scrollbar-style": "^3.2", - "season": "^6.0.1", + "season": "^6.0.2", "semver": "^4.3.3", "service-hub": "^0.7.4", "sinon": "1.17.4", From ef6b5ee07c42cf7fd31c56b83168496e6eeda8ad Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Mon, 23 Oct 2017 11:14:46 -0700 Subject: [PATCH 25/97] :arrow_up: language-gfm --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2fba03420..9c9a988db 100644 --- a/package.json +++ b/package.json @@ -141,7 +141,7 @@ "language-coffee-script": "0.49.1", "language-csharp": "0.14.3", "language-css": "0.42.6", - "language-gfm": "0.90.1", + "language-gfm": "0.90.2", "language-git": "0.19.1", "language-go": "0.44.2", "language-html": "0.48.1", From 8318b7207e5fcdf3f81425d61cc95cb15c974a1a Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Mon, 23 Oct 2017 11:22:34 -0700 Subject: [PATCH 26/97] :arrow_up: language-less --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9c9a988db..077b8d46f 100644 --- a/package.json +++ b/package.json @@ -149,7 +149,7 @@ "language-java": "0.27.4", "language-javascript": "0.127.5", "language-json": "0.19.1", - "language-less": "0.33.0", + "language-less": "0.33.1", "language-make": "0.22.3", "language-mustache": "0.14.3", "language-objective-c": "0.15.1", From 9e21931b91e48b71d01675a5b24850ab49cf86aa Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 23 Oct 2017 12:23:06 -0700 Subject: [PATCH 27/97] :arrow_up: text-buffer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 077b8d46f..af75ffd96 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "service-hub": "^0.7.4", "sinon": "1.17.4", "temp": "^0.8.3", - "text-buffer": "13.5.7", + "text-buffer": "13.5.8", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "winreg": "^1.2.1", From ed94726fab201200be8a33098dd91b0d54f1e1aa Mon Sep 17 00:00:00 2001 From: Justin Ratner Date: Mon, 23 Oct 2017 14:32:34 -0600 Subject: [PATCH 28/97] fix overlayComponent access syntax in test --- spec/text-editor-component-spec.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index d46748d91..5f0a28883 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -1896,8 +1896,7 @@ describe('TextEditorComponent', () => { const decoration = editor.decorateMarker(marker, {type: 'overlay', item: overlayElement, class: 'a'}) await component.getNextUpdatePromise() - let overlayComponent - component.overlayComponents.forEach(c => overlayComponent = c) + const overlayComponent = component.overlayComponents.values().next().value const overlayWrapper = overlayElement.parentElement expect(overlayWrapper.classList.contains('a')).toBe(true) From 5465830dbe04a43bc98eafccacef7f6824c7bb23 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Mon, 23 Oct 2017 11:23:20 -0700 Subject: [PATCH 29/97] :arrow_up: snippets --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index af75ffd96..2e98cc797 100644 --- a/package.json +++ b/package.json @@ -124,7 +124,7 @@ "open-on-github": "1.2.1", "package-generator": "1.1.1", "settings-view": "0.252.0", - "snippets": "1.1.5", + "snippets": "1.1.6", "spell-check": "0.72.3", "status-bar": "1.8.13", "styleguide": "0.49.7", From aa4796e7d614b8b58db1f11dc6cf1b162b96eeb6 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Mon, 23 Oct 2017 11:24:41 -0700 Subject: [PATCH 30/97] :arrow_up: settings-view --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2e98cc797..8b90346a4 100644 --- a/package.json +++ b/package.json @@ -123,7 +123,7 @@ "notifications": "0.69.2", "open-on-github": "1.2.1", "package-generator": "1.1.1", - "settings-view": "0.252.0", + "settings-view": "0.252.1", "snippets": "1.1.6", "spell-check": "0.72.3", "status-bar": "1.8.13", From adcbb7ab2c12a524cb5b91be9777c7f9f7afe0a7 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Mon, 23 Oct 2017 12:35:38 -0700 Subject: [PATCH 31/97] :arrow_up: first-mate --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8b90346a4..910f569fc 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "etch": "^0.12.6", "event-kit": "^2.4.0", "find-parent-dir": "^0.3.0", - "first-mate": "7.0.9", + "first-mate": "7.0.10", "focus-trap": "^2.3.0", "fs-admin": "^0.1.6", "fs-plus": "^3.0.1", From f31bbc58829003a85bb4c0c6bf818ef083c6e571 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Mon, 23 Oct 2017 14:04:04 -0700 Subject: [PATCH 32/97] :arrow_up: atom-keymap --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 910f569fc..2790f4c8f 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "dependencies": { "@atom/source-map-support": "^0.3.4", "async": "0.2.6", - "atom-keymap": "8.2.7", + "atom-keymap": "8.2.8", "atom-select-list": "^0.1.0", "atom-ui": "0.4.1", "babel-core": "5.8.38", From d03bedd8cff531cbcf3c62e9da377e99a469467d Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Mon, 23 Oct 2017 15:03:56 -0700 Subject: [PATCH 33/97] :arrow_up: styleguide --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2790f4c8f..51f6732d2 100644 --- a/package.json +++ b/package.json @@ -127,7 +127,7 @@ "snippets": "1.1.6", "spell-check": "0.72.3", "status-bar": "1.8.13", - "styleguide": "0.49.7", + "styleguide": "0.49.8", "symbols-view": "0.118.1", "tabs": "0.108.0", "timecop": "0.36.0", From d1844eccec173a16f030f2f90a08350da56fb300 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Mon, 23 Oct 2017 15:13:05 -0700 Subject: [PATCH 34/97] :arrow_up: markdown-preview --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 51f6732d2..8d8b98ccc 100644 --- a/package.json +++ b/package.json @@ -118,7 +118,7 @@ "keybinding-resolver": "0.38.0", "line-ending-selector": "0.7.4", "link": "0.31.3", - "markdown-preview": "0.159.15", + "markdown-preview": "0.159.16", "metrics": "1.2.6", "notifications": "0.69.2", "open-on-github": "1.2.1", From bbbf09ecf274d0665c70fb200b284fc425265dea Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 23 Oct 2017 16:18:01 -0600 Subject: [PATCH 35/97] Add preserveTrailingLineIndentation option to Selection.insertText We can use this to support a new command that preserves all formatting when pasting. --- spec/selection-spec.coffee | 5 +++++ src/selection.coffee | 18 ++++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/spec/selection-spec.coffee b/spec/selection-spec.coffee index cb070310a..b0e65be30 100644 --- a/spec/selection-spec.coffee +++ b/spec/selection-spec.coffee @@ -103,6 +103,11 @@ describe "Selection", -> selection.insertText("\r\n", autoIndent: true) expect(buffer.lineForRow(2)).toBe " " + it "does not adjust the indent of trailing lines if preserveTrailingLineIndentation is true", -> + selection.setBufferRange [[5, 0], [5, 0]] + selection.insertText(' foo\n bar\n', preserveTrailingLineIndentation: true, indentBasis: 1) + expect(buffer.lineForRow(6)).toBe(' bar') + describe ".fold()", -> it "folds the buffer range spanned by the selection", -> selection.setBufferRange([[0, 3], [1, 6]]) diff --git a/src/selection.coffee b/src/selection.coffee index 4d3fe8882..6fcf8dd36 100644 --- a/src/selection.coffee +++ b/src/selection.coffee @@ -356,13 +356,19 @@ class Selection extends Model # # * `text` A {String} representing the text to add # * `options` (optional) {Object} with keys: - # * `select` if `true`, selects the newly added text. - # * `autoIndent` if `true`, indents all inserted text appropriately. - # * `autoIndentNewline` if `true`, indent newline appropriately. - # * `autoDecreaseIndent` if `true`, decreases indent level appropriately + # * `select` If `true`, selects the newly added text. + # * `autoIndent` If `true`, indents all inserted text appropriately. + # * `autoIndentNewline` If `true`, indent newline appropriately. + # * `autoDecreaseIndent` If `true`, decreases indent level appropriately # (for example, when a closing bracket is inserted). + # * `preserveTrailingLineIndentation` By default, when pasting multiple + # lines, Atom attempts to preserve the relative indent level between the + # first line and trailing lines, even if the indent level of the first + # line has changed from the copied text. If this option is `true`, this + # behavior is suppressed. + # level between the first lines and the trailing lines. # * `normalizeLineEndings` (optional) {Boolean} (default: true) - # * `undo` if `skip`, skips the undo stack for this operation. + # * `undo` If `skip`, skips the undo stack for this operation. insertText: (text, options={}) -> oldBufferRange = @getBufferRange() wasReversed = @isReversed() @@ -373,7 +379,7 @@ class Selection extends Model remainingLines = text.split('\n') firstInsertedLine = remainingLines.shift() - if options.indentBasis? + if options.indentBasis? and not options.preserveTrailingLineIndentation indentAdjustment = @editor.indentLevelForLine(precedingText) - options.indentBasis @adjustIndent(remainingLines, indentAdjustment) From 6701644bbd9c983f804dc0596bc880695e09971d Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 23 Oct 2017 17:02:41 -0600 Subject: [PATCH 36/97] Respect format-preserving options in TextEditor.pasteText --- spec/text-editor-spec.coffee | 13 +++++++++++++ src/text-editor.coffee | 5 +++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 53011fdcc..bc74cd443 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -4222,6 +4222,19 @@ describe "TextEditor", -> expect(editor.lineTextForBufferRow(3)).toBe(" if (items.length <= 1) return items;") expect(editor.getCursorBufferPosition()).toEqual([3, 13]) + it "respects options that preserve the formatting of the pasted text", -> + editor.update({autoIndentOnPaste: true}) + atom.clipboard.write("a(x);\n b(x);\r\nc(x);\n", indentBasis: 0) + editor.setCursorBufferPosition([5, 0]) + editor.insertText(' ') + editor.pasteText({autoIndent: false, preserveTrailingLineIndentation: true, normalizeLineEndings: false}) + + expect(editor.lineTextForBufferRow(5)).toBe " a(x);" + expect(editor.lineTextForBufferRow(6)).toBe " b(x);" + expect(editor.buffer.lineEndingForRow(6)).toBe "\r\n" + expect(editor.lineTextForBufferRow(7)).toBe "c(x);" + expect(editor.lineTextForBufferRow(8)).toBe " current = items.shift();" + describe ".indentSelectedRows()", -> describe "when nothing is selected", -> describe "when softTabs is enabled", -> diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 6700af089..32dd49a18 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -3247,12 +3247,13 @@ class TextEditor extends Model # corresponding clipboard selection text. # # * `options` (optional) See {Selection::insertText}. - pasteText: (options={}) -> + pasteText: (options) -> + options = Object.assign({}, options) {text: clipboardText, metadata} = @constructor.clipboard.readWithMetadata() return false unless @emitWillInsertTextEvent(clipboardText) metadata ?= {} - options.autoIndent = @shouldAutoIndentOnPaste() + options.autoIndent ?= @shouldAutoIndentOnPaste() @mutateSelectedText (selection, index) => if metadata.selections?.length is @getSelections().length From 40ed5838a5a80f5681b2a43dbfa5d036c491a29d Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Mon, 23 Oct 2017 16:07:41 -0700 Subject: [PATCH 37/97] :arrow_up: dedent --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8d8b98ccc..4056b0d71 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "clear-cut": "^2.0.2", "coffee-script": "1.11.1", "color": "^0.7.3", - "dedent": "^0.6.0", + "dedent": "^0.7.0", "devtron": "1.3.0", "etch": "^0.12.6", "event-kit": "^2.4.0", From fd85c1bb5abec895bd780f2ed69033f5d89b3439 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 23 Oct 2017 17:14:41 -0600 Subject: [PATCH 38/97] Add `Paste without reformatting` command It is bound to cmd-shift-V on macOS and ctrl-shift-V on Windows and Linux. It is also available in the edit menu. --- keymaps/darwin.cson | 1 + keymaps/linux.cson | 1 + keymaps/win32.cson | 1 + menus/darwin.cson | 1 + menus/linux.cson | 1 + menus/win32.cson | 1 + src/register-default-commands.coffee | 5 +++++ 7 files changed, 11 insertions(+) diff --git a/keymaps/darwin.cson b/keymaps/darwin.cson index fa942d97c..7161a8478 100644 --- a/keymaps/darwin.cson +++ b/keymaps/darwin.cson @@ -132,6 +132,7 @@ 'ctrl-shift-w': 'editor:select-word' 'cmd-ctrl-left': 'editor:move-selection-left' 'cmd-ctrl-right': 'editor:move-selection-right' + 'cmd-shift-V': 'editor:paste-without-reformatting' # Emacs 'alt-f': 'editor:move-to-end-of-word' diff --git a/keymaps/linux.cson b/keymaps/linux.cson index d6ded1f90..9d3e4dbb1 100644 --- a/keymaps/linux.cson +++ b/keymaps/linux.cson @@ -105,6 +105,7 @@ 'alt-shift-right': 'editor:select-to-next-subword-boundary' 'alt-backspace': 'editor:delete-to-beginning-of-subword' 'alt-delete': 'editor:delete-to-end-of-subword' + 'ctrl-shift-V': 'editor:paste-without-reformatting' # Sublime Parity 'ctrl-a': 'core:select-all' diff --git a/keymaps/win32.cson b/keymaps/win32.cson index 14f5a4283..8a8e92249 100644 --- a/keymaps/win32.cson +++ b/keymaps/win32.cson @@ -110,6 +110,7 @@ 'alt-shift-right': 'editor:select-to-next-subword-boundary' 'alt-backspace': 'editor:delete-to-beginning-of-subword' 'alt-delete': 'editor:delete-to-end-of-subword' + 'ctrl-shift-V': 'editor:paste-without-reformatting' # Sublime Parity 'ctrl-a': 'core:select-all' diff --git a/menus/darwin.cson b/menus/darwin.cson index 055cd2405..2dffda1ef 100644 --- a/menus/darwin.cson +++ b/menus/darwin.cson @@ -65,6 +65,7 @@ { label: 'Copy', command: 'core:copy' } { label: 'Copy Path', command: 'editor:copy-path' } { label: 'Paste', command: 'core:paste' } + { label: 'Paste Without Reformatting', command: 'editor:paste-without-reformatting' } { label: 'Select All', command: 'core:select-all' } { type: 'separator' } { label: 'Toggle Comments', command: 'editor:toggle-line-comments' } diff --git a/menus/linux.cson b/menus/linux.cson index 2a1ca47f8..b44900398 100644 --- a/menus/linux.cson +++ b/menus/linux.cson @@ -38,6 +38,7 @@ { label: 'C&opy', command: 'core:copy' } { label: 'Copy Pat&h', command: 'editor:copy-path' } { label: '&Paste', command: 'core:paste' } + { label: 'Paste Without Reformatting', command: 'editor:paste-without-reformatting' } { label: 'Select &All', command: 'core:select-all' } { type: 'separator' } { label: '&Toggle Comments', command: 'editor:toggle-line-comments' } diff --git a/menus/win32.cson b/menus/win32.cson index 553b6017e..a921bae74 100644 --- a/menus/win32.cson +++ b/menus/win32.cson @@ -46,6 +46,7 @@ { label: '&Copy', command: 'core:copy' } { label: 'Copy Pat&h', command: 'editor:copy-path' } { label: '&Paste', command: 'core:paste' } + { label: 'Paste Without Reformatting', command: 'editor:paste-without-reformatting' } { label: 'Select &All', command: 'core:select-all' } { type: 'separator' } { label: '&Toggle Comments', command: 'editor:toggle-line-comments' } diff --git a/src/register-default-commands.coffee b/src/register-default-commands.coffee index d5b741c40..7dc0d3298 100644 --- a/src/register-default-commands.coffee +++ b/src/register-default-commands.coffee @@ -174,6 +174,11 @@ module.exports = ({commandRegistry, commandInstaller, config, notificationManage 'core:cut': -> @cutSelectedText() 'core:copy': -> @copySelectedText() 'core:paste': -> @pasteText() + 'editor:paste-without-reformatting': -> @pasteText({ + normalizeLineEndings: false, + autoIndent: false, + preserveTrailingLineIndentation: true + }) 'editor:delete-to-previous-word-boundary': -> @deleteToPreviousWordBoundary() 'editor:delete-to-next-word-boundary': -> @deleteToNextWordBoundary() 'editor:delete-to-beginning-of-word': -> @deleteToBeginningOfWord() From 311567ecec887c937d45287b92f667e827fcb1db Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 23 Oct 2017 16:45:12 -0700 Subject: [PATCH 39/97] Simplify .toggleLineComments method to avoid using oniguruma --- spec/tokenized-buffer-spec.js | 18 ++++---- src/tokenized-buffer.js | 78 +++++++++++++++++++++-------------- 2 files changed, 56 insertions(+), 40 deletions(-) diff --git a/spec/tokenized-buffer-spec.js b/spec/tokenized-buffer-spec.js index ba43f9ff3..9dc636bef 100644 --- a/spec/tokenized-buffer-spec.js +++ b/spec/tokenized-buffer-spec.js @@ -692,38 +692,38 @@ describe('TokenizedBuffer', () => { it('comments/uncomments lines in the given range', () => { tokenizedBuffer.toggleLineCommentsForBufferRows(0, 1) - expect(buffer.lineForRow(0)).toBe('/*body {') - expect(buffer.lineForRow(1)).toBe(' font-size: 1234px;*/') + expect(buffer.lineForRow(0)).toBe('/* body {') + expect(buffer.lineForRow(1)).toBe(' font-size: 1234px; */') expect(buffer.lineForRow(2)).toBe(' width: 110%;') expect(buffer.lineForRow(3)).toBe(' font-weight: bold !important;') tokenizedBuffer.toggleLineCommentsForBufferRows(2, 2) - expect(buffer.lineForRow(0)).toBe('/*body {') - expect(buffer.lineForRow(1)).toBe(' font-size: 1234px;*/') - expect(buffer.lineForRow(2)).toBe(' /*width: 110%;*/') + expect(buffer.lineForRow(0)).toBe('/* body {') + expect(buffer.lineForRow(1)).toBe(' font-size: 1234px; */') + expect(buffer.lineForRow(2)).toBe(' /* width: 110%; */') expect(buffer.lineForRow(3)).toBe(' font-weight: bold !important;') tokenizedBuffer.toggleLineCommentsForBufferRows(0, 1) expect(buffer.lineForRow(0)).toBe('body {') expect(buffer.lineForRow(1)).toBe(' font-size: 1234px;') - expect(buffer.lineForRow(2)).toBe(' /*width: 110%;*/') + expect(buffer.lineForRow(2)).toBe(' /* width: 110%; */') expect(buffer.lineForRow(3)).toBe(' font-weight: bold !important;') }) it('uncomments lines with leading whitespace', () => { - buffer.setTextInRange([[2, 0], [2, Infinity]], ' /*width: 110%;*/') + buffer.setTextInRange([[2, 0], [2, Infinity]], ' /* width: 110%; */') tokenizedBuffer.toggleLineCommentsForBufferRows(2, 2) expect(buffer.lineForRow(2)).toBe(' width: 110%;') }) it('uncomments lines with trailing whitespace', () => { - buffer.setTextInRange([[2, 0], [2, Infinity]], '/*width: 110%;*/ ') + buffer.setTextInRange([[2, 0], [2, Infinity]], '/* width: 110%; */ ') tokenizedBuffer.toggleLineCommentsForBufferRows(2, 2) expect(buffer.lineForRow(2)).toBe('width: 110%; ') }) it('uncomments lines with leading and trailing whitespace', () => { - buffer.setTextInRange([[2, 0], [2, Infinity]], ' /*width: 110%;*/ ') + buffer.setTextInRange([[2, 0], [2, Infinity]], ' /* width: 110%; */ ') tokenizedBuffer.toggleLineCommentsForBufferRows(2, 2) expect(buffer.lineForRow(2)).toBe(' width: 110%; ') }) diff --git a/src/tokenized-buffer.js b/src/tokenized-buffer.js index b4bc0d41c..13a1b17fa 100644 --- a/src/tokenized-buffer.js +++ b/src/tokenized-buffer.js @@ -165,37 +165,32 @@ class TokenizedBuffer { toggleLineCommentsForBufferRows (start, end) { const scope = this.scopeDescriptorForPosition([start, 0]) - const commentStrings = this.commentStringsForScopeDescriptor(scope) - if (!commentStrings) return - const {commentStartString, commentEndString} = commentStrings + let {commentStartString, commentEndString} = this.commentStringsForScopeDescriptor(scope) if (!commentStartString) return - - const commentStartRegexString = _.escapeRegExp(commentStartString).replace(/(\s+)$/, '(?:$1)?') - const commentStartRegex = new OnigRegExp(`^(\\s*)(${commentStartRegexString})`) + commentStartString = commentStartString.trim() if (commentEndString) { - const shouldUncomment = commentStartRegex.testSync(this.buffer.lineForRow(start)) - if (shouldUncomment) { - const commentEndRegexString = _.escapeRegExp(commentEndString).replace(/^(\s+)/, '(?:$1)?') - const commentEndRegex = new OnigRegExp(`(${commentEndRegexString})(\\s*)$`) - const startMatch = commentStartRegex.searchSync(this.buffer.lineForRow(start)) - const endMatch = commentEndRegex.searchSync(this.buffer.lineForRow(end)) - if (startMatch && endMatch) { + commentEndString = commentEndString.trim() + const startDelimiterColumnRange = this.columnRangeForStartDelimiter( + this.buffer.lineForRow(start), + commentStartString + ) + if (startDelimiterColumnRange) { + const endDelimiterColumnRange = this.columnRangeForEndDelimiter( + this.buffer.lineForRow(end), + commentEndString + ) + if (endDelimiterColumnRange) { this.buffer.transact(() => { - const columnStart = startMatch[1].length - const columnEnd = columnStart + startMatch[2].length - this.buffer.setTextInRange([[start, columnStart], [start, columnEnd]], '') - - const endLength = this.buffer.lineLengthForRow(end) - endMatch[2].length - const endColumn = endLength - endMatch[1].length - return this.buffer.setTextInRange([[end, endColumn], [end, endLength]], '') + this.buffer.delete([[end, endDelimiterColumnRange[0]], [end, endDelimiterColumnRange[1]]]) + this.buffer.delete([[start, startDelimiterColumnRange[0]], [start, startDelimiterColumnRange[1]]]) }) } } else { this.buffer.transact(() => { const indentLength = this.buffer.lineForRow(start).match(/^\s*/)[0].length - this.buffer.insert([start, indentLength], commentStartString) - this.buffer.insert([end, this.buffer.lineLengthForRow(end)], commentEndString) + this.buffer.insert([start, indentLength], commentStartString + ' ') + this.buffer.insert([end, this.buffer.lineLengthForRow(end)], ' ' + commentEndString) }) } } else { @@ -204,7 +199,7 @@ class TokenizedBuffer { for (let row = start; row <= end; row++) { const line = this.buffer.lineForRow(row) if (NON_WHITESPACE_REGEX.test(line)) { - if (commentStartRegex.testSync(line)) { + if (this.columnRangeForStartDelimiter(line, commentStartString)) { hasCommentedLines = true } else { hasUncommentedLines = true @@ -216,12 +211,11 @@ class TokenizedBuffer { if (shouldUncomment) { for (let row = start; row <= end; row++) { - const match = commentStartRegex.searchSync(this.buffer.lineForRow(row)) - if (match) { - const columnStart = match[1].length - const columnEnd = columnStart + match[2].length - this.buffer.setTextInRange([[row, columnStart], [row, columnEnd]], '') - } + const columnRange = this.columnRangeForStartDelimiter( + this.buffer.lineForRow(row), + commentStartString + ) + if (columnRange) this.buffer.delete([[row, columnRange[0]], [row, columnRange[1]]]) } } else { let minIndentLevel = Infinity @@ -247,11 +241,11 @@ class TokenizedBuffer { const line = this.buffer.lineForRow(row) if (NON_WHITESPACE_REGEX.test(line)) { const indentColumn = this.columnForIndentLevel(line, minIndentLevel) - this.buffer.insert(Point(row, indentColumn), commentStartString) + this.buffer.insert(Point(row, indentColumn), commentStartString + ' ') } else { this.buffer.setTextInRange( new Range(new Point(row, 0), new Point(row, Infinity)), - indentString + commentStartString + indentString + commentStartString + ' ' ) } } @@ -259,6 +253,26 @@ class TokenizedBuffer { } } + columnRangeForStartDelimiter (line, delimiter) { + const startColumn = line.search(NON_WHITESPACE_REGEX) + if (startColumn === -1) return null + if (!line.startsWith(delimiter, startColumn)) return null + + let endColumn = startColumn + delimiter.length + if (line[endColumn] === ' ') endColumn++ + return [startColumn, endColumn] + } + + columnRangeForEndDelimiter (line, delimiter) { + let startColumn = line.lastIndexOf(delimiter) + if (startColumn === -1) return null + + const endColumn = startColumn + delimiter.length + if (NON_WHITESPACE_REGEX.test(line.slice(endColumn))) return null + if (line[startColumn - 1] === ' ') startColumn-- + return [startColumn, endColumn] + } + buildIterator () { return new TokenizedBufferIterator(this) } @@ -844,6 +858,8 @@ class TokenizedBuffer { commentStringsForScopeDescriptor (scopes) { if (this.scopedSettingsDelegate) { return this.scopedSettingsDelegate.getCommentStrings(scopes) + } else { + return {} } } From 7637bc32d154f6f92b750eb26481853e94129a86 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Mon, 23 Oct 2017 16:57:34 -0700 Subject: [PATCH 40/97] :arrow_up: atom-package-manager --- apm/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apm/package.json b/apm/package.json index 5391c9972..e759a39d2 100644 --- a/apm/package.json +++ b/apm/package.json @@ -6,6 +6,6 @@ "url": "https://github.com/atom/atom.git" }, "dependencies": { - "atom-package-manager": "1.18.8" + "atom-package-manager": "1.18.9" } } From 079f4d901cdd006263d2844992eca6a52b898a0f Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 23 Oct 2017 17:00:05 -0700 Subject: [PATCH 41/97] Move all .toggleLineComments tests to text-editor-spec.js --- spec/text-editor-spec.coffee | 102 ------------- spec/text-editor-spec.js | 272 ++++++++++++++++++++++++++++++++++ spec/tokenized-buffer-spec.js | 180 ---------------------- 3 files changed, 272 insertions(+), 282 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 53011fdcc..de2f9fe8d 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -4363,108 +4363,6 @@ describe "TextEditor", -> expect(editor.lineTextForBufferRow(4)).toBe " }" expect(editor.lineTextForBufferRow(5)).toBe " i=1" - describe ".toggleLineCommentsInSelection()", -> - it "toggles comments on the selected lines", -> - editor.setSelectedBufferRange([[4, 5], [7, 5]]) - editor.toggleLineCommentsInSelection() - - expect(buffer.lineForRow(4)).toBe " // while(items.length > 0) {" - expect(buffer.lineForRow(5)).toBe " // current = items.shift();" - expect(buffer.lineForRow(6)).toBe " // current < pivot ? left.push(current) : right.push(current);" - expect(buffer.lineForRow(7)).toBe " // }" - expect(editor.getSelectedBufferRange()).toEqual [[4, 8], [7, 8]] - - editor.toggleLineCommentsInSelection() - expect(buffer.lineForRow(4)).toBe " while(items.length > 0) {" - expect(buffer.lineForRow(5)).toBe " current = items.shift();" - expect(buffer.lineForRow(6)).toBe " current < pivot ? left.push(current) : right.push(current);" - expect(buffer.lineForRow(7)).toBe " }" - - it "does not comment the last line of a non-empty selection if it ends at column 0", -> - editor.setSelectedBufferRange([[4, 5], [7, 0]]) - editor.toggleLineCommentsInSelection() - expect(buffer.lineForRow(4)).toBe " // while(items.length > 0) {" - expect(buffer.lineForRow(5)).toBe " // current = items.shift();" - expect(buffer.lineForRow(6)).toBe " // current < pivot ? left.push(current) : right.push(current);" - expect(buffer.lineForRow(7)).toBe " }" - - it "uncomments lines if all lines match the comment regex", -> - editor.setSelectedBufferRange([[0, 0], [0, 1]]) - editor.toggleLineCommentsInSelection() - expect(buffer.lineForRow(0)).toBe "// var quicksort = function () {" - - editor.setSelectedBufferRange([[0, 0], [2, Infinity]]) - editor.toggleLineCommentsInSelection() - expect(buffer.lineForRow(0)).toBe "// // var quicksort = function () {" - expect(buffer.lineForRow(1)).toBe "// var sort = function(items) {" - expect(buffer.lineForRow(2)).toBe "// if (items.length <= 1) return items;" - - editor.setSelectedBufferRange([[0, 0], [2, Infinity]]) - editor.toggleLineCommentsInSelection() - expect(buffer.lineForRow(0)).toBe "// var quicksort = function () {" - expect(buffer.lineForRow(1)).toBe " var sort = function(items) {" - expect(buffer.lineForRow(2)).toBe " if (items.length <= 1) return items;" - - editor.setSelectedBufferRange([[0, 0], [0, Infinity]]) - editor.toggleLineCommentsInSelection() - expect(buffer.lineForRow(0)).toBe "var quicksort = function () {" - - it "uncomments commented lines separated by an empty line", -> - editor.setSelectedBufferRange([[0, 0], [1, Infinity]]) - editor.toggleLineCommentsInSelection() - expect(buffer.lineForRow(0)).toBe "// var quicksort = function () {" - expect(buffer.lineForRow(1)).toBe "// var sort = function(items) {" - - buffer.insert([0, Infinity], '\n') - - editor.setSelectedBufferRange([[0, 0], [2, Infinity]]) - editor.toggleLineCommentsInSelection() - expect(buffer.lineForRow(0)).toBe "var quicksort = function () {" - expect(buffer.lineForRow(1)).toBe "" - expect(buffer.lineForRow(2)).toBe " var sort = function(items) {" - - it "preserves selection emptiness", -> - editor.setCursorBufferPosition([4, 0]) - editor.toggleLineCommentsInSelection() - expect(editor.getLastSelection().isEmpty()).toBeTruthy() - - it "does not explode if the current language mode has no comment regex", -> - editor = new TextEditor(buffer: new TextBuffer(text: 'hello')) - editor.setSelectedBufferRange([[0, 0], [0, 5]]) - editor.toggleLineCommentsInSelection() - expect(editor.lineTextForBufferRow(0)).toBe "hello" - - it "does nothing for empty lines and null grammar", -> - runs -> - editor.setGrammar(atom.grammars.grammarForScopeName('text.plain.null-grammar')) - editor.setCursorBufferPosition([10, 0]) - editor.toggleLineCommentsInSelection() - expect(editor.buffer.lineForRow(10)).toBe "" - - it "uncomments when the line lacks the trailing whitespace in the comment regex", -> - editor.setCursorBufferPosition([10, 0]) - editor.toggleLineCommentsInSelection() - - expect(buffer.lineForRow(10)).toBe "// " - expect(editor.getSelectedBufferRange()).toEqual [[10, 3], [10, 3]] - editor.backspace() - expect(buffer.lineForRow(10)).toBe "//" - - editor.toggleLineCommentsInSelection() - expect(buffer.lineForRow(10)).toBe "" - expect(editor.getSelectedBufferRange()).toEqual [[10, 0], [10, 0]] - - it "uncomments when the line has leading whitespace", -> - editor.setCursorBufferPosition([10, 0]) - editor.toggleLineCommentsInSelection() - - expect(buffer.lineForRow(10)).toBe "// " - editor.moveToBeginningOfLine() - editor.insertText(" ") - editor.setSelectedBufferRange([[10, 0], [10, 0]]) - editor.toggleLineCommentsInSelection() - expect(buffer.lineForRow(10)).toBe " " - describe ".undo() and .redo()", -> it "undoes/redoes the last change", -> editor.insertText("foo") diff --git a/spec/text-editor-spec.js b/spec/text-editor-spec.js index b766a8ac9..d10efa695 100644 --- a/spec/text-editor-spec.js +++ b/spec/text-editor-spec.js @@ -2,6 +2,8 @@ const fs = require('fs') const temp = require('temp').track() const {Point, Range} = require('text-buffer') const {it, fit, ffit, fffit, beforeEach, afterEach} = require('./async-spec-helpers') +const TextBuffer = require('text-buffer') +const TextEditor = require('../src/text-editor') describe('TextEditor', () => { let editor @@ -58,6 +60,276 @@ describe('TextEditor', () => { }) }) + describe('.toggleLineCommentsInSelection()', () => { + beforeEach(async () => { + await atom.packages.activatePackage('language-javascript') + editor = await atom.workspace.open('sample.js') + }) + + it('toggles comments on the selected lines', () => { + editor.setSelectedBufferRange([[4, 5], [7, 5]]) + editor.toggleLineCommentsInSelection() + + expect(editor.lineTextForBufferRow(4)).toBe(' // while(items.length > 0) {') + expect(editor.lineTextForBufferRow(5)).toBe(' // current = items.shift();') + expect(editor.lineTextForBufferRow(6)).toBe(' // current < pivot ? left.push(current) : right.push(current);') + expect(editor.lineTextForBufferRow(7)).toBe(' // }') + expect(editor.getSelectedBufferRange()).toEqual([[4, 8], [7, 8]]) + + editor.toggleLineCommentsInSelection() + expect(editor.lineTextForBufferRow(4)).toBe(' while(items.length > 0) {') + expect(editor.lineTextForBufferRow(5)).toBe(' current = items.shift();') + expect(editor.lineTextForBufferRow(6)).toBe(' current < pivot ? left.push(current) : right.push(current);') + expect(editor.lineTextForBufferRow(7)).toBe(' }') + }) + + it('does not comment the last line of a non-empty selection if it ends at column 0', () => { + editor.setSelectedBufferRange([[4, 5], [7, 0]]) + editor.toggleLineCommentsInSelection() + expect(editor.lineTextForBufferRow(4)).toBe(' // while(items.length > 0) {') + expect(editor.lineTextForBufferRow(5)).toBe(' // current = items.shift();') + expect(editor.lineTextForBufferRow(6)).toBe(' // current < pivot ? left.push(current) : right.push(current);') + expect(editor.lineTextForBufferRow(7)).toBe(' }') + }) + + it('uncomments lines if all lines match the comment regex', () => { + editor.setSelectedBufferRange([[0, 0], [0, 1]]) + editor.toggleLineCommentsInSelection() + expect(editor.lineTextForBufferRow(0)).toBe('// var quicksort = function () {') + + editor.setSelectedBufferRange([[0, 0], [2, Infinity]]) + editor.toggleLineCommentsInSelection() + expect(editor.lineTextForBufferRow(0)).toBe('// // var quicksort = function () {') + expect(editor.lineTextForBufferRow(1)).toBe('// var sort = function(items) {') + expect(editor.lineTextForBufferRow(2)).toBe('// if (items.length <= 1) return items;') + + editor.setSelectedBufferRange([[0, 0], [2, Infinity]]) + editor.toggleLineCommentsInSelection() + expect(editor.lineTextForBufferRow(0)).toBe('// var quicksort = function () {') + expect(editor.lineTextForBufferRow(1)).toBe(' var sort = function(items) {') + expect(editor.lineTextForBufferRow(2)).toBe(' if (items.length <= 1) return items;') + + editor.setSelectedBufferRange([[0, 0], [0, Infinity]]) + editor.toggleLineCommentsInSelection() + expect(editor.lineTextForBufferRow(0)).toBe('var quicksort = function () {') + }) + + it('uncomments commented lines separated by an empty line', () => { + editor.setSelectedBufferRange([[0, 0], [1, Infinity]]) + editor.toggleLineCommentsInSelection() + expect(editor.lineTextForBufferRow(0)).toBe('// var quicksort = function () {') + expect(editor.lineTextForBufferRow(1)).toBe('// var sort = function(items) {') + + editor.getBuffer().insert([0, Infinity], '\n') + + editor.setSelectedBufferRange([[0, 0], [2, Infinity]]) + editor.toggleLineCommentsInSelection() + expect(editor.lineTextForBufferRow(0)).toBe('var quicksort = function () {') + expect(editor.lineTextForBufferRow(1)).toBe('') + expect(editor.lineTextForBufferRow(2)).toBe(' var sort = function(items) {') + }) + + it('preserves selection emptiness', () => { + editor.setCursorBufferPosition([4, 0]) + editor.toggleLineCommentsInSelection() + expect(editor.getLastSelection().isEmpty()).toBeTruthy() + }) + + it('does not explode if the current language mode has no comment regex', () => { + const editor = new TextEditor({buffer: new TextBuffer({text: 'hello'})}) + editor.setSelectedBufferRange([[0, 0], [0, 5]]) + editor.toggleLineCommentsInSelection() + expect(editor.lineTextForBufferRow(0)).toBe('hello') + }) + + it('does nothing for empty lines and null grammar', () => { + editor.setGrammar(atom.grammars.grammarForScopeName('text.plain.null-grammar')) + editor.setCursorBufferPosition([10, 0]) + editor.toggleLineCommentsInSelection() + expect(editor.lineTextForBufferRow(10)).toBe('') + }) + + it('uncomments when the line lacks the trailing whitespace in the comment regex', () => { + editor.setCursorBufferPosition([10, 0]) + editor.toggleLineCommentsInSelection() + + expect(editor.lineTextForBufferRow(10)).toBe('// ') + expect(editor.getSelectedBufferRange()).toEqual([[10, 3], [10, 3]]) + editor.backspace() + expect(editor.lineTextForBufferRow(10)).toBe('//') + + editor.toggleLineCommentsInSelection() + expect(editor.lineTextForBufferRow(10)).toBe('') + expect(editor.getSelectedBufferRange()).toEqual([[10, 0], [10, 0]]) + }) + + it('uncomments when the line has leading whitespace', () => { + editor.setCursorBufferPosition([10, 0]) + editor.toggleLineCommentsInSelection() + + expect(editor.lineTextForBufferRow(10)).toBe('// ') + editor.moveToBeginningOfLine() + editor.insertText(' ') + editor.setSelectedBufferRange([[10, 0], [10, 0]]) + editor.toggleLineCommentsInSelection() + expect(editor.lineTextForBufferRow(10)).toBe(' ') + }) + }) + + describe('.toggleLineCommentsForBufferRows', () => { + describe('xml', () => { + beforeEach(async () => { + await atom.packages.activatePackage('language-xml') + editor = await atom.workspace.open('test.xml') + editor.setText('') + }) + + it('removes the leading whitespace from the comment end pattern match when uncommenting lines', () => { + editor.toggleLineCommentsForBufferRows(0, 0) + expect(editor.lineTextForBufferRow(0)).toBe('test') + }) + }) + + describe('less', () => { + beforeEach(async () => { + await atom.packages.activatePackage('language-less') + await atom.packages.activatePackage('language-css') + editor = await atom.workspace.open('sample.less') + }) + + it('only uses the `commentEnd` pattern if it comes from the same grammar as the `commentStart` when commenting lines', () => { + editor.toggleLineCommentsForBufferRows(0, 0) + expect(editor.lineTextForBufferRow(0)).toBe('// @color: #4D926F;') + }) + }) + + describe('css', () => { + beforeEach(async () => { + await atom.packages.activatePackage('language-css') + editor = await atom.workspace.open('css.css') + }) + + it('comments/uncomments lines in the given range', () => { + editor.toggleLineCommentsForBufferRows(0, 1) + expect(editor.lineTextForBufferRow(0)).toBe('/* body {') + expect(editor.lineTextForBufferRow(1)).toBe(' font-size: 1234px; */') + expect(editor.lineTextForBufferRow(2)).toBe(' width: 110%;') + expect(editor.lineTextForBufferRow(3)).toBe(' font-weight: bold !important;') + + editor.toggleLineCommentsForBufferRows(2, 2) + expect(editor.lineTextForBufferRow(0)).toBe('/* body {') + expect(editor.lineTextForBufferRow(1)).toBe(' font-size: 1234px; */') + expect(editor.lineTextForBufferRow(2)).toBe(' /* width: 110%; */') + expect(editor.lineTextForBufferRow(3)).toBe(' font-weight: bold !important;') + + editor.toggleLineCommentsForBufferRows(0, 1) + expect(editor.lineTextForBufferRow(0)).toBe('body {') + expect(editor.lineTextForBufferRow(1)).toBe(' font-size: 1234px;') + expect(editor.lineTextForBufferRow(2)).toBe(' /* width: 110%; */') + expect(editor.lineTextForBufferRow(3)).toBe(' font-weight: bold !important;') + }) + + it('uncomments lines with leading whitespace', () => { + editor.setTextInBufferRange([[2, 0], [2, Infinity]], ' /* width: 110%; */') + editor.toggleLineCommentsForBufferRows(2, 2) + expect(editor.lineTextForBufferRow(2)).toBe(' width: 110%;') + }) + + it('uncomments lines with trailing whitespace', () => { + editor.setTextInBufferRange([[2, 0], [2, Infinity]], '/* width: 110%; */ ') + editor.toggleLineCommentsForBufferRows(2, 2) + expect(editor.lineTextForBufferRow(2)).toBe('width: 110%; ') + }) + + it('uncomments lines with leading and trailing whitespace', () => { + editor.setTextInBufferRange([[2, 0], [2, Infinity]], ' /* width: 110%; */ ') + editor.toggleLineCommentsForBufferRows(2, 2) + expect(editor.lineTextForBufferRow(2)).toBe(' width: 110%; ') + }) + }) + + describe('coffeescript', () => { + beforeEach(async () => { + await atom.packages.activatePackage('language-coffee-script') + editor = await atom.workspace.open('coffee.coffee') + }) + + it('comments/uncomments lines in the given range', () => { + editor.toggleLineCommentsForBufferRows(4, 6) + expect(editor.lineTextForBufferRow(4)).toBe(' # pivot = items.shift()') + expect(editor.lineTextForBufferRow(5)).toBe(' # left = []') + expect(editor.lineTextForBufferRow(6)).toBe(' # right = []') + + editor.toggleLineCommentsForBufferRows(4, 5) + expect(editor.lineTextForBufferRow(4)).toBe(' pivot = items.shift()') + expect(editor.lineTextForBufferRow(5)).toBe(' left = []') + expect(editor.lineTextForBufferRow(6)).toBe(' # right = []') + }) + + it('comments/uncomments empty lines', () => { + editor.toggleLineCommentsForBufferRows(4, 7) + expect(editor.lineTextForBufferRow(4)).toBe(' # pivot = items.shift()') + expect(editor.lineTextForBufferRow(5)).toBe(' # left = []') + expect(editor.lineTextForBufferRow(6)).toBe(' # right = []') + expect(editor.lineTextForBufferRow(7)).toBe(' # ') + + editor.toggleLineCommentsForBufferRows(4, 5) + expect(editor.lineTextForBufferRow(4)).toBe(' pivot = items.shift()') + expect(editor.lineTextForBufferRow(5)).toBe(' left = []') + expect(editor.lineTextForBufferRow(6)).toBe(' # right = []') + expect(editor.lineTextForBufferRow(7)).toBe(' # ') + }) + }) + + describe('javascript', () => { + beforeEach(async () => { + await atom.packages.activatePackage('language-javascript') + editor = await atom.workspace.open('sample.js') + }) + + it('comments/uncomments lines in the given range', () => { + editor.toggleLineCommentsForBufferRows(4, 7) + expect(editor.lineTextForBufferRow(4)).toBe(' // while(items.length > 0) {') + expect(editor.lineTextForBufferRow(5)).toBe(' // current = items.shift();') + expect(editor.lineTextForBufferRow(6)).toBe(' // current < pivot ? left.push(current) : right.push(current);') + expect(editor.lineTextForBufferRow(7)).toBe(' // }') + + editor.toggleLineCommentsForBufferRows(4, 5) + expect(editor.lineTextForBufferRow(4)).toBe(' while(items.length > 0) {') + expect(editor.lineTextForBufferRow(5)).toBe(' current = items.shift();') + expect(editor.lineTextForBufferRow(6)).toBe(' // current < pivot ? left.push(current) : right.push(current);') + expect(editor.lineTextForBufferRow(7)).toBe(' // }') + + editor.setText('\tvar i;') + editor.toggleLineCommentsForBufferRows(0, 0) + expect(editor.lineTextForBufferRow(0)).toBe('\t// var i;') + + editor.setText('var i;') + editor.toggleLineCommentsForBufferRows(0, 0) + expect(editor.lineTextForBufferRow(0)).toBe('// var i;') + + editor.setText(' var i;') + editor.toggleLineCommentsForBufferRows(0, 0) + expect(editor.lineTextForBufferRow(0)).toBe(' // var i;') + + editor.setText(' ') + editor.toggleLineCommentsForBufferRows(0, 0) + expect(editor.lineTextForBufferRow(0)).toBe(' // ') + + editor.setText(' a\n \n b') + editor.toggleLineCommentsForBufferRows(0, 2) + expect(editor.lineTextForBufferRow(0)).toBe(' // a') + expect(editor.lineTextForBufferRow(1)).toBe(' // ') + expect(editor.lineTextForBufferRow(2)).toBe(' // b') + + editor.setText(' \n // var i;') + editor.toggleLineCommentsForBufferRows(0, 1) + expect(editor.lineTextForBufferRow(0)).toBe(' ') + expect(editor.lineTextForBufferRow(1)).toBe(' var i;') + }) + }) + }) + describe('folding', () => { beforeEach(async () => { await atom.packages.activatePackage('language-javascript') diff --git a/spec/tokenized-buffer-spec.js b/spec/tokenized-buffer-spec.js index 9dc636bef..b1574673a 100644 --- a/spec/tokenized-buffer-spec.js +++ b/spec/tokenized-buffer-spec.js @@ -643,186 +643,6 @@ describe('TokenizedBuffer', () => { }) }) - describe('.toggleLineCommentsForBufferRows', () => { - describe('xml', () => { - beforeEach(async () => { - await atom.packages.activatePackage('language-xml') - buffer = new TextBuffer('') - tokenizedBuffer = new TokenizedBuffer({ - buffer, - grammar: atom.grammars.grammarForScopeName('text.xml'), - scopedSettingsDelegate: new ScopedSettingsDelegate(atom.config) - }) - }) - - it('removes the leading whitespace from the comment end pattern match when uncommenting lines', () => { - tokenizedBuffer.toggleLineCommentsForBufferRows(0, 0) - expect(buffer.lineForRow(0)).toBe('test') - }) - }) - - describe('less', () => { - beforeEach(async () => { - await atom.packages.activatePackage('language-less') - await atom.packages.activatePackage('language-css') - buffer = await TextBuffer.load(require.resolve('./fixtures/sample.less')) - tokenizedBuffer = new TokenizedBuffer({ - buffer, - grammar: atom.grammars.grammarForScopeName('source.css.less'), - scopedSettingsDelegate: new ScopedSettingsDelegate(atom.config) - }) - }) - - it('only uses the `commentEnd` pattern if it comes from the same grammar as the `commentStart` when commenting lines', () => { - tokenizedBuffer.toggleLineCommentsForBufferRows(0, 0) - expect(buffer.lineForRow(0)).toBe('// @color: #4D926F;') - }) - }) - - describe('css', () => { - beforeEach(async () => { - await atom.packages.activatePackage('language-css') - buffer = await TextBuffer.load(require.resolve('./fixtures/css.css')) - tokenizedBuffer = new TokenizedBuffer({ - buffer, - grammar: atom.grammars.grammarForScopeName('source.css'), - scopedSettingsDelegate: new ScopedSettingsDelegate(atom.config) - }) - }) - - it('comments/uncomments lines in the given range', () => { - tokenizedBuffer.toggleLineCommentsForBufferRows(0, 1) - expect(buffer.lineForRow(0)).toBe('/* body {') - expect(buffer.lineForRow(1)).toBe(' font-size: 1234px; */') - expect(buffer.lineForRow(2)).toBe(' width: 110%;') - expect(buffer.lineForRow(3)).toBe(' font-weight: bold !important;') - - tokenizedBuffer.toggleLineCommentsForBufferRows(2, 2) - expect(buffer.lineForRow(0)).toBe('/* body {') - expect(buffer.lineForRow(1)).toBe(' font-size: 1234px; */') - expect(buffer.lineForRow(2)).toBe(' /* width: 110%; */') - expect(buffer.lineForRow(3)).toBe(' font-weight: bold !important;') - - tokenizedBuffer.toggleLineCommentsForBufferRows(0, 1) - expect(buffer.lineForRow(0)).toBe('body {') - expect(buffer.lineForRow(1)).toBe(' font-size: 1234px;') - expect(buffer.lineForRow(2)).toBe(' /* width: 110%; */') - expect(buffer.lineForRow(3)).toBe(' font-weight: bold !important;') - }) - - it('uncomments lines with leading whitespace', () => { - buffer.setTextInRange([[2, 0], [2, Infinity]], ' /* width: 110%; */') - tokenizedBuffer.toggleLineCommentsForBufferRows(2, 2) - expect(buffer.lineForRow(2)).toBe(' width: 110%;') - }) - - it('uncomments lines with trailing whitespace', () => { - buffer.setTextInRange([[2, 0], [2, Infinity]], '/* width: 110%; */ ') - tokenizedBuffer.toggleLineCommentsForBufferRows(2, 2) - expect(buffer.lineForRow(2)).toBe('width: 110%; ') - }) - - it('uncomments lines with leading and trailing whitespace', () => { - buffer.setTextInRange([[2, 0], [2, Infinity]], ' /* width: 110%; */ ') - tokenizedBuffer.toggleLineCommentsForBufferRows(2, 2) - expect(buffer.lineForRow(2)).toBe(' width: 110%; ') - }) - }) - - describe('coffeescript', () => { - beforeEach(async () => { - await atom.packages.activatePackage('language-coffee-script') - buffer = await TextBuffer.load(require.resolve('./fixtures/coffee.coffee')) - tokenizedBuffer = new TokenizedBuffer({ - buffer, - tabLength: 2, - grammar: atom.grammars.grammarForScopeName('source.coffee'), - scopedSettingsDelegate: new ScopedSettingsDelegate(atom.config) - }) - }) - - it('comments/uncomments lines in the given range', () => { - tokenizedBuffer.toggleLineCommentsForBufferRows(4, 6) - expect(buffer.lineForRow(4)).toBe(' # pivot = items.shift()') - expect(buffer.lineForRow(5)).toBe(' # left = []') - expect(buffer.lineForRow(6)).toBe(' # right = []') - - tokenizedBuffer.toggleLineCommentsForBufferRows(4, 5) - expect(buffer.lineForRow(4)).toBe(' pivot = items.shift()') - expect(buffer.lineForRow(5)).toBe(' left = []') - expect(buffer.lineForRow(6)).toBe(' # right = []') - }) - - it('comments/uncomments empty lines', () => { - tokenizedBuffer.toggleLineCommentsForBufferRows(4, 7) - expect(buffer.lineForRow(4)).toBe(' # pivot = items.shift()') - expect(buffer.lineForRow(5)).toBe(' # left = []') - expect(buffer.lineForRow(6)).toBe(' # right = []') - expect(buffer.lineForRow(7)).toBe(' # ') - - tokenizedBuffer.toggleLineCommentsForBufferRows(4, 5) - expect(buffer.lineForRow(4)).toBe(' pivot = items.shift()') - expect(buffer.lineForRow(5)).toBe(' left = []') - expect(buffer.lineForRow(6)).toBe(' # right = []') - expect(buffer.lineForRow(7)).toBe(' # ') - }) - }) - - describe('javascript', () => { - beforeEach(async () => { - await atom.packages.activatePackage('language-javascript') - buffer = await TextBuffer.load(require.resolve('./fixtures/sample.js')) - tokenizedBuffer = new TokenizedBuffer({ - buffer, - tabLength: 2, - grammar: atom.grammars.grammarForScopeName('source.js'), - scopedSettingsDelegate: new ScopedSettingsDelegate(atom.config) - }) - }) - - it('comments/uncomments lines in the given range', () => { - tokenizedBuffer.toggleLineCommentsForBufferRows(4, 7) - expect(buffer.lineForRow(4)).toBe(' // while(items.length > 0) {') - expect(buffer.lineForRow(5)).toBe(' // current = items.shift();') - expect(buffer.lineForRow(6)).toBe(' // current < pivot ? left.push(current) : right.push(current);') - expect(buffer.lineForRow(7)).toBe(' // }') - - tokenizedBuffer.toggleLineCommentsForBufferRows(4, 5) - expect(buffer.lineForRow(4)).toBe(' while(items.length > 0) {') - expect(buffer.lineForRow(5)).toBe(' current = items.shift();') - expect(buffer.lineForRow(6)).toBe(' // current < pivot ? left.push(current) : right.push(current);') - expect(buffer.lineForRow(7)).toBe(' // }') - - buffer.setText('\tvar i;') - tokenizedBuffer.toggleLineCommentsForBufferRows(0, 0) - expect(buffer.lineForRow(0)).toBe('\t// var i;') - - buffer.setText('var i;') - tokenizedBuffer.toggleLineCommentsForBufferRows(0, 0) - expect(buffer.lineForRow(0)).toBe('// var i;') - - buffer.setText(' var i;') - tokenizedBuffer.toggleLineCommentsForBufferRows(0, 0) - expect(buffer.lineForRow(0)).toBe(' // var i;') - - buffer.setText(' ') - tokenizedBuffer.toggleLineCommentsForBufferRows(0, 0) - expect(buffer.lineForRow(0)).toBe(' // ') - - buffer.setText(' a\n \n b') - tokenizedBuffer.toggleLineCommentsForBufferRows(0, 2) - expect(buffer.lineForRow(0)).toBe(' // a') - expect(buffer.lineForRow(1)).toBe(' // ') - expect(buffer.lineForRow(2)).toBe(' // b') - - buffer.setText(' \n // var i;') - tokenizedBuffer.toggleLineCommentsForBufferRows(0, 1) - expect(buffer.lineForRow(0)).toBe(' ') - expect(buffer.lineForRow(1)).toBe(' var i;') - }) - }) - }) - describe('.isFoldableAtRow(row)', () => { beforeEach(() => { buffer = atom.project.bufferForPathSync('sample.js') From cfe5cfce766fcdec4cd342e1b4c7c72f475c4693 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 23 Oct 2017 17:44:45 -0700 Subject: [PATCH 42/97] Move .toggleLineComments method from TokenizedBuffer to TextEditor --- src/text-editor-utils.js | 139 +++++++++++++++++++++++++++++++++++++++ src/text-editor.coffee | 9 ++- src/tokenized-buffer.js | 137 ++------------------------------------ 3 files changed, 148 insertions(+), 137 deletions(-) create mode 100644 src/text-editor-utils.js diff --git a/src/text-editor-utils.js b/src/text-editor-utils.js new file mode 100644 index 000000000..ab1104144 --- /dev/null +++ b/src/text-editor-utils.js @@ -0,0 +1,139 @@ +// This file is temporary. We should gradually convert methods in `text-editor.coffee` +// from CoffeeScript to JavaScript and move them here, so that we can eventually convert +// the entire class to JavaScript. + +const {Point, Range} = require('text-buffer') + +const NON_WHITESPACE_REGEX = /\S/ + +module.exports = { + toggleLineCommentsForBufferRows (start, end) { + let { + commentStartString, + commentEndString + } = this.tokenizedBuffer.commentStringsForPosition(Point(start, 0)) + if (!commentStartString) return + commentStartString = commentStartString.trim() + + if (commentEndString) { + commentEndString = commentEndString.trim() + const startDelimiterColumnRange = columnRangeForStartDelimiter( + this.buffer.lineForRow(start), + commentStartString + ) + if (startDelimiterColumnRange) { + const endDelimiterColumnRange = columnRangeForEndDelimiter( + this.buffer.lineForRow(end), + commentEndString + ) + if (endDelimiterColumnRange) { + this.buffer.transact(() => { + this.buffer.delete([[end, endDelimiterColumnRange[0]], [end, endDelimiterColumnRange[1]]]) + this.buffer.delete([[start, startDelimiterColumnRange[0]], [start, startDelimiterColumnRange[1]]]) + }) + } + } else { + this.buffer.transact(() => { + const indentLength = this.buffer.lineForRow(start).match(/^\s*/)[0].length + this.buffer.insert([start, indentLength], commentStartString + ' ') + this.buffer.insert([end, this.buffer.lineLengthForRow(end)], ' ' + commentEndString) + }) + } + } else { + let hasCommentedLines = false + let hasUncommentedLines = false + for (let row = start; row <= end; row++) { + const line = this.buffer.lineForRow(row) + if (NON_WHITESPACE_REGEX.test(line)) { + if (columnRangeForStartDelimiter(line, commentStartString)) { + hasCommentedLines = true + } else { + hasUncommentedLines = true + } + } + } + + const shouldUncomment = hasCommentedLines && !hasUncommentedLines + + if (shouldUncomment) { + for (let row = start; row <= end; row++) { + const columnRange = columnRangeForStartDelimiter( + this.buffer.lineForRow(row), + commentStartString + ) + if (columnRange) this.buffer.delete([[row, columnRange[0]], [row, columnRange[1]]]) + } + } else { + let minIndentLevel = Infinity + let minBlankIndentLevel = Infinity + for (let row = start; row <= end; row++) { + const line = this.buffer.lineForRow(row) + const indentLevel = this.indentLevelForLine(line) + if (NON_WHITESPACE_REGEX.test(line)) { + if (indentLevel < minIndentLevel) minIndentLevel = indentLevel + } else { + if (indentLevel < minBlankIndentLevel) minBlankIndentLevel = indentLevel + } + } + minIndentLevel = Number.isFinite(minIndentLevel) + ? minIndentLevel + : Number.isFinite(minBlankIndentLevel) + ? minBlankIndentLevel + : 0 + + const tabLength = this.getTabLength() + const indentString = ' '.repeat(tabLength * minIndentLevel) + for (let row = start; row <= end; row++) { + const line = this.buffer.lineForRow(row) + if (NON_WHITESPACE_REGEX.test(line)) { + const indentColumn = columnForIndentLevel(line, minIndentLevel, this.getTabLength()) + this.buffer.insert(Point(row, indentColumn), commentStartString + ' ') + } else { + this.buffer.setTextInRange( + new Range(new Point(row, 0), new Point(row, Infinity)), + indentString + commentStartString + ' ' + ) + } + } + } + } + } +} + +function columnForIndentLevel (line, indentLevel, tabLength) { + let column = 0 + let indentLength = 0 + const goalIndentLength = indentLevel * tabLength + while (indentLength < goalIndentLength) { + const char = line[column] + if (char === '\t') { + indentLength += tabLength - (indentLength % tabLength) + } else if (char === ' ') { + indentLength++ + } else { + break + } + column++ + } + return column +} + +function columnRangeForStartDelimiter (line, delimiter) { + const startColumn = line.search(NON_WHITESPACE_REGEX) + if (startColumn === -1) return null + if (!line.startsWith(delimiter, startColumn)) return null + + let endColumn = startColumn + delimiter.length + if (line[endColumn] === ' ') endColumn++ + return [startColumn, endColumn] +} + +function columnRangeForEndDelimiter (line, delimiter) { + let startColumn = line.lastIndexOf(delimiter) + if (startColumn === -1) return null + + const endColumn = startColumn + delimiter.length + if (NON_WHITESPACE_REGEX.test(line.slice(endColumn))) return null + if (line[startColumn - 1] === ' ') startColumn-- + return [startColumn, endColumn] +} diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 6700af089..f75822d77 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -9,6 +9,8 @@ TokenizedBuffer = require './tokenized-buffer' Cursor = require './cursor' Model = require './model' Selection = require './selection' +TextEditorUtils = require './text-editor-utils' + TextMateScopeSelector = require('first-mate').ScopeSelector GutterContainer = require './gutter-container' TextEditorComponent = null @@ -123,6 +125,8 @@ class TextEditor extends Model Object.defineProperty(@prototype, 'languageMode', get: -> @tokenizedBuffer) + Object.assign(@prototype, TextEditorUtils) + @deserialize: (state, atomEnvironment) -> # TODO: Return null on version mismatch when 1.8.0 has been out for a while if state.version isnt @prototype.serializationVersion and state.displayBuffer? @@ -3621,9 +3625,6 @@ class TextEditor extends Model getNonWordCharacters: (scopes) -> @scopedSettingsDelegate?.getNonWordCharacters?(scopes) ? @nonWordCharacters - getCommentStrings: (scopes) -> - @scopedSettingsDelegate?.getCommentStrings?(scopes) - ### Section: Event Handlers ### @@ -3886,8 +3887,6 @@ class TextEditor extends Model toggleLineCommentForBufferRow: (row) -> @toggleLineCommentsForBufferRows(row, row) - toggleLineCommentsForBufferRows: (start, end) -> @tokenizedBuffer.toggleLineCommentsForBufferRows(start, end) - rowRangeForParagraphAtBufferRow: (bufferRow) -> return unless NON_WHITESPACE_REGEXP.test(@lineTextForBufferRow(bufferRow)) diff --git a/src/tokenized-buffer.js b/src/tokenized-buffer.js index 13a1b17fa..2a9446256 100644 --- a/src/tokenized-buffer.js +++ b/src/tokenized-buffer.js @@ -163,116 +163,15 @@ class TokenizedBuffer { Section - Comments */ - toggleLineCommentsForBufferRows (start, end) { - const scope = this.scopeDescriptorForPosition([start, 0]) - let {commentStartString, commentEndString} = this.commentStringsForScopeDescriptor(scope) - if (!commentStartString) return - commentStartString = commentStartString.trim() - - if (commentEndString) { - commentEndString = commentEndString.trim() - const startDelimiterColumnRange = this.columnRangeForStartDelimiter( - this.buffer.lineForRow(start), - commentStartString - ) - if (startDelimiterColumnRange) { - const endDelimiterColumnRange = this.columnRangeForEndDelimiter( - this.buffer.lineForRow(end), - commentEndString - ) - if (endDelimiterColumnRange) { - this.buffer.transact(() => { - this.buffer.delete([[end, endDelimiterColumnRange[0]], [end, endDelimiterColumnRange[1]]]) - this.buffer.delete([[start, startDelimiterColumnRange[0]], [start, startDelimiterColumnRange[1]]]) - }) - } - } else { - this.buffer.transact(() => { - const indentLength = this.buffer.lineForRow(start).match(/^\s*/)[0].length - this.buffer.insert([start, indentLength], commentStartString + ' ') - this.buffer.insert([end, this.buffer.lineLengthForRow(end)], ' ' + commentEndString) - }) - } + commentStringsForPosition (position) { + if (this.scopedSettingsDelegate) { + const scope = this.scopeDescriptorForPosition(position) + return this.scopedSettingsDelegate.getCommentStrings(scope) } else { - let hasCommentedLines = false - let hasUncommentedLines = false - for (let row = start; row <= end; row++) { - const line = this.buffer.lineForRow(row) - if (NON_WHITESPACE_REGEX.test(line)) { - if (this.columnRangeForStartDelimiter(line, commentStartString)) { - hasCommentedLines = true - } else { - hasUncommentedLines = true - } - } - } - - const shouldUncomment = hasCommentedLines && !hasUncommentedLines - - if (shouldUncomment) { - for (let row = start; row <= end; row++) { - const columnRange = this.columnRangeForStartDelimiter( - this.buffer.lineForRow(row), - commentStartString - ) - if (columnRange) this.buffer.delete([[row, columnRange[0]], [row, columnRange[1]]]) - } - } else { - let minIndentLevel = Infinity - let minBlankIndentLevel = Infinity - for (let row = start; row <= end; row++) { - const line = this.buffer.lineForRow(row) - const indentLevel = this.indentLevelForLine(line) - if (NON_WHITESPACE_REGEX.test(line)) { - if (indentLevel < minIndentLevel) minIndentLevel = indentLevel - } else { - if (indentLevel < minBlankIndentLevel) minBlankIndentLevel = indentLevel - } - } - minIndentLevel = Number.isFinite(minIndentLevel) - ? minIndentLevel - : Number.isFinite(minBlankIndentLevel) - ? minBlankIndentLevel - : 0 - - const tabLength = this.getTabLength() - const indentString = ' '.repeat(tabLength * minIndentLevel) - for (let row = start; row <= end; row++) { - const line = this.buffer.lineForRow(row) - if (NON_WHITESPACE_REGEX.test(line)) { - const indentColumn = this.columnForIndentLevel(line, minIndentLevel) - this.buffer.insert(Point(row, indentColumn), commentStartString + ' ') - } else { - this.buffer.setTextInRange( - new Range(new Point(row, 0), new Point(row, Infinity)), - indentString + commentStartString + ' ' - ) - } - } - } + return {} } } - columnRangeForStartDelimiter (line, delimiter) { - const startColumn = line.search(NON_WHITESPACE_REGEX) - if (startColumn === -1) return null - if (!line.startsWith(delimiter, startColumn)) return null - - let endColumn = startColumn + delimiter.length - if (line[endColumn] === ' ') endColumn++ - return [startColumn, endColumn] - } - - columnRangeForEndDelimiter (line, delimiter) { - let startColumn = line.lastIndexOf(delimiter) - if (startColumn === -1) return null - - const endColumn = startColumn + delimiter.length - if (NON_WHITESPACE_REGEX.test(line.slice(endColumn))) return null - if (line[startColumn - 1] === ' ') startColumn-- - return [startColumn, endColumn] - } - buildIterator () { return new TokenizedBufferIterator(this) } @@ -608,24 +507,6 @@ class TokenizedBuffer { return scopes } - columnForIndentLevel (line, indentLevel, tabLength = this.tabLength) { - let column = 0 - let indentLength = 0 - const goalIndentLength = indentLevel * tabLength - while (indentLength < goalIndentLength) { - const char = line[column] - if (char === '\t') { - indentLength += tabLength - (indentLength % tabLength) - } else if (char === ' ') { - indentLength++ - } else { - break - } - column++ - } - return column - } - indentLevelForLine (line, tabLength = this.tabLength) { let indentLength = 0 for (let i = 0, {length} = line; i < length; i++) { @@ -855,14 +736,6 @@ class TokenizedBuffer { } } - commentStringsForScopeDescriptor (scopes) { - if (this.scopedSettingsDelegate) { - return this.scopedSettingsDelegate.getCommentStrings(scopes) - } else { - return {} - } - } - regexForPattern (pattern) { if (pattern) { if (!this.regexesByPattern[pattern]) { From f771cf9d1ae4ca47c941dee52e7266f0e2bf41ab Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Tue, 24 Oct 2017 08:47:44 -0400 Subject: [PATCH 43/97] =?UTF-8?q?=E2=98=A0=E2=98=95=20Decaffeinate=20spec/?= =?UTF-8?q?tooltip-manager-spec.coffee?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apply results of running: $ decaffeinate --keep-commonjs --prefer-const --loose-default-params --loose-for-expressions --loose-for-of --loose-includes spec/tooltip-manager-spec.coffee $ standard --fix spec/tooltip-manager-spec.js --- spec/tooltip-manager-spec.coffee | 213 ------------------------- spec/tooltip-manager-spec.js | 260 +++++++++++++++++++++++++++++++ 2 files changed, 260 insertions(+), 213 deletions(-) delete mode 100644 spec/tooltip-manager-spec.coffee create mode 100644 spec/tooltip-manager-spec.js diff --git a/spec/tooltip-manager-spec.coffee b/spec/tooltip-manager-spec.coffee deleted file mode 100644 index 95182853e..000000000 --- a/spec/tooltip-manager-spec.coffee +++ /dev/null @@ -1,213 +0,0 @@ -{CompositeDisposable} = require 'atom' -TooltipManager = require '../src/tooltip-manager' -Tooltip = require '../src/tooltip' -_ = require 'underscore-plus' - -describe "TooltipManager", -> - [manager, element] = [] - - ctrlX = _.humanizeKeystroke("ctrl-x") - ctrlY = _.humanizeKeystroke("ctrl-y") - - beforeEach -> - manager = new TooltipManager(keymapManager: atom.keymaps, viewRegistry: atom.views) - element = createElement 'foo' - - createElement = (className) -> - el = document.createElement('div') - el.classList.add(className) - jasmine.attachToDOM(el) - el - - mouseEnter = (element) -> - element.dispatchEvent(new CustomEvent('mouseenter', bubbles: false)) - element.dispatchEvent(new CustomEvent('mouseover', bubbles: true)) - - mouseLeave = (element) -> - element.dispatchEvent(new CustomEvent('mouseleave', bubbles: false)) - element.dispatchEvent(new CustomEvent('mouseout', bubbles: true)) - - hover = (element, fn) -> - mouseEnter(element) - advanceClock(manager.hoverDefaults.delay.show) - fn() - mouseLeave(element) - advanceClock(manager.hoverDefaults.delay.hide) - - describe "::add(target, options)", -> - describe "when the trigger is 'hover' (the default)", -> - it "creates a tooltip when hovering over the target element", -> - manager.add element, title: "Title" - hover element, -> - expect(document.body.querySelector(".tooltip")).toHaveText("Title") - - it "displays tooltips immediately when hovering over new elements once a tooltip has been displayed once", -> - disposables = new CompositeDisposable - element1 = createElement('foo') - disposables.add(manager.add element1, title: 'Title') - element2 = createElement('bar') - disposables.add(manager.add element2, title: 'Title') - element3 = createElement('baz') - disposables.add(manager.add element3, title: 'Title') - - hover element1, -> - expect(document.body.querySelector(".tooltip")).toBeNull() - - mouseEnter(element2) - expect(document.body.querySelector(".tooltip")).not.toBeNull() - mouseLeave(element2) - advanceClock(manager.hoverDefaults.delay.hide) - expect(document.body.querySelector(".tooltip")).toBeNull() - - advanceClock(Tooltip.FOLLOW_THROUGH_DURATION) - mouseEnter(element3) - expect(document.body.querySelector(".tooltip")).toBeNull() - advanceClock(manager.hoverDefaults.delay.show) - expect(document.body.querySelector(".tooltip")).not.toBeNull() - - disposables.dispose() - - describe "when the trigger is 'manual'", -> - it "creates a tooltip immediately and only hides it on dispose", -> - disposable = manager.add element, title: "Title", trigger: "manual" - expect(document.body.querySelector(".tooltip")).toHaveText("Title") - disposable.dispose() - expect(document.body.querySelector(".tooltip")).toBeNull() - - describe "when the trigger is 'click'", -> - it "shows and hides the tooltip when the target element is clicked", -> - disposable = manager.add element, title: "Title", trigger: "click" - expect(document.body.querySelector(".tooltip")).toBeNull() - element.click() - expect(document.body.querySelector(".tooltip")).not.toBeNull() - element.click() - expect(document.body.querySelector(".tooltip")).toBeNull() - - # Hide the tooltip when clicking anywhere but inside the tooltip element - element.click() - expect(document.body.querySelector(".tooltip")).not.toBeNull() - document.body.querySelector(".tooltip").click() - expect(document.body.querySelector(".tooltip")).not.toBeNull() - document.body.querySelector(".tooltip").firstChild.click() - expect(document.body.querySelector(".tooltip")).not.toBeNull() - document.body.click() - expect(document.body.querySelector(".tooltip")).toBeNull() - - # Tooltip can show again after hiding due to clicking outside of the tooltip - element.click() - expect(document.body.querySelector(".tooltip")).not.toBeNull() - element.click() - expect(document.body.querySelector(".tooltip")).toBeNull() - - it "allows a custom item to be specified for the content of the tooltip", -> - tooltipElement = document.createElement('div') - manager.add element, item: {element: tooltipElement} - hover element, -> - expect(tooltipElement.closest(".tooltip")).not.toBeNull() - - it "allows a custom class to be specified for the tooltip", -> - tooltipElement = document.createElement('div') - manager.add element, title: 'Title', class: 'custom-tooltip-class' - hover element, -> - expect(document.body.querySelector(".tooltip").classList.contains('custom-tooltip-class')).toBe(true) - - it "allows jQuery elements to be passed as the target", -> - element2 = document.createElement('div') - jasmine.attachToDOM(element2) - - fakeJqueryWrapper = [element, element2] - fakeJqueryWrapper.jquery = 'any-version' - disposable = manager.add fakeJqueryWrapper, title: "Title" - - hover element, -> expect(document.body.querySelector(".tooltip")).toHaveText("Title") - expect(document.body.querySelector(".tooltip")).toBeNull() - hover element2, -> expect(document.body.querySelector(".tooltip")).toHaveText("Title") - expect(document.body.querySelector(".tooltip")).toBeNull() - - disposable.dispose() - - hover element, -> expect(document.body.querySelector(".tooltip")).toBeNull() - hover element2, -> expect(document.body.querySelector(".tooltip")).toBeNull() - - describe "when a keyBindingCommand is specified", -> - describe "when a title is specified", -> - it "appends the key binding corresponding to the command to the title", -> - atom.keymaps.add 'test', - '.foo': 'ctrl-x ctrl-y': 'test-command' - '.bar': 'ctrl-x ctrl-z': 'test-command' - - manager.add element, title: "Title", keyBindingCommand: 'test-command' - - hover element, -> - tooltipElement = document.body.querySelector(".tooltip") - expect(tooltipElement).toHaveText "Title #{ctrlX} #{ctrlY}" - - describe "when no title is specified", -> - it "shows the key binding corresponding to the command alone", -> - atom.keymaps.add 'test', '.foo': 'ctrl-x ctrl-y': 'test-command' - - manager.add element, keyBindingCommand: 'test-command' - - hover element, -> - tooltipElement = document.body.querySelector(".tooltip") - expect(tooltipElement).toHaveText "#{ctrlX} #{ctrlY}" - - describe "when a keyBindingTarget is specified", -> - it "looks up the key binding relative to the target", -> - atom.keymaps.add 'test', - '.bar': 'ctrl-x ctrl-z': 'test-command' - '.foo': 'ctrl-x ctrl-y': 'test-command' - - manager.add element, keyBindingCommand: 'test-command', keyBindingTarget: element - - hover element, -> - tooltipElement = document.body.querySelector(".tooltip") - expect(tooltipElement).toHaveText "#{ctrlX} #{ctrlY}" - - it "does not display the keybinding if there is nothing mapped to the specified keyBindingCommand", -> - manager.add element, title: 'A Title', keyBindingCommand: 'test-command', keyBindingTarget: element - - hover element, -> - tooltipElement = document.body.querySelector(".tooltip") - expect(tooltipElement.textContent).toBe "A Title" - - describe "when .dispose() is called on the returned disposable", -> - it "no longer displays the tooltip on hover", -> - disposable = manager.add element, title: "Title" - - hover element, -> - expect(document.body.querySelector(".tooltip")).toHaveText("Title") - - disposable.dispose() - - hover element, -> - expect(document.body.querySelector(".tooltip")).toBeNull() - - describe "when the window is resized", -> - it "hides the tooltips", -> - disposable = manager.add element, title: "Title" - hover element, -> - expect(document.body.querySelector(".tooltip")).not.toBeNull() - window.dispatchEvent(new CustomEvent('resize')) - expect(document.body.querySelector(".tooltip")).toBeNull() - disposable.dispose() - - describe "findTooltips", -> - it "adds and remove tooltips correctly", -> - expect(manager.findTooltips(element).length).toBe(0) - disposable1 = manager.add element, title: "elem1" - expect(manager.findTooltips(element).length).toBe(1) - disposable2 = manager.add element, title: "elem2" - expect(manager.findTooltips(element).length).toBe(2) - disposable1.dispose() - expect(manager.findTooltips(element).length).toBe(1) - disposable2.dispose() - expect(manager.findTooltips(element).length).toBe(0) - - it "lets us hide tooltips programmatically", -> - disposable = manager.add element, title: "Title" - hover element, -> - expect(document.body.querySelector(".tooltip")).not.toBeNull() - manager.findTooltips(element)[0].hide() - expect(document.body.querySelector(".tooltip")).toBeNull() - disposable.dispose() diff --git a/spec/tooltip-manager-spec.js b/spec/tooltip-manager-spec.js new file mode 100644 index 000000000..c022db44a --- /dev/null +++ b/spec/tooltip-manager-spec.js @@ -0,0 +1,260 @@ +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const {CompositeDisposable} = require('atom') +const TooltipManager = require('../src/tooltip-manager') +const Tooltip = require('../src/tooltip') +const _ = require('underscore-plus') + +describe('TooltipManager', function () { + let [manager, element] = Array.from([]) + + const ctrlX = _.humanizeKeystroke('ctrl-x') + const ctrlY = _.humanizeKeystroke('ctrl-y') + + beforeEach(function () { + manager = new TooltipManager({keymapManager: atom.keymaps, viewRegistry: atom.views}) + return element = createElement('foo') + }) + + var createElement = function (className) { + const el = document.createElement('div') + el.classList.add(className) + jasmine.attachToDOM(el) + return el + } + + const mouseEnter = function (element) { + element.dispatchEvent(new CustomEvent('mouseenter', {bubbles: false})) + return element.dispatchEvent(new CustomEvent('mouseover', {bubbles: true})) + } + + const mouseLeave = function (element) { + element.dispatchEvent(new CustomEvent('mouseleave', {bubbles: false})) + return element.dispatchEvent(new CustomEvent('mouseout', {bubbles: true})) + } + + const hover = function (element, fn) { + mouseEnter(element) + advanceClock(manager.hoverDefaults.delay.show) + fn() + mouseLeave(element) + return advanceClock(manager.hoverDefaults.delay.hide) + } + + return describe('::add(target, options)', function () { + describe("when the trigger is 'hover' (the default)", function () { + it('creates a tooltip when hovering over the target element', function () { + manager.add(element, {title: 'Title'}) + return hover(element, () => expect(document.body.querySelector('.tooltip')).toHaveText('Title')) + }) + + return it('displays tooltips immediately when hovering over new elements once a tooltip has been displayed once', function () { + const disposables = new CompositeDisposable() + const element1 = createElement('foo') + disposables.add(manager.add(element1, {title: 'Title'})) + const element2 = createElement('bar') + disposables.add(manager.add(element2, {title: 'Title'})) + const element3 = createElement('baz') + disposables.add(manager.add(element3, {title: 'Title'})) + + hover(element1, function () {}) + expect(document.body.querySelector('.tooltip')).toBeNull() + + mouseEnter(element2) + expect(document.body.querySelector('.tooltip')).not.toBeNull() + mouseLeave(element2) + advanceClock(manager.hoverDefaults.delay.hide) + expect(document.body.querySelector('.tooltip')).toBeNull() + + advanceClock(Tooltip.FOLLOW_THROUGH_DURATION) + mouseEnter(element3) + expect(document.body.querySelector('.tooltip')).toBeNull() + advanceClock(manager.hoverDefaults.delay.show) + expect(document.body.querySelector('.tooltip')).not.toBeNull() + + return disposables.dispose() + }) + }) + + describe("when the trigger is 'manual'", () => + it('creates a tooltip immediately and only hides it on dispose', function () { + const disposable = manager.add(element, {title: 'Title', trigger: 'manual'}) + expect(document.body.querySelector('.tooltip')).toHaveText('Title') + disposable.dispose() + return expect(document.body.querySelector('.tooltip')).toBeNull() + }) + ) + + describe("when the trigger is 'click'", () => + it('shows and hides the tooltip when the target element is clicked', function () { + const disposable = manager.add(element, {title: 'Title', trigger: 'click'}) + expect(document.body.querySelector('.tooltip')).toBeNull() + element.click() + expect(document.body.querySelector('.tooltip')).not.toBeNull() + element.click() + expect(document.body.querySelector('.tooltip')).toBeNull() + + // Hide the tooltip when clicking anywhere but inside the tooltip element + element.click() + expect(document.body.querySelector('.tooltip')).not.toBeNull() + document.body.querySelector('.tooltip').click() + expect(document.body.querySelector('.tooltip')).not.toBeNull() + document.body.querySelector('.tooltip').firstChild.click() + expect(document.body.querySelector('.tooltip')).not.toBeNull() + document.body.click() + expect(document.body.querySelector('.tooltip')).toBeNull() + + // Tooltip can show again after hiding due to clicking outside of the tooltip + element.click() + expect(document.body.querySelector('.tooltip')).not.toBeNull() + element.click() + return expect(document.body.querySelector('.tooltip')).toBeNull() + }) + ) + + it('allows a custom item to be specified for the content of the tooltip', function () { + const tooltipElement = document.createElement('div') + manager.add(element, {item: {element: tooltipElement}}) + return hover(element, () => expect(tooltipElement.closest('.tooltip')).not.toBeNull()) + }) + + it('allows a custom class to be specified for the tooltip', function () { + const tooltipElement = document.createElement('div') + manager.add(element, {title: 'Title', class: 'custom-tooltip-class'}) + return hover(element, () => expect(document.body.querySelector('.tooltip').classList.contains('custom-tooltip-class')).toBe(true)) + }) + + it('allows jQuery elements to be passed as the target', function () { + const element2 = document.createElement('div') + jasmine.attachToDOM(element2) + + const fakeJqueryWrapper = [element, element2] + fakeJqueryWrapper.jquery = 'any-version' + const disposable = manager.add(fakeJqueryWrapper, {title: 'Title'}) + + hover(element, () => expect(document.body.querySelector('.tooltip')).toHaveText('Title')) + expect(document.body.querySelector('.tooltip')).toBeNull() + hover(element2, () => expect(document.body.querySelector('.tooltip')).toHaveText('Title')) + expect(document.body.querySelector('.tooltip')).toBeNull() + + disposable.dispose() + + hover(element, () => expect(document.body.querySelector('.tooltip')).toBeNull()) + return hover(element2, () => expect(document.body.querySelector('.tooltip')).toBeNull()) + }) + + describe('when a keyBindingCommand is specified', function () { + describe('when a title is specified', () => + it('appends the key binding corresponding to the command to the title', function () { + atom.keymaps.add('test', { + '.foo': { 'ctrl-x ctrl-y': 'test-command' + }, + '.bar': { 'ctrl-x ctrl-z': 'test-command' + } + } + ) + + manager.add(element, {title: 'Title', keyBindingCommand: 'test-command'}) + + return hover(element, function () { + const tooltipElement = document.body.querySelector('.tooltip') + return expect(tooltipElement).toHaveText(`Title ${ctrlX} ${ctrlY}`) + }) + }) + ) + + describe('when no title is specified', () => + it('shows the key binding corresponding to the command alone', function () { + atom.keymaps.add('test', {'.foo': {'ctrl-x ctrl-y': 'test-command'}}) + + manager.add(element, {keyBindingCommand: 'test-command'}) + + return hover(element, function () { + const tooltipElement = document.body.querySelector('.tooltip') + return expect(tooltipElement).toHaveText(`${ctrlX} ${ctrlY}`) + }) + }) + ) + + return describe('when a keyBindingTarget is specified', function () { + it('looks up the key binding relative to the target', function () { + atom.keymaps.add('test', { + '.bar': { 'ctrl-x ctrl-z': 'test-command' + }, + '.foo': { 'ctrl-x ctrl-y': 'test-command' + } + } + ) + + manager.add(element, {keyBindingCommand: 'test-command', keyBindingTarget: element}) + + return hover(element, function () { + const tooltipElement = document.body.querySelector('.tooltip') + return expect(tooltipElement).toHaveText(`${ctrlX} ${ctrlY}`) + }) + }) + + return it('does not display the keybinding if there is nothing mapped to the specified keyBindingCommand', function () { + manager.add(element, {title: 'A Title', keyBindingCommand: 'test-command', keyBindingTarget: element}) + + return hover(element, function () { + const tooltipElement = document.body.querySelector('.tooltip') + return expect(tooltipElement.textContent).toBe('A Title') + }) + }) + }) + }) + + describe('when .dispose() is called on the returned disposable', () => + it('no longer displays the tooltip on hover', function () { + const disposable = manager.add(element, {title: 'Title'}) + + hover(element, () => expect(document.body.querySelector('.tooltip')).toHaveText('Title')) + + disposable.dispose() + + return hover(element, () => expect(document.body.querySelector('.tooltip')).toBeNull()) + }) + ) + + describe('when the window is resized', () => + it('hides the tooltips', function () { + const disposable = manager.add(element, {title: 'Title'}) + return hover(element, function () { + expect(document.body.querySelector('.tooltip')).not.toBeNull() + window.dispatchEvent(new CustomEvent('resize')) + expect(document.body.querySelector('.tooltip')).toBeNull() + return disposable.dispose() + }) + }) + ) + + return describe('findTooltips', function () { + it('adds and remove tooltips correctly', function () { + expect(manager.findTooltips(element).length).toBe(0) + const disposable1 = manager.add(element, {title: 'elem1'}) + expect(manager.findTooltips(element).length).toBe(1) + const disposable2 = manager.add(element, {title: 'elem2'}) + expect(manager.findTooltips(element).length).toBe(2) + disposable1.dispose() + expect(manager.findTooltips(element).length).toBe(1) + disposable2.dispose() + return expect(manager.findTooltips(element).length).toBe(0) + }) + + return it('lets us hide tooltips programmatically', function () { + const disposable = manager.add(element, {title: 'Title'}) + return hover(element, function () { + expect(document.body.querySelector('.tooltip')).not.toBeNull() + manager.findTooltips(element)[0].hide() + expect(document.body.querySelector('.tooltip')).toBeNull() + return disposable.dispose() + }) + }) + }) + }) +}) From 7f75a46b97dfbae0ade622e8edf91f4afa72623c Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Tue, 24 Oct 2017 08:51:15 -0400 Subject: [PATCH 44/97] =?UTF-8?q?=F0=9F=91=95=20Fix=20"Return=20statement?= =?UTF-8?q?=20should=20not=20contain=20assignment"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spec/tooltip-manager-spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/tooltip-manager-spec.js b/spec/tooltip-manager-spec.js index c022db44a..222b5a766 100644 --- a/spec/tooltip-manager-spec.js +++ b/spec/tooltip-manager-spec.js @@ -17,7 +17,7 @@ describe('TooltipManager', function () { beforeEach(function () { manager = new TooltipManager({keymapManager: atom.keymaps, viewRegistry: atom.views}) - return element = createElement('foo') + element = createElement('foo') }) var createElement = function (className) { From f976c93d5ad52beebdc6a4e975a1ab20a226b281 Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Tue, 24 Oct 2017 08:51:40 -0400 Subject: [PATCH 45/97] :shirt: Fix "'disposable' is assigned a value but never used" --- spec/tooltip-manager-spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/tooltip-manager-spec.js b/spec/tooltip-manager-spec.js index 222b5a766..35988c24e 100644 --- a/spec/tooltip-manager-spec.js +++ b/spec/tooltip-manager-spec.js @@ -91,7 +91,7 @@ describe('TooltipManager', function () { describe("when the trigger is 'click'", () => it('shows and hides the tooltip when the target element is clicked', function () { - const disposable = manager.add(element, {title: 'Title', trigger: 'click'}) + manager.add(element, {title: 'Title', trigger: 'click'}) expect(document.body.querySelector('.tooltip')).toBeNull() element.click() expect(document.body.querySelector('.tooltip')).not.toBeNull() From 90cfb69c7cd291d5c920e739275a735edc17fc17 Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Tue, 24 Oct 2017 08:53:24 -0400 Subject: [PATCH 46/97] :shirt: Fix "'tooltipElement' is assigned a value but never used" --- spec/tooltip-manager-spec.js | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/tooltip-manager-spec.js b/spec/tooltip-manager-spec.js index 35988c24e..2380d2dc9 100644 --- a/spec/tooltip-manager-spec.js +++ b/spec/tooltip-manager-spec.js @@ -123,7 +123,6 @@ describe('TooltipManager', function () { }) it('allows a custom class to be specified for the tooltip', function () { - const tooltipElement = document.createElement('div') manager.add(element, {title: 'Title', class: 'custom-tooltip-class'}) return hover(element, () => expect(document.body.querySelector('.tooltip').classList.contains('custom-tooltip-class')).toBe(true)) }) From 76eb993e7e507ffbe88d2f60046f62d8e2bd69c2 Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Tue, 24 Oct 2017 08:56:20 -0400 Subject: [PATCH 47/97] :art: DS101 Remove unnecessary use of Array.from --- spec/tooltip-manager-spec.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spec/tooltip-manager-spec.js b/spec/tooltip-manager-spec.js index 2380d2dc9..683e08e45 100644 --- a/spec/tooltip-manager-spec.js +++ b/spec/tooltip-manager-spec.js @@ -1,6 +1,5 @@ /* * decaffeinate suggestions: - * DS101: Remove unnecessary use of Array.from * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ @@ -10,7 +9,7 @@ const Tooltip = require('../src/tooltip') const _ = require('underscore-plus') describe('TooltipManager', function () { - let [manager, element] = Array.from([]) + let manager, element const ctrlX = _.humanizeKeystroke('ctrl-x') const ctrlY = _.humanizeKeystroke('ctrl-y') From 706f7e3d4408285fc6efb86504d54c7543909e60 Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Tue, 24 Oct 2017 08:58:59 -0400 Subject: [PATCH 48/97] :art: DS102 Remove unnecessary code created because of implicit returns --- spec/tooltip-manager-spec.js | 65 +++++++++++++++++------------------- 1 file changed, 30 insertions(+), 35 deletions(-) diff --git a/spec/tooltip-manager-spec.js b/spec/tooltip-manager-spec.js index 683e08e45..0edcd646f 100644 --- a/spec/tooltip-manager-spec.js +++ b/spec/tooltip-manager-spec.js @@ -1,8 +1,3 @@ -/* - * decaffeinate suggestions: - * DS102: Remove unnecessary code created because of implicit returns - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ const {CompositeDisposable} = require('atom') const TooltipManager = require('../src/tooltip-manager') const Tooltip = require('../src/tooltip') @@ -28,12 +23,12 @@ describe('TooltipManager', function () { const mouseEnter = function (element) { element.dispatchEvent(new CustomEvent('mouseenter', {bubbles: false})) - return element.dispatchEvent(new CustomEvent('mouseover', {bubbles: true})) + element.dispatchEvent(new CustomEvent('mouseover', {bubbles: true})) } const mouseLeave = function (element) { element.dispatchEvent(new CustomEvent('mouseleave', {bubbles: false})) - return element.dispatchEvent(new CustomEvent('mouseout', {bubbles: true})) + element.dispatchEvent(new CustomEvent('mouseout', {bubbles: true})) } const hover = function (element, fn) { @@ -41,17 +36,17 @@ describe('TooltipManager', function () { advanceClock(manager.hoverDefaults.delay.show) fn() mouseLeave(element) - return advanceClock(manager.hoverDefaults.delay.hide) + advanceClock(manager.hoverDefaults.delay.hide) } - return describe('::add(target, options)', function () { + describe('::add(target, options)', function () { describe("when the trigger is 'hover' (the default)", function () { it('creates a tooltip when hovering over the target element', function () { manager.add(element, {title: 'Title'}) - return hover(element, () => expect(document.body.querySelector('.tooltip')).toHaveText('Title')) + hover(element, () => expect(document.body.querySelector('.tooltip')).toHaveText('Title')) }) - return it('displays tooltips immediately when hovering over new elements once a tooltip has been displayed once', function () { + it('displays tooltips immediately when hovering over new elements once a tooltip has been displayed once', function () { const disposables = new CompositeDisposable() const element1 = createElement('foo') disposables.add(manager.add(element1, {title: 'Title'})) @@ -75,7 +70,7 @@ describe('TooltipManager', function () { advanceClock(manager.hoverDefaults.delay.show) expect(document.body.querySelector('.tooltip')).not.toBeNull() - return disposables.dispose() + disposables.dispose() }) }) @@ -84,7 +79,7 @@ describe('TooltipManager', function () { const disposable = manager.add(element, {title: 'Title', trigger: 'manual'}) expect(document.body.querySelector('.tooltip')).toHaveText('Title') disposable.dispose() - return expect(document.body.querySelector('.tooltip')).toBeNull() + expect(document.body.querySelector('.tooltip')).toBeNull() }) ) @@ -111,19 +106,19 @@ describe('TooltipManager', function () { element.click() expect(document.body.querySelector('.tooltip')).not.toBeNull() element.click() - return expect(document.body.querySelector('.tooltip')).toBeNull() + expect(document.body.querySelector('.tooltip')).toBeNull() }) ) it('allows a custom item to be specified for the content of the tooltip', function () { const tooltipElement = document.createElement('div') manager.add(element, {item: {element: tooltipElement}}) - return hover(element, () => expect(tooltipElement.closest('.tooltip')).not.toBeNull()) + hover(element, () => expect(tooltipElement.closest('.tooltip')).not.toBeNull()) }) it('allows a custom class to be specified for the tooltip', function () { manager.add(element, {title: 'Title', class: 'custom-tooltip-class'}) - return hover(element, () => expect(document.body.querySelector('.tooltip').classList.contains('custom-tooltip-class')).toBe(true)) + hover(element, () => expect(document.body.querySelector('.tooltip').classList.contains('custom-tooltip-class')).toBe(true)) }) it('allows jQuery elements to be passed as the target', function () { @@ -142,7 +137,7 @@ describe('TooltipManager', function () { disposable.dispose() hover(element, () => expect(document.body.querySelector('.tooltip')).toBeNull()) - return hover(element2, () => expect(document.body.querySelector('.tooltip')).toBeNull()) + hover(element2, () => expect(document.body.querySelector('.tooltip')).toBeNull()) }) describe('when a keyBindingCommand is specified', function () { @@ -158,9 +153,9 @@ describe('TooltipManager', function () { manager.add(element, {title: 'Title', keyBindingCommand: 'test-command'}) - return hover(element, function () { + hover(element, function () { const tooltipElement = document.body.querySelector('.tooltip') - return expect(tooltipElement).toHaveText(`Title ${ctrlX} ${ctrlY}`) + expect(tooltipElement).toHaveText(`Title ${ctrlX} ${ctrlY}`) }) }) ) @@ -171,14 +166,14 @@ describe('TooltipManager', function () { manager.add(element, {keyBindingCommand: 'test-command'}) - return hover(element, function () { + hover(element, function () { const tooltipElement = document.body.querySelector('.tooltip') - return expect(tooltipElement).toHaveText(`${ctrlX} ${ctrlY}`) + expect(tooltipElement).toHaveText(`${ctrlX} ${ctrlY}`) }) }) ) - return describe('when a keyBindingTarget is specified', function () { + describe('when a keyBindingTarget is specified', function () { it('looks up the key binding relative to the target', function () { atom.keymaps.add('test', { '.bar': { 'ctrl-x ctrl-z': 'test-command' @@ -190,18 +185,18 @@ describe('TooltipManager', function () { manager.add(element, {keyBindingCommand: 'test-command', keyBindingTarget: element}) - return hover(element, function () { + hover(element, function () { const tooltipElement = document.body.querySelector('.tooltip') - return expect(tooltipElement).toHaveText(`${ctrlX} ${ctrlY}`) + expect(tooltipElement).toHaveText(`${ctrlX} ${ctrlY}`) }) }) - return it('does not display the keybinding if there is nothing mapped to the specified keyBindingCommand', function () { + it('does not display the keybinding if there is nothing mapped to the specified keyBindingCommand', function () { manager.add(element, {title: 'A Title', keyBindingCommand: 'test-command', keyBindingTarget: element}) - return hover(element, function () { + hover(element, function () { const tooltipElement = document.body.querySelector('.tooltip') - return expect(tooltipElement.textContent).toBe('A Title') + expect(tooltipElement.textContent).toBe('A Title') }) }) }) @@ -215,23 +210,23 @@ describe('TooltipManager', function () { disposable.dispose() - return hover(element, () => expect(document.body.querySelector('.tooltip')).toBeNull()) + hover(element, () => expect(document.body.querySelector('.tooltip')).toBeNull()) }) ) describe('when the window is resized', () => it('hides the tooltips', function () { const disposable = manager.add(element, {title: 'Title'}) - return hover(element, function () { + hover(element, function () { expect(document.body.querySelector('.tooltip')).not.toBeNull() window.dispatchEvent(new CustomEvent('resize')) expect(document.body.querySelector('.tooltip')).toBeNull() - return disposable.dispose() + disposable.dispose() }) }) ) - return describe('findTooltips', function () { + describe('findTooltips', function () { it('adds and remove tooltips correctly', function () { expect(manager.findTooltips(element).length).toBe(0) const disposable1 = manager.add(element, {title: 'elem1'}) @@ -241,16 +236,16 @@ describe('TooltipManager', function () { disposable1.dispose() expect(manager.findTooltips(element).length).toBe(1) disposable2.dispose() - return expect(manager.findTooltips(element).length).toBe(0) + expect(manager.findTooltips(element).length).toBe(0) }) - return it('lets us hide tooltips programmatically', function () { + it('lets us hide tooltips programmatically', function () { const disposable = manager.add(element, {title: 'Title'}) - return hover(element, function () { + hover(element, function () { expect(document.body.querySelector('.tooltip')).not.toBeNull() manager.findTooltips(element)[0].hide() expect(document.body.querySelector('.tooltip')).toBeNull() - return disposable.dispose() + disposable.dispose() }) }) }) From aa69409b1b576ea50eba2bbe3306a9d3d3979980 Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Tue, 24 Oct 2017 09:04:11 -0400 Subject: [PATCH 49/97] :art: Prefer arrow function syntax --- spec/tooltip-manager-spec.js | 44 ++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/spec/tooltip-manager-spec.js b/spec/tooltip-manager-spec.js index 0edcd646f..2f95299f3 100644 --- a/spec/tooltip-manager-spec.js +++ b/spec/tooltip-manager-spec.js @@ -3,7 +3,7 @@ const TooltipManager = require('../src/tooltip-manager') const Tooltip = require('../src/tooltip') const _ = require('underscore-plus') -describe('TooltipManager', function () { +describe('TooltipManager', () => { let manager, element const ctrlX = _.humanizeKeystroke('ctrl-x') @@ -39,14 +39,14 @@ describe('TooltipManager', function () { advanceClock(manager.hoverDefaults.delay.hide) } - describe('::add(target, options)', function () { - describe("when the trigger is 'hover' (the default)", function () { - it('creates a tooltip when hovering over the target element', function () { + describe('::add(target, options)', () => { + describe("when the trigger is 'hover' (the default)", () => { + it('creates a tooltip when hovering over the target element', () => { manager.add(element, {title: 'Title'}) hover(element, () => expect(document.body.querySelector('.tooltip')).toHaveText('Title')) }) - it('displays tooltips immediately when hovering over new elements once a tooltip has been displayed once', function () { + it('displays tooltips immediately when hovering over new elements once a tooltip has been displayed once', () => { const disposables = new CompositeDisposable() const element1 = createElement('foo') disposables.add(manager.add(element1, {title: 'Title'})) @@ -55,7 +55,7 @@ describe('TooltipManager', function () { const element3 = createElement('baz') disposables.add(manager.add(element3, {title: 'Title'})) - hover(element1, function () {}) + hover(element1, () => {}) expect(document.body.querySelector('.tooltip')).toBeNull() mouseEnter(element2) @@ -75,7 +75,7 @@ describe('TooltipManager', function () { }) describe("when the trigger is 'manual'", () => - it('creates a tooltip immediately and only hides it on dispose', function () { + it('creates a tooltip immediately and only hides it on dispose', () => { const disposable = manager.add(element, {title: 'Title', trigger: 'manual'}) expect(document.body.querySelector('.tooltip')).toHaveText('Title') disposable.dispose() @@ -84,7 +84,7 @@ describe('TooltipManager', function () { ) describe("when the trigger is 'click'", () => - it('shows and hides the tooltip when the target element is clicked', function () { + it('shows and hides the tooltip when the target element is clicked', () => { manager.add(element, {title: 'Title', trigger: 'click'}) expect(document.body.querySelector('.tooltip')).toBeNull() element.click() @@ -110,18 +110,18 @@ describe('TooltipManager', function () { }) ) - it('allows a custom item to be specified for the content of the tooltip', function () { + it('allows a custom item to be specified for the content of the tooltip', () => { const tooltipElement = document.createElement('div') manager.add(element, {item: {element: tooltipElement}}) hover(element, () => expect(tooltipElement.closest('.tooltip')).not.toBeNull()) }) - it('allows a custom class to be specified for the tooltip', function () { + it('allows a custom class to be specified for the tooltip', () => { manager.add(element, {title: 'Title', class: 'custom-tooltip-class'}) hover(element, () => expect(document.body.querySelector('.tooltip').classList.contains('custom-tooltip-class')).toBe(true)) }) - it('allows jQuery elements to be passed as the target', function () { + it('allows jQuery elements to be passed as the target', () => { const element2 = document.createElement('div') jasmine.attachToDOM(element2) @@ -140,9 +140,9 @@ describe('TooltipManager', function () { hover(element2, () => expect(document.body.querySelector('.tooltip')).toBeNull()) }) - describe('when a keyBindingCommand is specified', function () { + describe('when a keyBindingCommand is specified', () => { describe('when a title is specified', () => - it('appends the key binding corresponding to the command to the title', function () { + it('appends the key binding corresponding to the command to the title', () => { atom.keymaps.add('test', { '.foo': { 'ctrl-x ctrl-y': 'test-command' }, @@ -161,7 +161,7 @@ describe('TooltipManager', function () { ) describe('when no title is specified', () => - it('shows the key binding corresponding to the command alone', function () { + it('shows the key binding corresponding to the command alone', () => { atom.keymaps.add('test', {'.foo': {'ctrl-x ctrl-y': 'test-command'}}) manager.add(element, {keyBindingCommand: 'test-command'}) @@ -173,8 +173,8 @@ describe('TooltipManager', function () { }) ) - describe('when a keyBindingTarget is specified', function () { - it('looks up the key binding relative to the target', function () { + describe('when a keyBindingTarget is specified', () => { + it('looks up the key binding relative to the target', () => { atom.keymaps.add('test', { '.bar': { 'ctrl-x ctrl-z': 'test-command' }, @@ -191,7 +191,7 @@ describe('TooltipManager', function () { }) }) - it('does not display the keybinding if there is nothing mapped to the specified keyBindingCommand', function () { + it('does not display the keybinding if there is nothing mapped to the specified keyBindingCommand', () => { manager.add(element, {title: 'A Title', keyBindingCommand: 'test-command', keyBindingTarget: element}) hover(element, function () { @@ -203,7 +203,7 @@ describe('TooltipManager', function () { }) describe('when .dispose() is called on the returned disposable', () => - it('no longer displays the tooltip on hover', function () { + it('no longer displays the tooltip on hover', () => { const disposable = manager.add(element, {title: 'Title'}) hover(element, () => expect(document.body.querySelector('.tooltip')).toHaveText('Title')) @@ -215,7 +215,7 @@ describe('TooltipManager', function () { ) describe('when the window is resized', () => - it('hides the tooltips', function () { + it('hides the tooltips', () => { const disposable = manager.add(element, {title: 'Title'}) hover(element, function () { expect(document.body.querySelector('.tooltip')).not.toBeNull() @@ -226,8 +226,8 @@ describe('TooltipManager', function () { }) ) - describe('findTooltips', function () { - it('adds and remove tooltips correctly', function () { + describe('findTooltips', () => { + it('adds and remove tooltips correctly', () => { expect(manager.findTooltips(element).length).toBe(0) const disposable1 = manager.add(element, {title: 'elem1'}) expect(manager.findTooltips(element).length).toBe(1) @@ -239,7 +239,7 @@ describe('TooltipManager', function () { expect(manager.findTooltips(element).length).toBe(0) }) - it('lets us hide tooltips programmatically', function () { + it('lets us hide tooltips programmatically', () => { const disposable = manager.add(element, {title: 'Title'}) hover(element, function () { expect(document.body.querySelector('.tooltip')).not.toBeNull() From fc620b9e80d67ca99f962431461b8fc4d085d9df Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Tue, 24 Oct 2017 09:06:50 -0400 Subject: [PATCH 50/97] :art: Move helper functions outside of `describe` block --- spec/tooltip-manager-spec.js | 44 ++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/spec/tooltip-manager-spec.js b/spec/tooltip-manager-spec.js index 2f95299f3..65587839f 100644 --- a/spec/tooltip-manager-spec.js +++ b/spec/tooltip-manager-spec.js @@ -9,28 +9,6 @@ describe('TooltipManager', () => { const ctrlX = _.humanizeKeystroke('ctrl-x') const ctrlY = _.humanizeKeystroke('ctrl-y') - beforeEach(function () { - manager = new TooltipManager({keymapManager: atom.keymaps, viewRegistry: atom.views}) - element = createElement('foo') - }) - - var createElement = function (className) { - const el = document.createElement('div') - el.classList.add(className) - jasmine.attachToDOM(el) - return el - } - - const mouseEnter = function (element) { - element.dispatchEvent(new CustomEvent('mouseenter', {bubbles: false})) - element.dispatchEvent(new CustomEvent('mouseover', {bubbles: true})) - } - - const mouseLeave = function (element) { - element.dispatchEvent(new CustomEvent('mouseleave', {bubbles: false})) - element.dispatchEvent(new CustomEvent('mouseout', {bubbles: true})) - } - const hover = function (element, fn) { mouseEnter(element) advanceClock(manager.hoverDefaults.delay.show) @@ -39,6 +17,11 @@ describe('TooltipManager', () => { advanceClock(manager.hoverDefaults.delay.hide) } + beforeEach(function () { + manager = new TooltipManager({keymapManager: atom.keymaps, viewRegistry: atom.views}) + element = createElement('foo') + }) + describe('::add(target, options)', () => { describe("when the trigger is 'hover' (the default)", () => { it('creates a tooltip when hovering over the target element', () => { @@ -251,3 +234,20 @@ describe('TooltipManager', () => { }) }) }) + +function createElement (className) { + const el = document.createElement('div') + el.classList.add(className) + jasmine.attachToDOM(el) + return el +} + +function mouseEnter (element) { + element.dispatchEvent(new CustomEvent('mouseenter', {bubbles: false})) + element.dispatchEvent(new CustomEvent('mouseover', {bubbles: true})) +} + +function mouseLeave (element) { + element.dispatchEvent(new CustomEvent('mouseleave', {bubbles: false})) + element.dispatchEvent(new CustomEvent('mouseout', {bubbles: true})) +} From e42435208f933d8f119a3e3d6abd97dd944a89a5 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Wed, 25 Oct 2017 00:01:42 +0200 Subject: [PATCH 51/97] :arrow_up: apm@1.18.10 --- apm/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apm/package.json b/apm/package.json index e759a39d2..336544d3e 100644 --- a/apm/package.json +++ b/apm/package.json @@ -6,6 +6,6 @@ "url": "https://github.com/atom/atom.git" }, "dependencies": { - "atom-package-manager": "1.18.9" + "atom-package-manager": "1.18.10" } } From 54a67b60cbcad3eb001c4e7dc6f4235cb7cfeaca Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Wed, 25 Oct 2017 14:49:04 +0200 Subject: [PATCH 52/97] :arrow_up: language-ruby@0.71.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4056b0d71..783076c91 100644 --- a/package.json +++ b/package.json @@ -157,7 +157,7 @@ "language-php": "0.42.1", "language-property-list": "0.9.1", "language-python": "0.45.4", - "language-ruby": "0.71.3", + "language-ruby": "0.71.4", "language-ruby-on-rails": "0.25.2", "language-sass": "0.61.1", "language-shellscript": "0.25.3", From 51349a79fb44c450f14f6067e415cbe6cbef59fc Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Wed, 25 Oct 2017 14:49:54 +0200 Subject: [PATCH 53/97] :arrow_up: language-hyperlink@0.16.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 783076c91..25f4e0c5a 100644 --- a/package.json +++ b/package.json @@ -145,7 +145,7 @@ "language-git": "0.19.1", "language-go": "0.44.2", "language-html": "0.48.1", - "language-hyperlink": "0.16.2", + "language-hyperlink": "0.16.3", "language-java": "0.27.4", "language-javascript": "0.127.5", "language-json": "0.19.1", From c1ec73602f55871ffb87edf653f246223d2677e8 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Wed, 25 Oct 2017 14:50:26 +0200 Subject: [PATCH 54/97] :arrow_up: language-todo@0.29.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 25f4e0c5a..a865c288d 100644 --- a/package.json +++ b/package.json @@ -164,7 +164,7 @@ "language-source": "0.9.0", "language-sql": "0.25.8", "language-text": "0.7.3", - "language-todo": "0.29.2", + "language-todo": "0.29.3", "language-toml": "0.18.1", "language-typescript": "0.2.2", "language-xml": "0.35.2", From 287d98b321db215033eeaea47b479d4a356ba416 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Wed, 25 Oct 2017 14:51:08 +0200 Subject: [PATCH 55/97] :arrow_up: language-javascript@0.127.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a865c288d..3fe5b5235 100644 --- a/package.json +++ b/package.json @@ -147,7 +147,7 @@ "language-html": "0.48.1", "language-hyperlink": "0.16.3", "language-java": "0.27.4", - "language-javascript": "0.127.5", + "language-javascript": "0.127.6", "language-json": "0.19.1", "language-less": "0.33.1", "language-make": "0.22.3", From fff1c06c50bbf8964ed9e1df4a3a74d2318e1b27 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Wed, 25 Oct 2017 14:51:39 +0200 Subject: [PATCH 56/97] :arrow_up: language-go@0.44.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3fe5b5235..3aba6812c 100644 --- a/package.json +++ b/package.json @@ -143,7 +143,7 @@ "language-css": "0.42.6", "language-gfm": "0.90.2", "language-git": "0.19.1", - "language-go": "0.44.2", + "language-go": "0.44.3", "language-html": "0.48.1", "language-hyperlink": "0.16.3", "language-java": "0.27.4", From b525b7212bd6399aeb8de4cc945e05cf4e7b179b Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Wed, 25 Oct 2017 14:52:30 +0200 Subject: [PATCH 57/97] :arrow_up: language-php@0.42.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3aba6812c..498e17b2b 100644 --- a/package.json +++ b/package.json @@ -154,7 +154,7 @@ "language-mustache": "0.14.3", "language-objective-c": "0.15.1", "language-perl": "0.37.0", - "language-php": "0.42.1", + "language-php": "0.42.2", "language-property-list": "0.9.1", "language-python": "0.45.4", "language-ruby": "0.71.4", From 2aca4268a5466fc7ea77e7f391b20ed778d0d840 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Wed, 25 Oct 2017 14:52:57 +0200 Subject: [PATCH 58/97] :arrow_up: language-yaml@0.31.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 498e17b2b..384b4902f 100644 --- a/package.json +++ b/package.json @@ -168,7 +168,7 @@ "language-toml": "0.18.1", "language-typescript": "0.2.2", "language-xml": "0.35.2", - "language-yaml": "0.31.0" + "language-yaml": "0.31.1" }, "private": true, "scripts": { From 5173b8f23fd38cfba452a2e6ad3838fa0368dbfa Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Wed, 25 Oct 2017 14:53:51 +0200 Subject: [PATCH 59/97] :arrow_up: language-css@0.42.7 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 384b4902f..1e6352564 100644 --- a/package.json +++ b/package.json @@ -140,7 +140,7 @@ "language-clojure": "0.22.4", "language-coffee-script": "0.49.1", "language-csharp": "0.14.3", - "language-css": "0.42.6", + "language-css": "0.42.7", "language-gfm": "0.90.2", "language-git": "0.19.1", "language-go": "0.44.3", From 946b4be5cfd3659876f6963a55899b55f9d0ddd2 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Wed, 25 Oct 2017 14:54:32 +0200 Subject: [PATCH 60/97] :arrow_up: language-sass@0.61.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1e6352564..d82792259 100644 --- a/package.json +++ b/package.json @@ -159,7 +159,7 @@ "language-python": "0.45.4", "language-ruby": "0.71.4", "language-ruby-on-rails": "0.25.2", - "language-sass": "0.61.1", + "language-sass": "0.61.2", "language-shellscript": "0.25.3", "language-source": "0.9.0", "language-sql": "0.25.8", From f83a8f7e7e631b944ea336e0b7173494ccce6168 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Wed, 25 Oct 2017 14:55:06 +0200 Subject: [PATCH 61/97] :arrow_up: language-shellscript@0.25.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d82792259..ba5140094 100644 --- a/package.json +++ b/package.json @@ -160,7 +160,7 @@ "language-ruby": "0.71.4", "language-ruby-on-rails": "0.25.2", "language-sass": "0.61.2", - "language-shellscript": "0.25.3", + "language-shellscript": "0.25.4", "language-source": "0.9.0", "language-sql": "0.25.8", "language-text": "0.7.3", From d474ddcd746a8bbe84bfe5385f7fc5a1e43e155e Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Wed, 25 Oct 2017 14:55:46 +0200 Subject: [PATCH 62/97] :arrow_up: language-coffee-script@0.49.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ba5140094..629019363 100644 --- a/package.json +++ b/package.json @@ -138,7 +138,7 @@ "wrap-guide": "0.40.2", "language-c": "0.58.1", "language-clojure": "0.22.4", - "language-coffee-script": "0.49.1", + "language-coffee-script": "0.49.2", "language-csharp": "0.14.3", "language-css": "0.42.7", "language-gfm": "0.90.2", From e76eee10e3d40a5f5b1653d913aaaba226715123 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Wed, 25 Oct 2017 14:56:30 +0200 Subject: [PATCH 63/97] :arrow_up: language-python@0.45.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 629019363..a83d0b694 100644 --- a/package.json +++ b/package.json @@ -156,7 +156,7 @@ "language-perl": "0.37.0", "language-php": "0.42.2", "language-property-list": "0.9.1", - "language-python": "0.45.4", + "language-python": "0.45.5", "language-ruby": "0.71.4", "language-ruby-on-rails": "0.25.2", "language-sass": "0.61.2", From 6dd28c0b37e3d6440bffcc5f6f4fe2ee695f226f Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Wed, 25 Oct 2017 14:56:58 +0200 Subject: [PATCH 64/97] :arrow_up: language-java@0.27.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a83d0b694..ea9b9b8e6 100644 --- a/package.json +++ b/package.json @@ -146,7 +146,7 @@ "language-go": "0.44.3", "language-html": "0.48.1", "language-hyperlink": "0.16.3", - "language-java": "0.27.4", + "language-java": "0.27.5", "language-javascript": "0.127.6", "language-json": "0.19.1", "language-less": "0.33.1", From 13ecc8a2280e47cdf5491962afe4d754cdf4d388 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Wed, 25 Oct 2017 14:58:42 +0200 Subject: [PATCH 65/97] :arrow_up: language-mustache@0.14.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ea9b9b8e6..2919dcceb 100644 --- a/package.json +++ b/package.json @@ -151,7 +151,7 @@ "language-json": "0.19.1", "language-less": "0.33.1", "language-make": "0.22.3", - "language-mustache": "0.14.3", + "language-mustache": "0.14.4", "language-objective-c": "0.15.1", "language-perl": "0.37.0", "language-php": "0.42.2", From dfd1e715bf6e845c955a1b4a3d616ede953b2598 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Wed, 25 Oct 2017 14:59:25 +0200 Subject: [PATCH 66/97] :arrow_up: language-html@0.48.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2919dcceb..3902ffa30 100644 --- a/package.json +++ b/package.json @@ -144,7 +144,7 @@ "language-gfm": "0.90.2", "language-git": "0.19.1", "language-go": "0.44.3", - "language-html": "0.48.1", + "language-html": "0.48.2", "language-hyperlink": "0.16.3", "language-java": "0.27.5", "language-javascript": "0.127.6", From 7b7ddb9eb989afa7ed15efcbf1da0f5021eb6906 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Wed, 25 Oct 2017 15:00:00 +0200 Subject: [PATCH 67/97] :arrow_up: language-less@0.34.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3902ffa30..2887ec8bf 100644 --- a/package.json +++ b/package.json @@ -149,7 +149,7 @@ "language-java": "0.27.5", "language-javascript": "0.127.6", "language-json": "0.19.1", - "language-less": "0.33.1", + "language-less": "0.34.0", "language-make": "0.22.3", "language-mustache": "0.14.4", "language-objective-c": "0.15.1", From 2bf9e4b0c7b1a4a9ba45b6ce78a69a4f06023ac6 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Wed, 25 Oct 2017 17:36:21 +0200 Subject: [PATCH 68/97] Use scope names rather than names Some languages are not guaranteed to have names --- spec/workspace-spec.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/spec/workspace-spec.js b/spec/workspace-spec.js index 43a04eba9..1bde0e6fe 100644 --- a/spec/workspace-spec.js +++ b/spec/workspace-spec.js @@ -1585,15 +1585,15 @@ i = /test/; #FIXME\ atom2.project.deserialize(atom.project.serialize()) atom2.workspace.deserialize(atom.workspace.serialize(), atom2.deserializers) - expect(atom2.grammars.getGrammars().map(grammar => grammar.name).sort()).toEqual([ - 'CoffeeScript', - 'CoffeeScript (Literate)', - 'JSDoc', - 'JavaScript', - 'Null Grammar', - 'Regular Expression Replacement (JavaScript)', - 'Regular Expressions (JavaScript)', - 'TODO' + expect(atom2.grammars.getGrammars().map(grammar => grammar.scopeName).sort()).toEqual([ + 'source.coffee', + 'source.js', + 'source.js.regexp', + 'source.js.regexp.replacement', + 'source.jsdoc', + 'source.litcoffee', + 'text.plain.null-grammar', + 'text.todo' ]) atom2.destroy() From 5fc8563fe56c2962a1267cdc0fa786db0b74352d Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Wed, 25 Oct 2017 18:03:45 +0200 Subject: [PATCH 69/97] :arrow_up: grammar-selector@0.49.7 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2887ec8bf..acf21aca8 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "github": "0.7.0", "git-diff": "1.3.6", "go-to-line": "0.32.1", - "grammar-selector": "0.49.6", + "grammar-selector": "0.49.7", "image-view": "0.62.4", "incompatible-packages": "0.27.3", "keybinding-resolver": "0.38.0", From 364964ea0a1bf0a5b5ca612e2ee8be10bbdf8db2 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 25 Oct 2017 10:34:43 -0600 Subject: [PATCH 70/97] Always assign a project path outside of bundle for legacy package specs This prevents package specs that don't have a fixtures directory from attempting to read files out of a non-existent directory inside the ASAR bundle, which causes ENOTDIR errors in superstring. If the spec does not have a parent folder containing a fixtures directory, we now set the default project path to `os.tmpdir()`. --- spec/spec-helper.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/spec-helper.coffee b/spec/spec-helper.coffee index c20bfc827..7621f9cae 100644 --- a/spec/spec-helper.coffee +++ b/spec/spec-helper.coffee @@ -58,7 +58,7 @@ if specPackagePath = FindParentDir.sync(testPaths[0], 'package.json') if specDirectory = FindParentDir.sync(testPaths[0], 'fixtures') specProjectPath = path.join(specDirectory, 'fixtures') else - specProjectPath = path.join(__dirname, 'fixtures') + specProjectPath = require('os').tmpdir() beforeEach -> atom.project.setPaths([specProjectPath]) From 00242541aed5edb03cfdaa7570c6f84011b28b32 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 25 Oct 2017 12:15:33 -0600 Subject: [PATCH 71/97] Don't destroy folds that are completely contained within a selection --- package.json | 2 +- spec/text-editor-spec.coffee | 5 ++++- src/selection.coffee | 2 +- src/text-editor.coffee | 7 ++++++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 4056b0d71..4e1216846 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "service-hub": "^0.7.4", "sinon": "1.17.4", "temp": "^0.8.3", - "text-buffer": "13.5.8", + "text-buffer": "13.6.0-0", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "winreg": "^1.2.1", diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index bc74cd443..5bb010321 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -1871,7 +1871,7 @@ describe "TextEditor", -> expect(selection1.getBufferRange()).toEqual [[2, 2], [3, 3]] describe "when the 'preserveFolds' option is false (the default)", -> - it "removes folds that contain the selections", -> + it "removes folds that contain one or both of the selection's end points", -> editor.setSelectedBufferRange([[0, 0], [0, 0]]) editor.foldBufferRowRange(1, 4) editor.foldBufferRowRange(2, 3) @@ -1884,6 +1884,9 @@ describe "TextEditor", -> expect(editor.isFoldedAtScreenRow(6)).toBeFalsy() expect(editor.isFoldedAtScreenRow(10)).toBeTruthy() + editor.setSelectedBufferRange([[10, 0], [12, 0]]) + expect(editor.isFoldedAtScreenRow(10)).toBeTruthy() + describe "when the 'preserveFolds' option is true", -> it "does not remove folds that contain the selections", -> editor.setSelectedBufferRange([[0, 0], [0, 0]]) diff --git a/src/selection.coffee b/src/selection.coffee index 6fcf8dd36..0907888d6 100644 --- a/src/selection.coffee +++ b/src/selection.coffee @@ -87,7 +87,7 @@ class Selection extends Model setBufferRange: (bufferRange, options={}) -> bufferRange = Range.fromObject(bufferRange) options.reversed ?= @isReversed() - @editor.destroyFoldsIntersectingBufferRange(bufferRange) unless options.preserveFolds + @editor.destroyFoldsContainingBufferPositions([bufferRange.start, bufferRange.end]) unless options.preserveFolds @modifySelection => needsFlash = options.flash delete options.flash if options.flash? diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 32dd49a18..400d48f97 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -2495,8 +2495,9 @@ class TextEditor extends Model # # Returns the added {Selection}. addSelectionForBufferRange: (bufferRange, options={}) -> + bufferRange = Range.fromObject(bufferRange) unless options.preserveFolds - @destroyFoldsIntersectingBufferRange(bufferRange) + @displayLayer.destroyFoldsContainingBufferPositions([bufferRange.start, bufferRange.end]) @selectionsMarkerLayer.markBufferRange(bufferRange, {invalidate: 'never', reversed: options.reversed ? false}) @getLastSelection().autoscroll() unless options.autoscroll is false @getLastSelection() @@ -3446,6 +3447,10 @@ class TextEditor extends Model destroyFoldsIntersectingBufferRange: (bufferRange) -> @displayLayer.destroyFoldsIntersectingBufferRange(bufferRange) + # Remove any {Fold}s found that intersect the given array of buffer positions. + destroyFoldsContainingBufferPositions: (bufferPositions) -> + @displayLayer.destroyFoldsContainingBufferPositions(bufferPositions) + ### Section: Gutters ### From 2189bd502c73a79d30ba8f8712213ce264b59fe6 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Wed, 25 Oct 2017 20:33:52 +0200 Subject: [PATCH 72/97] :arrow_up: grammar-selector@0.49.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index acf21aca8..f09178dff 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "github": "0.7.0", "git-diff": "1.3.6", "go-to-line": "0.32.1", - "grammar-selector": "0.49.7", + "grammar-selector": "0.49.8", "image-view": "0.62.4", "incompatible-packages": "0.27.3", "keybinding-resolver": "0.38.0", From 577911179969cc8fc51e36f416eda05765d03962 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 25 Oct 2017 12:48:20 -0600 Subject: [PATCH 73/97] Use destroyFoldsContainingBufferPosition in more cases --- package.json | 2 +- src/selection.coffee | 2 +- src/text-editor-component.js | 2 +- src/text-editor.coffee | 13 ++++++------- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 4e1216846..a447d53b5 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "service-hub": "^0.7.4", "sinon": "1.17.4", "temp": "^0.8.3", - "text-buffer": "13.6.0-0", + "text-buffer": "13.6.0-2", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "winreg": "^1.2.1", diff --git a/src/selection.coffee b/src/selection.coffee index 0907888d6..cb45286b8 100644 --- a/src/selection.coffee +++ b/src/selection.coffee @@ -87,7 +87,7 @@ class Selection extends Model setBufferRange: (bufferRange, options={}) -> bufferRange = Range.fromObject(bufferRange) options.reversed ?= @isReversed() - @editor.destroyFoldsContainingBufferPositions([bufferRange.start, bufferRange.end]) unless options.preserveFolds + @editor.destroyFoldsContainingBufferPositions([bufferRange.start, bufferRange.end], true) unless options.preserveFolds @modifySelection => needsFlash = options.flash delete options.flash if options.flash? diff --git a/src/text-editor-component.js b/src/text-editor-component.js index 641cdad02..f19b7e31c 100644 --- a/src/text-editor-component.js +++ b/src/text-editor-component.js @@ -1771,7 +1771,7 @@ class TextEditorComponent { if (target && target.matches('.fold-marker')) { const bufferPosition = model.bufferPositionForScreenPosition(screenPosition) - model.destroyFoldsIntersectingBufferRange(Range(bufferPosition, bufferPosition)) + model.destroyFoldsContainingBufferPositions([bufferPosition], false) return } diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 400d48f97..dd359df9e 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -2497,7 +2497,7 @@ class TextEditor extends Model addSelectionForBufferRange: (bufferRange, options={}) -> bufferRange = Range.fromObject(bufferRange) unless options.preserveFolds - @displayLayer.destroyFoldsContainingBufferPositions([bufferRange.start, bufferRange.end]) + @displayLayer.destroyFoldsContainingBufferPositions([bufferRange.start, bufferRange.end], true) @selectionsMarkerLayer.markBufferRange(bufferRange, {invalidate: 'never', reversed: options.reversed ? false}) @getLastSelection().autoscroll() unless options.autoscroll is false @getLastSelection() @@ -3318,8 +3318,7 @@ class TextEditor extends Model # Essential: Unfold the most recent cursor's row by one level. unfoldCurrentRow: -> {row} = @getCursorBufferPosition() - position = Point(row, Infinity) - @displayLayer.destroyFoldsIntersectingBufferRange(Range(position, position)) + @displayLayer.destroyFoldsContainingBufferPositions([Point(row, Infinity)], false) # Essential: Fold the given row in buffer coordinates based on its indentation # level. @@ -3348,7 +3347,7 @@ class TextEditor extends Model # * `bufferRow` A {Number} unfoldBufferRow: (bufferRow) -> position = Point(bufferRow, Infinity) - @displayLayer.destroyFoldsIntersectingBufferRange(Range(position, position)) + @displayLayer.destroyFoldsContainingBufferPositions([position]) # Extended: For each selection, fold the rows it intersects. foldSelectedLines: -> @@ -3447,9 +3446,9 @@ class TextEditor extends Model destroyFoldsIntersectingBufferRange: (bufferRange) -> @displayLayer.destroyFoldsIntersectingBufferRange(bufferRange) - # Remove any {Fold}s found that intersect the given array of buffer positions. - destroyFoldsContainingBufferPositions: (bufferPositions) -> - @displayLayer.destroyFoldsContainingBufferPositions(bufferPositions) + # Remove any {Fold}s found that contain the given array of buffer positions. + destroyFoldsContainingBufferPositions: (bufferPositions, excludeEndpoints) -> + @displayLayer.destroyFoldsContainingBufferPositions(bufferPositions, excludeEndpoints) ### Section: Gutters From 1722273630bf8b306ac34bb7b4b38ffa40135cab Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 25 Oct 2017 12:52:31 -0700 Subject: [PATCH 74/97] :arrow_up: autocomplete-plus --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f09178dff..6c1d675cc 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,7 @@ "autocomplete-atom-api": "0.10.3", "autocomplete-css": "0.17.3", "autocomplete-html": "0.8.2", - "autocomplete-plus": "2.36.8", + "autocomplete-plus": "2.37.0", "autocomplete-snippets": "1.11.2", "autoflow": "0.29.0", "autosave": "0.24.6", From ba7c3e57f51d2e290390715981a1b3648727625b Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 25 Oct 2017 13:19:13 -0700 Subject: [PATCH 75/97] :arrow_up: text-buffer for new onWillChange behavior --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6c1d675cc..afba9de19 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "service-hub": "^0.7.4", "sinon": "1.17.4", "temp": "^0.8.3", - "text-buffer": "13.5.8", + "text-buffer": "13.6.0-will-change-event-1", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "winreg": "^1.2.1", From 197425ffe42ac1ded431694b5d99907a8bb36ff8 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 25 Oct 2017 14:47:29 -0700 Subject: [PATCH 76/97] :arrow_up: text-buffer (prerelease) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index afba9de19..78c4847b3 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "service-hub": "^0.7.4", "sinon": "1.17.4", "temp": "^0.8.3", - "text-buffer": "13.6.0-will-change-event-1", + "text-buffer": "13.6.0-will-change-event-2", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "winreg": "^1.2.1", From d715f3ee2743c48c830ca9b2859efea9fedc71df Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 25 Oct 2017 16:35:02 -0600 Subject: [PATCH 77/97] :arrow_up: text-buffer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a447d53b5..43446f242 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "service-hub": "^0.7.4", "sinon": "1.17.4", "temp": "^0.8.3", - "text-buffer": "13.6.0-2", + "text-buffer": "13.6.0", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "winreg": "^1.2.1", From 3e9c6601a252435be2488074a2742162c6bb9a93 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 25 Oct 2017 20:30:42 -0600 Subject: [PATCH 78/97] :arrow_up: markdown-preview --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7f4c4bbce..6578f7392 100644 --- a/package.json +++ b/package.json @@ -118,7 +118,7 @@ "keybinding-resolver": "0.38.0", "line-ending-selector": "0.7.4", "link": "0.31.3", - "markdown-preview": "0.159.16", + "markdown-preview": "0.159.17", "metrics": "1.2.6", "notifications": "0.69.2", "open-on-github": "1.2.1", From 6ad37f08d3b4f38cbb9c025b1e07be1399b02d1c Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Thu, 26 Oct 2017 10:50:59 +0200 Subject: [PATCH 79/97] :arrow_up: autocomplete-css@0.17.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6578f7392..b34cce317 100644 --- a/package.json +++ b/package.json @@ -92,7 +92,7 @@ "about": "1.7.8", "archive-view": "0.63.4", "autocomplete-atom-api": "0.10.3", - "autocomplete-css": "0.17.3", + "autocomplete-css": "0.17.4", "autocomplete-html": "0.8.2", "autocomplete-plus": "2.37.0", "autocomplete-snippets": "1.11.2", From 1f5565fec748c2d2a24967e9cb885a5ac4ff87ad Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Thu, 26 Oct 2017 10:55:03 +0200 Subject: [PATCH 80/97] :arrow_up: autocomplete-html@0.8.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b34cce317..d5bea7833 100644 --- a/package.json +++ b/package.json @@ -93,7 +93,7 @@ "archive-view": "0.63.4", "autocomplete-atom-api": "0.10.3", "autocomplete-css": "0.17.4", - "autocomplete-html": "0.8.2", + "autocomplete-html": "0.8.3", "autocomplete-plus": "2.37.0", "autocomplete-snippets": "1.11.2", "autoflow": "0.29.0", From 9ae36efc2fa2a5ad01aabf948ffffa8cf52f73e4 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Thu, 26 Oct 2017 10:59:05 +0200 Subject: [PATCH 81/97] :arrow_up: autocomplete-atom-api@0.10.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d5bea7833..6ad2f68b8 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "solarized-light-syntax": "1.1.2", "about": "1.7.8", "archive-view": "0.63.4", - "autocomplete-atom-api": "0.10.3", + "autocomplete-atom-api": "0.10.5", "autocomplete-css": "0.17.4", "autocomplete-html": "0.8.3", "autocomplete-plus": "2.37.0", From a32f1c3684a9de77af1b6dbd9e133a8b3b30c904 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 26 Oct 2017 06:52:23 -0600 Subject: [PATCH 82/97] :arrow_up: settings-view --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6578f7392..eaf1951a5 100644 --- a/package.json +++ b/package.json @@ -123,7 +123,7 @@ "notifications": "0.69.2", "open-on-github": "1.2.1", "package-generator": "1.1.1", - "settings-view": "0.252.1", + "settings-view": "0.252.2", "snippets": "1.1.6", "spell-check": "0.72.3", "status-bar": "1.8.13", From f21ede2da253c0f4c32bba5e8706daf459bf2336 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 26 Oct 2017 09:59:11 -0700 Subject: [PATCH 83/97] :arrow_up: text-buffer for new onDidChange behavior --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 78e75f11b..6cae9962e 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "service-hub": "^0.7.4", "sinon": "1.17.4", "temp": "^0.8.3", - "text-buffer": "13.6.1", + "text-buffer": "13.7.0-did-change-event-1", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "winreg": "^1.2.1", From 32e9547558c60c283225e461d3b588a76f15a344 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 26 Oct 2017 14:36:42 -0600 Subject: [PATCH 84/97] :arrow_up: tree-view /cc @Alhadis --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 418a9da02..96e7330fd 100644 --- a/package.json +++ b/package.json @@ -131,7 +131,7 @@ "symbols-view": "0.118.1", "tabs": "0.108.0", "timecop": "0.36.0", - "tree-view": "0.220.0", + "tree-view": "0.221.0", "update-package-dependencies": "0.12.0", "welcome": "0.36.5", "whitespace": "0.37.4", From 590302572621abd6a84b70f6b5dc3a2d5a268e8c Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 26 Oct 2017 14:29:24 -0700 Subject: [PATCH 85/97] :arrow_up: autocomplete-plus --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6cae9962e..494654b87 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,7 @@ "autocomplete-atom-api": "0.10.5", "autocomplete-css": "0.17.4", "autocomplete-html": "0.8.3", - "autocomplete-plus": "2.37.0", + "autocomplete-plus": "2.37.1", "autocomplete-snippets": "1.11.2", "autoflow": "0.29.0", "autosave": "0.24.6", From ada645aaa16c51b49357b161d81af51903d07cc3 Mon Sep 17 00:00:00 2001 From: Justin Ratner Date: Thu, 26 Oct 2017 15:31:43 -0600 Subject: [PATCH 86/97] fix optimizer bailing on performDocumentUpdate --- src/view-registry.js | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/view-registry.js b/src/view-registry.js index dcc1624fc..87bf8620f 100644 --- a/src/view-registry.js +++ b/src/view-registry.js @@ -233,16 +233,26 @@ class ViewRegistry { this.nextUpdatePromise = null this.resolveNextUpdatePromise = null - let writer - while ((writer = this.documentWriters.shift())) { writer() } + var writer = this.documentWriters.shift() + while (writer) { + writer() + writer = this.documentWriters.shift() + } - let reader + var reader = this.documentReaders.shift() this.documentReadInProgress = true - while ((reader = this.documentReaders.shift())) { reader() } + while (reader) { + reader() + reader = this.documentReaders.shift() + } this.documentReadInProgress = false // process updates requested as a result of reads - while ((writer = this.documentWriters.shift())) { writer() } + writer = this.documentWriters.shift() + while (writer) { + writer() + writer = this.documentWriters.shift() + } if (resolveNextUpdatePromise) { resolveNextUpdatePromise() } } From ab07a6ec63b37719f1b5454ef485643728889ee5 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 26 Oct 2017 14:56:17 -0700 Subject: [PATCH 87/97] :arrow_up: text-buffer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 494654b87..8ea46bba2 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "service-hub": "^0.7.4", "sinon": "1.17.4", "temp": "^0.8.3", - "text-buffer": "13.7.0-did-change-event-1", + "text-buffer": "13.7.0", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "winreg": "^1.2.1", From 2f0fb1798240c1c2468a37be87610d67e640cf2d Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 26 Oct 2017 15:39:35 -0700 Subject: [PATCH 88/97] :arrow_up: autocomplete-plus --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b5343381f..491795d55 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,7 @@ "autocomplete-atom-api": "0.10.5", "autocomplete-css": "0.17.4", "autocomplete-html": "0.8.3", - "autocomplete-plus": "2.37.1", + "autocomplete-plus": "2.37.2", "autocomplete-snippets": "1.11.2", "autoflow": "0.29.0", "autosave": "0.24.6", From 67dc7c745ecc6ec3c09dbdb346bee05170b8a065 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 26 Oct 2017 16:53:16 -0700 Subject: [PATCH 89/97] :arrow_up: text-buffer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 491795d55..f800b1b3f 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "service-hub": "^0.7.4", "sinon": "1.17.4", "temp": "^0.8.3", - "text-buffer": "13.7.0", + "text-buffer": "13.7.1", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "winreg": "^1.2.1", From e4044699dc18903a50cfffa45f733544fa60a165 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Fri, 27 Oct 2017 21:49:27 +0200 Subject: [PATCH 90/97] :memo: [ci skip] --- src/workspace.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/workspace.js b/src/workspace.js index 80dfc47cb..dcaf06006 100644 --- a/src/workspace.js +++ b/src/workspace.js @@ -659,7 +659,7 @@ module.exports = class Workspace extends Model { // changing or closing tabs and ensures critical UI feedback, like changing the // highlighted tab, gets priority over work that can be done asynchronously. // - // * `callback` {Function} to be called when the active pane item stopts + // * `callback` {Function} to be called when the active pane item stops // changing. // * `item` The active pane item. // From 06ca120efe7850aee43b80235a1e659b0a237257 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Fri, 27 Oct 2017 13:52:58 -0700 Subject: [PATCH 91/97] :arrow_up: status-bar --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f800b1b3f..362de6ac3 100644 --- a/package.json +++ b/package.json @@ -126,7 +126,7 @@ "settings-view": "0.252.2", "snippets": "1.1.6", "spell-check": "0.72.3", - "status-bar": "1.8.13", + "status-bar": "1.8.14", "styleguide": "0.49.8", "symbols-view": "0.118.1", "tabs": "0.108.0", From e695e6565fc70cc159ea206e499cb1169bb0313e Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 27 Oct 2017 16:46:48 -0600 Subject: [PATCH 92/97] Switch to fork of nsfw to fix symlink loops on Linux --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 362de6ac3..ab8346925 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "license": "MIT", "electronVersion": "1.6.15", "dependencies": { + "@atom/nsfw": "^1.0.17", "@atom/source-map-support": "^0.3.4", "async": "0.2.6", "atom-keymap": "8.2.8", @@ -53,7 +54,6 @@ "mocha-multi-reporters": "^1.1.4", "mock-spawn": "^0.2.6", "normalize-package-data": "^2.0.0", - "nsfw": "^1.0.15", "nslog": "^3", "oniguruma": "6.2.1", "pathwatcher": "8.0.1", From ab79a2d2b29fe51af9d9b73cc58df91beaaffaf9 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Sat, 28 Oct 2017 00:53:01 +0200 Subject: [PATCH 93/97] :memo: [ci skip] --- src/command-registry.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/command-registry.js b/src/command-registry.js index 30089b7f1..ba75918ab 100644 --- a/src/command-registry.js +++ b/src/command-registry.js @@ -89,7 +89,7 @@ module.exports = class CommandRegistry { // DOM element, the command will be associated with just that element. // * `commandName` A {String} containing the name of a command you want to // handle such as `user:insert-date`. - // * `listener` A listener which handles the event. Either A {Function} to + // * `listener` A listener which handles the event. Either a {Function} to // call when the given command is invoked on an element matching the // selector, or an {Object} with a `didDispatch` property which is such a // function. @@ -97,7 +97,7 @@ module.exports = class CommandRegistry { // The function (`listener` itself if it is a function, or the `didDispatch` // method if `listener` is an object) will be called with `this` referencing // the matching DOM node and the following argument: - // * `event` A standard DOM event instance. Call `stopPropagation` or + // * `event`: A standard DOM event instance. Call `stopPropagation` or // `stopImmediatePropagation` to terminate bubbling early. // // Additionally, `listener` may have additional properties which are returned From 02d348e56ed98f67b0ab7483d2444d6854f878ae Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 27 Oct 2017 17:10:59 -0600 Subject: [PATCH 94/97] :arrow_up: @atom/nsfw to public version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ab8346925..e063e25ce 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "license": "MIT", "electronVersion": "1.6.15", "dependencies": { - "@atom/nsfw": "^1.0.17", + "@atom/nsfw": "^1.0.18", "@atom/source-map-support": "^0.3.4", "async": "0.2.6", "atom-keymap": "8.2.8", From 2f4d6ae3177c4005ef6ec1c9bd9bc9abfe1e2a13 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Sat, 28 Oct 2017 17:04:07 +0200 Subject: [PATCH 95/97] :arrow_up: snippets@1.1.7 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 362de6ac3..f945df5b3 100644 --- a/package.json +++ b/package.json @@ -124,7 +124,7 @@ "open-on-github": "1.2.1", "package-generator": "1.1.1", "settings-view": "0.252.2", - "snippets": "1.1.6", + "snippets": "1.1.7", "spell-check": "0.72.3", "status-bar": "1.8.14", "styleguide": "0.49.8", From 042c22f4321fb3a35e3d8d0c2562966c12a6fc35 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Sun, 29 Oct 2017 19:01:16 +0100 Subject: [PATCH 96/97] :arrow_up: first-mate@7.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 63d729a3a..0df753e69 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "etch": "^0.12.6", "event-kit": "^2.4.0", "find-parent-dir": "^0.3.0", - "first-mate": "7.0.10", + "first-mate": "7.1.0", "focus-trap": "^2.3.0", "fs-admin": "^0.1.6", "fs-plus": "^3.0.1", From 4eea63c50b4f8061f633392bf581e3d3a4fb3e5b Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Mon, 30 Oct 2017 10:31:41 +0100 Subject: [PATCH 97/97] :memo: --- src/workspace.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/workspace.js b/src/workspace.js index dcaf06006..defb43df0 100644 --- a/src/workspace.js +++ b/src/workspace.js @@ -1050,10 +1050,10 @@ module.exports = class Workspace extends Model { // Essential: Search the workspace for items matching the given URI and hide them. // - // * `itemOrURI` (optional) The item to hide or a {String} containing the URI + // * `itemOrURI` The item to hide or a {String} containing the URI // of the item to hide. // - // Returns a {boolean} indicating whether any items were found (and hidden). + // Returns a {Boolean} indicating whether any items were found (and hidden). hide (itemOrURI) { let foundItems = false