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; } };