From 44de7e13e6592950ce837d52a28bf0511985302e Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Tue, 14 Nov 2017 19:17:04 +0100 Subject: [PATCH 1/4] link.coffee -> link.js --- lib/link.coffee | 65 ------------------------------------------ lib/link.js | 76 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 65 deletions(-) delete mode 100644 lib/link.coffee create mode 100644 lib/link.js diff --git a/lib/link.coffee b/lib/link.coffee deleted file mode 100644 index f6c9b01..0000000 --- a/lib/link.coffee +++ /dev/null @@ -1,65 +0,0 @@ -url = require 'url' -{shell} = require 'electron' -_ = require 'underscore-plus' - -selector = null - -module.exports = - activate: -> - atom.commands.add('atom-workspace', 'link:open', openLink) - -openLink = -> - editor = atom.workspace.getActiveTextEditor() - return unless editor? - - link = linkUnderCursor(editor) - return unless link? - - if editor.getGrammar().scopeName is 'source.gfm' - link = linkForName(editor.getBuffer(), link) - - {protocol} = url.parse(link) - if protocol is 'http:' or protocol is 'https:' - shell.openExternal(link) - -# Get the link under the cursor in the editor -# -# Returns a {String} link or undefined if no link found. -linkUnderCursor = (editor) -> - cursorPosition = editor.getCursorBufferPosition() - link = linkAtPosition(editor, cursorPosition) - return link if link? - - # Look for a link to the left of the cursor - if cursorPosition.column > 0 - linkAtPosition(editor, cursorPosition.translate([0, -1])) - -# Get the link at the buffer position in the editor. -# -# Returns a {String} link or undefined if no link found. -linkAtPosition = (editor, bufferPosition) -> - unless selector? - {ScopeSelector} = require 'first-mate' - selector = new ScopeSelector('markup.underline.link') - - if token = editor.tokenForBufferPosition(bufferPosition) - token.value if token.value and selector.matches(token.scopes) - -# Get the link for the given name. -# -# This is for Markdown links of the style: -# -# ``` -# [label][name] -# -# [name]: https://github.com -# ``` -# -# Returns a {String} link -linkForName = (buffer, linkName) -> - link = linkName - regex = new RegExp("^\\s*\\[#{_.escapeRegExp(linkName)}\\]\\s*:\\s*(.+)$", 'g') - buffer.backwardsScanInRange regex, buffer.getRange(), ({match, stop}) -> - link = match[1] - stop() - link diff --git a/lib/link.js b/lib/link.js new file mode 100644 index 0000000..7eb66df --- /dev/null +++ b/lib/link.js @@ -0,0 +1,76 @@ +const url = require('url') +const {shell} = require('electron') +const _ = require('underscore-plus') + +let selector = null + +module.exports = { + activate() { + atom.commands.add('atom-workspace', 'link:open', () => this.openLink()) + }, + + openLink() { + const editor = atom.workspace.getActiveTextEditor() + if (editor == null) return + + let link = this.linkUnderCursor(editor) + if (link == null) return + + if (editor.getGrammar().scopeName === 'source.gfm') { + link = this.linkForName(editor.getBuffer(), link) + } + + const {protocol} = url.parse(link) + if (protocol === 'http:' || protocol === 'https:') shell.openExternal(link) + }, + + // Get the link under the cursor in the editor + // + // Returns a {String} link or undefined if no link found. + linkUnderCursor(editor) { + const cursorPosition = editor.getCursorBufferPosition() + const link = this.linkAtPosition(editor, cursorPosition) + if (link != null) return link + + // Look for a link to the left of the cursor + if (cursorPosition.column > 0) { + return this.linkAtPosition(editor, cursorPosition.translate([0, -1])) + } + }, + + // Get the link at the buffer position in the editor. + // + // Returns a {String} link or undefined if no link found. + linkAtPosition(editor, bufferPosition) { + let token + if (selector == null) { + const {ScopeSelector} = require('first-mate') + selector = new ScopeSelector('markup.underline.link') + } + + if (token = editor.tokenForBufferPosition(bufferPosition)) { + if (token.value && selector.matches(token.scopes)) return token.value + } + }, + + // Get the link for the given name. + // + // This is for Markdown links of the style: + // + // ``` + // [label][name] + // + // [name]: https://github.com + // ``` + // + // Returns a {String} link + linkForName(buffer, linkName) { + let link = linkName + const regex = new RegExp(`^\\s*\\[${_.escapeRegExp(linkName)}\\]\\s*:\\s*(.+)$`, 'g') + buffer.backwardsScanInRange(regex, buffer.getRange(), ({match, stop}) => { + link = match[1] + stop() + }) + return link + } +} From 96e47ed244441066e43872e8568edf2e4ecf9bd0 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Tue, 14 Nov 2017 19:31:08 +0100 Subject: [PATCH 2/4] link-spec.coffee -> link-spec.js --- spec/async-spec-helpers.js | 103 +++++++++++++++++++++++++++++++++++++ spec/link-spec.coffee | 98 ----------------------------------- spec/link-spec.js | 96 ++++++++++++++++++++++++++++++++++ 3 files changed, 199 insertions(+), 98 deletions(-) create mode 100644 spec/async-spec-helpers.js delete mode 100644 spec/link-spec.coffee create mode 100644 spec/link-spec.js diff --git a/spec/async-spec-helpers.js b/spec/async-spec-helpers.js new file mode 100644 index 0000000..73002c0 --- /dev/null +++ b/spec/async-spec-helpers.js @@ -0,0 +1,103 @@ +/** @babel */ + +export function beforeEach (fn) { + global.beforeEach(function () { + const result = fn() + if (result instanceof Promise) { + waitsForPromise(() => result) + } + }) +} + +export function afterEach (fn) { + global.afterEach(function () { + const result = fn() + if (result instanceof Promise) { + waitsForPromise(() => result) + } + }) +} + +['it', 'fit', 'ffit', 'fffit'].forEach(function (name) { + module.exports[name] = function (description, fn) { + if (fn === undefined) { + global[name](description) + return + } + + global[name](description, function () { + const result = fn() + if (result instanceof Promise) { + waitsForPromise(() => result) + } + }) + } +}) + +export async function conditionPromise (condition, description = 'anonymous condition') { + const startTime = Date.now() + + while (true) { + await timeoutPromise(100) + + if (await condition()) { + return + } + + if (Date.now() - startTime > 5000) { + throw new Error('Timed out waiting on ' + description) + } + } +} + +export function timeoutPromise (timeout) { + return new Promise(function (resolve) { + global.setTimeout(resolve, timeout) + }) +} + +function waitsForPromise (fn) { + const promise = fn() + global.waitsFor('spec promise to resolve', function (done) { + promise.then(done, function (error) { + jasmine.getEnv().currentSpec.fail(error) + done() + }) + }) +} + +export function emitterEventPromise (emitter, event, timeout = 15000) { + return new Promise((resolve, reject) => { + const timeoutHandle = setTimeout(() => { + reject(new Error(`Timed out waiting for '${event}' event`)) + }, timeout) + emitter.once(event, () => { + clearTimeout(timeoutHandle) + resolve() + }) + }) +} + +export function promisify (original) { + return function (...args) { + return new Promise((resolve, reject) => { + args.push((err, ...results) => { + if (err) { + reject(err) + } else { + resolve(...results) + } + }) + + return original(...args) + }) + } +} + +export function promisifySome (obj, fnNames) { + const result = {} + for (const fnName of fnNames) { + result[fnName] = promisify(obj[fnName]) + } + return result +} diff --git a/spec/link-spec.coffee b/spec/link-spec.coffee deleted file mode 100644 index ebabcf1..0000000 --- a/spec/link-spec.coffee +++ /dev/null @@ -1,98 +0,0 @@ -{shell} = require 'electron' - -describe "link package", -> - beforeEach -> - waitsForPromise -> - atom.packages.activatePackage('language-gfm') - - waitsForPromise -> - atom.packages.activatePackage('language-javascript') - - waitsForPromise -> - atom.packages.activatePackage('language-hyperlink') - - waitsForPromise -> - activationPromise = atom.packages.activatePackage('link') - atom.commands.dispatch(atom.views.getView(atom.workspace), 'link:open') - activationPromise - - describe "when the cursor is on a link", -> - it "opens the link using the 'open' command", -> - waitsForPromise -> - atom.workspace.open('sample.js') - - runs -> - editor = atom.workspace.getActiveTextEditor() - editor.setText("// \"http://github.com\"") - - spyOn(shell, 'openExternal') - atom.commands.dispatch(atom.views.getView(editor), 'link:open') - expect(shell.openExternal).not.toHaveBeenCalled() - - editor.setCursorBufferPosition([0, 4]) - atom.commands.dispatch(atom.views.getView(editor), 'link:open') - - expect(shell.openExternal).toHaveBeenCalled() - expect(shell.openExternal.argsForCall[0][0]).toBe 'http://github.com' - - shell.openExternal.reset() - editor.setCursorBufferPosition([0, 8]) - atom.commands.dispatch(atom.views.getView(editor), 'link:open') - - expect(shell.openExternal).toHaveBeenCalled() - expect(shell.openExternal.argsForCall[0][0]).toBe 'http://github.com' - - shell.openExternal.reset() - editor.setCursorBufferPosition([0, 21]) - atom.commands.dispatch(atom.views.getView(editor), 'link:open') - - expect(shell.openExternal).toHaveBeenCalled() - expect(shell.openExternal.argsForCall[0][0]).toBe 'http://github.com' - - describe "when the cursor is on a [name][url-name] style markdown link", -> - it "opens the named url", -> - waitsForPromise -> - atom.workspace.open('README.md') - - runs -> - editor = atom.workspace.getActiveTextEditor() - editor.setText """ - you should [click][here] - you should not [click][her] - - [here]: http://github.com - """ - - spyOn(shell, 'openExternal') - editor.setCursorBufferPosition([0, 0]) - atom.commands.dispatch(atom.views.getView(editor), 'link:open') - expect(shell.openExternal).not.toHaveBeenCalled() - - editor.setCursorBufferPosition([0, 20]) - atom.commands.dispatch(atom.views.getView(editor), 'link:open') - - expect(shell.openExternal).toHaveBeenCalled() - expect(shell.openExternal.argsForCall[0][0]).toBe 'http://github.com' - - shell.openExternal.reset() - editor.setCursorBufferPosition([1, 24]) - atom.commands.dispatch(atom.views.getView(editor), 'link:open') - - expect(shell.openExternal).not.toHaveBeenCalled() - - it "does not open non http/https links", -> - waitsForPromise -> - atom.workspace.open('sample.js') - - runs -> - editor = atom.workspace.getActiveTextEditor() - editor.setText("// ftp://github.com\n") - - spyOn(shell, 'openExternal') - atom.commands.dispatch(atom.views.getView(editor), 'link:open') - expect(shell.openExternal).not.toHaveBeenCalled() - - editor.setCursorBufferPosition([0, 5]) - atom.commands.dispatch(atom.views.getView(editor), 'link:open') - - expect(shell.openExternal).not.toHaveBeenCalled() diff --git a/spec/link-spec.js b/spec/link-spec.js new file mode 100644 index 0000000..32f866e --- /dev/null +++ b/spec/link-spec.js @@ -0,0 +1,96 @@ +const {shell} = require('electron') + +const {it, fit, ffit, afterEach, beforeEach} = require('./async-spec-helpers') + +describe("link package", () => { + beforeEach(async () => { + await atom.packages.activatePackage('language-gfm') + await atom.packages.activatePackage('language-javascript') + await atom.packages.activatePackage('language-hyperlink') + + const activationPromise = atom.packages.activatePackage('link') + atom.commands.dispatch(atom.views.getView(atom.workspace), 'link:open') + await activationPromise + }) + + describe("when the cursor is on a link", () => { + it("opens the link using the 'open' command", async () => { + await atom.workspace.open('sample.js') + + const editor = atom.workspace.getActiveTextEditor() + editor.setText('// "http://github.com"') + + spyOn(shell, 'openExternal') + atom.commands.dispatch(atom.views.getView(editor), 'link:open') + expect(shell.openExternal).not.toHaveBeenCalled() + + editor.setCursorBufferPosition([0, 4]) + atom.commands.dispatch(atom.views.getView(editor), 'link:open') + + expect(shell.openExternal).toHaveBeenCalled() + expect(shell.openExternal.argsForCall[0][0]).toBe('http://github.com') + + shell.openExternal.reset() + editor.setCursorBufferPosition([0, 8]) + atom.commands.dispatch(atom.views.getView(editor), 'link:open') + + expect(shell.openExternal).toHaveBeenCalled() + expect(shell.openExternal.argsForCall[0][0]).toBe('http://github.com') + + shell.openExternal.reset() + editor.setCursorBufferPosition([0, 21]) + atom.commands.dispatch(atom.views.getView(editor), 'link:open') + + expect(shell.openExternal).toHaveBeenCalled() + expect(shell.openExternal.argsForCall[0][0]).toBe('http://github.com') + }) + + describe("when the cursor is on a [name][url-name] style markdown link", () => + it("opens the named url", async () => { + await atom.workspace.open('README.md') + + const editor = atom.workspace.getActiveTextEditor() + editor.setText(`\ +you should [click][here] +you should not [click][her] + +[here]: http://github.com\ +` + ) + + spyOn(shell, 'openExternal') + editor.setCursorBufferPosition([0, 0]) + atom.commands.dispatch(atom.views.getView(editor), 'link:open') + expect(shell.openExternal).not.toHaveBeenCalled() + + editor.setCursorBufferPosition([0, 20]) + atom.commands.dispatch(atom.views.getView(editor), 'link:open') + + expect(shell.openExternal).toHaveBeenCalled() + expect(shell.openExternal.argsForCall[0][0]).toBe('http://github.com') + + shell.openExternal.reset() + editor.setCursorBufferPosition([1, 24]) + atom.commands.dispatch(atom.views.getView(editor), 'link:open') + + expect(shell.openExternal).not.toHaveBeenCalled() + }) + ) + + it("does not open non http/https links", async () => { + await atom.workspace.open('sample.js') + + const editor = atom.workspace.getActiveTextEditor() + editor.setText("// ftp://github.com\n") + + spyOn(shell, 'openExternal') + atom.commands.dispatch(atom.views.getView(editor), 'link:open') + expect(shell.openExternal).not.toHaveBeenCalled() + + editor.setCursorBufferPosition([0, 5]) + atom.commands.dispatch(atom.views.getView(editor), 'link:open') + + expect(shell.openExternal).not.toHaveBeenCalled() + }) + }) +}) From 1877e159ef5131539328f742f6f5a1b5dd3b1cc8 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Tue, 14 Nov 2017 19:36:06 +0100 Subject: [PATCH 3/4] Coffeelint -> Standard --- .coffeelintignore | 1 - coffeelint.json | 37 ------------------------------------- package.json | 13 ++++++++++++- 3 files changed, 12 insertions(+), 39 deletions(-) delete mode 100644 .coffeelintignore delete mode 100644 coffeelint.json diff --git a/.coffeelintignore b/.coffeelintignore deleted file mode 100644 index 1db51fe..0000000 --- a/.coffeelintignore +++ /dev/null @@ -1 +0,0 @@ -spec/fixtures diff --git a/coffeelint.json b/coffeelint.json deleted file mode 100644 index a5dd715..0000000 --- a/coffeelint.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "max_line_length": { - "level": "ignore" - }, - "no_empty_param_list": { - "level": "error" - }, - "arrow_spacing": { - "level": "error" - }, - "no_interpolation_in_single_quotes": { - "level": "error" - }, - "no_debugger": { - "level": "error" - }, - "prefer_english_operator": { - "level": "error" - }, - "colon_assignment_spacing": { - "spacing": { - "left": 0, - "right": 1 - }, - "level": "error" - }, - "braces_spacing": { - "spaces": 0, - "level": "error" - }, - "spacing_after_comma": { - "level": "error" - }, - "no_stand_alone_at": { - "level": "error" - } -} diff --git a/package.json b/package.json index da30d31..d557c39 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,17 @@ "underscore-plus": "1.x" }, "devDependencies": { - "coffeelint": "^1.9.7" + "standard": "^10.0.3" + }, + "standard": { + "env": { + "atomtest": true, + "browser": true, + "jasmine": true, + "node": true + }, + "globals": [ + "atom" + ] } } From 72eb46ffef09b60a6db2e80061de8873fc258278 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Tue, 14 Nov 2017 19:39:04 +0100 Subject: [PATCH 4/4] :shirt: --- lib/link.js | 16 +++++++--------- spec/link-spec.js | 14 +++++++------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/lib/link.js b/lib/link.js index 7eb66df..3e04ddf 100644 --- a/lib/link.js +++ b/lib/link.js @@ -5,11 +5,11 @@ const _ = require('underscore-plus') let selector = null module.exports = { - activate() { + activate () { atom.commands.add('atom-workspace', 'link:open', () => this.openLink()) }, - openLink() { + openLink () { const editor = atom.workspace.getActiveTextEditor() if (editor == null) return @@ -27,7 +27,7 @@ module.exports = { // Get the link under the cursor in the editor // // Returns a {String} link or undefined if no link found. - linkUnderCursor(editor) { + linkUnderCursor (editor) { const cursorPosition = editor.getCursorBufferPosition() const link = this.linkAtPosition(editor, cursorPosition) if (link != null) return link @@ -41,16 +41,14 @@ module.exports = { // Get the link at the buffer position in the editor. // // Returns a {String} link or undefined if no link found. - linkAtPosition(editor, bufferPosition) { - let token + linkAtPosition (editor, bufferPosition) { if (selector == null) { const {ScopeSelector} = require('first-mate') selector = new ScopeSelector('markup.underline.link') } - if (token = editor.tokenForBufferPosition(bufferPosition)) { - if (token.value && selector.matches(token.scopes)) return token.value - } + const token = editor.tokenForBufferPosition(bufferPosition) + if (token && token.value && selector.matches(token.scopes)) return token.value }, // Get the link for the given name. @@ -64,7 +62,7 @@ module.exports = { // ``` // // Returns a {String} link - linkForName(buffer, linkName) { + linkForName (buffer, linkName) { let link = linkName const regex = new RegExp(`^\\s*\\[${_.escapeRegExp(linkName)}\\]\\s*:\\s*(.+)$`, 'g') buffer.backwardsScanInRange(regex, buffer.getRange(), ({match, stop}) => { diff --git a/spec/link-spec.js b/spec/link-spec.js index 32f866e..36830aa 100644 --- a/spec/link-spec.js +++ b/spec/link-spec.js @@ -1,8 +1,8 @@ const {shell} = require('electron') -const {it, fit, ffit, afterEach, beforeEach} = require('./async-spec-helpers') +const {it, fit, ffit, afterEach, beforeEach} = require('./async-spec-helpers') // eslint-disable-line no-unused-vars -describe("link package", () => { +describe('link package', () => { beforeEach(async () => { await atom.packages.activatePackage('language-gfm') await atom.packages.activatePackage('language-javascript') @@ -13,7 +13,7 @@ describe("link package", () => { await activationPromise }) - describe("when the cursor is on a link", () => { + describe('when the cursor is on a link', () => { it("opens the link using the 'open' command", async () => { await atom.workspace.open('sample.js') @@ -45,8 +45,8 @@ describe("link package", () => { expect(shell.openExternal.argsForCall[0][0]).toBe('http://github.com') }) - describe("when the cursor is on a [name][url-name] style markdown link", () => - it("opens the named url", async () => { + describe('when the cursor is on a [name][url-name] style markdown link', () => + it('opens the named url', async () => { await atom.workspace.open('README.md') const editor = atom.workspace.getActiveTextEditor() @@ -77,11 +77,11 @@ you should not [click][her] }) ) - it("does not open non http/https links", async () => { + it('does not open non http/https links', async () => { await atom.workspace.open('sample.js') const editor = atom.workspace.getActiveTextEditor() - editor.setText("// ftp://github.com\n") + editor.setText('// ftp://github.com\n') spyOn(shell, 'openExternal') atom.commands.dispatch(atom.views.getView(editor), 'link:open')