mirror of
https://github.com/atom/atom.git
synced 2026-01-26 07:19:06 -05:00
Merge pull request #18105 from atom/migrate-link-package
➡️ Migrate core package 'link' into ./packages
This commit is contained in:
7
package-lock.json
generated
7
package-lock.json
generated
@@ -3181,8 +3181,8 @@
|
||||
}
|
||||
},
|
||||
"language-hyperlink": {
|
||||
"version": "https://www.atom.io/api/packages/language-hyperlink/versions/0.16.3/tarball",
|
||||
"integrity": "sha512-IDkh820N85GVgcP0EiU2QceAcmRHyYQCzJkaG7eSwWmOxvf5e+bO9g2U28sED14hQjH+No4MRfU5+grEmAnvuw=="
|
||||
"version": "https://www.atom.io/api/packages/language-hyperlink/versions/0.17.0/tarball",
|
||||
"integrity": "sha512-V7IEqrIvn75LX/iQ/MPA75nKdfQ3kfJ5zWvUoCAxfvE9tJ6X25eSvqqgp10zxI0yjB9AQ/JDywYsKd5fEmQ8NQ=="
|
||||
},
|
||||
"language-java": {
|
||||
"version": "https://www.atom.io/api/packages/language-java/versions/0.30.0/tarball",
|
||||
@@ -3477,8 +3477,7 @@
|
||||
}
|
||||
},
|
||||
"link": {
|
||||
"version": "https://www.atom.io/api/packages/link/versions/0.31.6/tarball",
|
||||
"integrity": "sha512-+LqJ1Iv9bPTeovPSO7uOgEID3MgKVntaB7DjVWD1JLOCbaLPdqHHOmCRHifhsAE8cVm3HxdxLo7N1vmBXF+TUg==",
|
||||
"version": "file:packages/link",
|
||||
"requires": {
|
||||
"underscore-plus": "1.x"
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
"language-git": "https://www.atom.io/api/packages/language-git/versions/0.19.1/tarball",
|
||||
"language-go": "https://www.atom.io/api/packages/language-go/versions/0.46.3/tarball",
|
||||
"language-html": "https://www.atom.io/api/packages/language-html/versions/0.51.5/tarball",
|
||||
"language-hyperlink": "https://www.atom.io/api/packages/language-hyperlink/versions/0.16.3/tarball",
|
||||
"language-hyperlink": "https://www.atom.io/api/packages/language-hyperlink/versions/0.17.0/tarball",
|
||||
"language-java": "https://www.atom.io/api/packages/language-java/versions/0.30.0/tarball",
|
||||
"language-javascript": "https://www.atom.io/api/packages/language-javascript/versions/0.129.9/tarball",
|
||||
"language-json": "https://www.atom.io/api/packages/language-json/versions/0.19.2/tarball",
|
||||
@@ -115,7 +115,7 @@
|
||||
"less-cache": "1.1.0",
|
||||
"line-ending-selector": "https://www.atom.io/api/packages/line-ending-selector/versions/0.7.7/tarball",
|
||||
"line-top-index": "0.3.1",
|
||||
"link": "https://www.atom.io/api/packages/link/versions/0.31.6/tarball",
|
||||
"link": "file:packages/link",
|
||||
"markdown-preview": "https://www.atom.io/api/packages/markdown-preview/versions/0.159.25/tarball",
|
||||
"marked": "^0.3.12",
|
||||
"metrics": "https://www.atom.io/api/packages/metrics/versions/1.6.2/tarball",
|
||||
@@ -210,7 +210,7 @@
|
||||
"incompatible-packages": "file:./packages/incompatible-packages",
|
||||
"keybinding-resolver": "0.38.4",
|
||||
"line-ending-selector": "0.7.7",
|
||||
"link": "0.31.6",
|
||||
"link": "file:./packages/link",
|
||||
"markdown-preview": "0.159.25",
|
||||
"metrics": "1.6.2",
|
||||
"notifications": "0.70.5",
|
||||
@@ -238,7 +238,7 @@
|
||||
"language-git": "0.19.1",
|
||||
"language-go": "0.46.3",
|
||||
"language-html": "0.51.5",
|
||||
"language-hyperlink": "0.16.3",
|
||||
"language-hyperlink": "0.17.0",
|
||||
"language-java": "0.30.0",
|
||||
"language-javascript": "0.129.9",
|
||||
"language-json": "0.19.2",
|
||||
|
||||
@@ -74,7 +74,7 @@ See [RFC 003](https://github.com/atom/atom/blob/master/docs/rfcs/003-consolidate
|
||||
| **language-xml** | [`atom/language-xml`][language-xml] | |
|
||||
| **language-yaml** | [`atom/language-yaml`][language-yaml] | |
|
||||
| **line-ending-selector** | [`atom/line-ending-selector`][line-ending-selector] | [#17847](https://github.com/atom/atom/issues/17847) |
|
||||
| **link** | [`atom/link`][link] | [#17848](https://github.com/atom/atom/issues/17848) |
|
||||
| **link** | [`./packages/link`][./link] | [#17848](https://github.com/atom/atom/issues/17848) |
|
||||
| **markdown-preview** | [`atom/markdown-preview`][markdown-preview] | |
|
||||
| **metrics** | [`atom/metrics`][metrics] | |
|
||||
| **notifications** | [`atom/notifications`][notifications] | |
|
||||
|
||||
2
packages/link/.gitignore
vendored
Normal file
2
packages/link/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
npm-debug.log
|
||||
1
packages/link/.npmignore
Normal file
1
packages/link/.npmignore
Normal file
@@ -0,0 +1 @@
|
||||
npm-debug.log
|
||||
20
packages/link/LICENSE.md
Normal file
20
packages/link/LICENSE.md
Normal file
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2014 GitHub Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
11
packages/link/README.md
Normal file
11
packages/link/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Link package
|
||||
|
||||
Opens http(s) links under the cursor.
|
||||
|
||||
### Commands and Keybindings
|
||||
|
||||
|Command|Selector|Description|Keybinding (Linux)|Keybinding (macOS)|Keybinding (Windows)|
|
||||
|-------|--------|-----------|------------------|------------------|--------------------|
|
||||
|`link:open`|`atom-text-editor`|Opens the http(s) link under the cursor||<kbd>ctrl-shift-o</kbd>||
|
||||
|
||||
Custom keybindings can be added by referencing the above commands. To learn more, visit the [Using Atom: Basic Customization](http://flight-manual.atom.io/using-atom/sections/basic-customization/#customizing-keybindings) or [Behind Atom: Keymaps In-Depth](http://flight-manual.atom.io/behind-atom/sections/keymaps-in-depth) sections of the Atom Flight Manual.
|
||||
2
packages/link/keymaps/links.cson
Normal file
2
packages/link/keymaps/links.cson
Normal file
@@ -0,0 +1,2 @@
|
||||
'.platform-darwin atom-text-editor':
|
||||
'ctrl-shift-o': 'link:open'
|
||||
75
packages/link/lib/link.js
Normal file
75
packages/link/lib/link.js
Normal file
@@ -0,0 +1,75 @@
|
||||
const url = require('url')
|
||||
const {shell} = require('electron')
|
||||
const _ = require('underscore-plus')
|
||||
|
||||
const LINK_SCOPE_REGEX = /markup\.underline\.link/
|
||||
|
||||
module.exports = {
|
||||
activate () {
|
||||
this.commandDisposable = atom.commands.add('atom-text-editor', 'link:open', () => this.openLink())
|
||||
},
|
||||
|
||||
deactivate () {
|
||||
this.commandDisposable.dispose()
|
||||
},
|
||||
|
||||
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, link)
|
||||
}
|
||||
|
||||
const {protocol} = url.parse(link)
|
||||
if (protocol === 'http:' || protocol === 'https:' || protocol === 'atom:') 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) {
|
||||
const token = editor.tokenForBufferPosition(bufferPosition)
|
||||
if (token && token.value && token.scopes.some(scope => LINK_SCOPE_REGEX.test(scope))) {
|
||||
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 (editor, linkName) {
|
||||
let link = linkName
|
||||
const regex = new RegExp(`^\\s*\\[${_.escapeRegExp(linkName)}\\]\\s*:\\s*(.+)$`, 'g')
|
||||
editor.backwardsScanInBufferRange(regex, [[0, 0], [Infinity, Infinity]], ({match, stop}) => {
|
||||
link = match[1]
|
||||
stop()
|
||||
})
|
||||
return link
|
||||
}
|
||||
}
|
||||
4
packages/link/menus/link.cson
Normal file
4
packages/link/menus/link.cson
Normal file
@@ -0,0 +1,4 @@
|
||||
'context-menu':
|
||||
'atom-text-editor .syntax--markup.syntax--underline.syntax--link': [
|
||||
{label: 'Open link', command: 'link:open'}
|
||||
]
|
||||
33
packages/link/package.json
Normal file
33
packages/link/package.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "link",
|
||||
"version": "0.31.6",
|
||||
"main": "./lib/link",
|
||||
"description": "Opens http(s) links under the cursor",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/atom/atom",
|
||||
"engines": {
|
||||
"atom": "*"
|
||||
},
|
||||
"activationCommands": {
|
||||
"atom-workspace": [
|
||||
"link:open"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"underscore-plus": "1.x"
|
||||
},
|
||||
"devDependencies": {
|
||||
"standard": "^10.0.3"
|
||||
},
|
||||
"standard": {
|
||||
"env": {
|
||||
"atomtest": true,
|
||||
"browser": true,
|
||||
"jasmine": true,
|
||||
"node": true
|
||||
},
|
||||
"globals": [
|
||||
"atom"
|
||||
]
|
||||
}
|
||||
}
|
||||
103
packages/link/spec/async-spec-helpers.js
Normal file
103
packages/link/spec/async-spec-helpers.js
Normal file
@@ -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
|
||||
}
|
||||
132
packages/link/spec/link-spec.js
Normal file
132
packages/link/spec/link-spec.js
Normal file
@@ -0,0 +1,132 @@
|
||||
const {shell} = require('electron')
|
||||
|
||||
const {it, fit, ffit, afterEach, beforeEach} = require('./async-spec-helpers') // eslint-disable-line no-unused-vars
|
||||
|
||||
describe('link package', () => {
|
||||
beforeEach(async () => {
|
||||
await atom.packages.activatePackage('language-gfm')
|
||||
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.md')
|
||||
|
||||
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')
|
||||
})
|
||||
|
||||
// only works in Atom >= 1.33.0
|
||||
// https://github.com/atom/link/pull/33#issuecomment-419643655
|
||||
const atomVersion = atom.getVersion().split('.')
|
||||
console.error("atomVersion", atomVersion)
|
||||
if (+atomVersion[0] > 1 || +atomVersion[1] >= 33) {
|
||||
it("opens an 'atom:' link", async () => {
|
||||
await atom.workspace.open('sample.md')
|
||||
|
||||
const editor = atom.workspace.getActiveTextEditor()
|
||||
editor.setText('// "atom://core/open/file?filename=sample.js&line=1&column=2"')
|
||||
|
||||
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('atom://core/open/file?filename=sample.js&line=1&column=2')
|
||||
|
||||
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('atom://core/open/file?filename=sample.js&line=1&column=2')
|
||||
|
||||
shell.openExternal.reset()
|
||||
editor.setCursorBufferPosition([0, 60])
|
||||
atom.commands.dispatch(atom.views.getView(editor), 'link:open')
|
||||
|
||||
expect(shell.openExternal).toHaveBeenCalled()
|
||||
expect(shell.openExternal.argsForCall[0][0]).toBe('atom://core/open/file?filename=sample.js&line=1&column=2')
|
||||
})
|
||||
}
|
||||
|
||||
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/atom links', async () => {
|
||||
await atom.workspace.open('sample.md')
|
||||
|
||||
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()
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user