From dfeffe2d2e7f5507c2ad90aa6a109a87d688bf45 Mon Sep 17 00:00:00 2001 From: Labhansh Agrawal Date: Wed, 17 Feb 2021 17:05:31 +0530 Subject: [PATCH] add file url handler --- bin/snapshot-libs.js | 1 - lib/components/term.tsx | 28 +++++++++++++++++++++----- lib/utils/link-matchers.ts | 41 ++++++++++++++++++++++++++++++++++++++ package.json | 4 ++-- webpack.config.ts | 1 - yarn.lock | 17 +++++++++++----- 6 files changed, 78 insertions(+), 14 deletions(-) create mode 100644 lib/utils/link-matchers.ts diff --git a/bin/snapshot-libs.js b/bin/snapshot-libs.js index 791e8ec868b5..481c41fc40d1 100644 --- a/bin/snapshot-libs.js +++ b/bin/snapshot-libs.js @@ -24,7 +24,6 @@ if (false) { require('xterm-addon-fit'); require('xterm-addon-image'); require('xterm-addon-search'); - require('xterm-addon-web-links'); require('xterm-addon-webgl'); require('xterm-addon-canvas'); require('xterm'); diff --git a/lib/components/term.tsx b/lib/components/term.tsx index 5ae7b9202c3f..cd668d242556 100644 --- a/lib/components/term.tsx +++ b/lib/components/term.tsx @@ -2,7 +2,6 @@ import React from 'react'; import type {ITerminalOptions, IDisposable} from 'xterm'; import {Terminal} from 'xterm'; import {FitAddon} from 'xterm-addon-fit'; -import {WebLinksAddon} from 'xterm-addon-web-links'; import type {ISearchDecorationOptions} from 'xterm-addon-search'; import {SearchAddon} from 'xterm-addon-search'; import {WebglAddon} from 'xterm-addon-webgl'; @@ -20,6 +19,8 @@ import isEqual from 'lodash/isEqual'; import {decorate} from '../utils/plugins'; import 'xterm/css/xterm.css'; import {ImageAddon} from 'xterm-addon-image'; +import {LinkProvider} from 'xterm-link-provider'; +import {FileURLMatcher, URLMatcher} from '../utils/link-matchers'; const SearchBox = decorate(_SearchBox, 'SearchBox'); @@ -210,10 +211,27 @@ export default class Term extends React.PureComponent< this.term.attachCustomKeyEventHandler(this.keyboardHandler); this.term.loadAddon(this.fitAddon); this.term.loadAddon(this.searchAddon); - this.term.loadAddon( - new WebLinksAddon((event, uri) => { - if (shallActivateWebLink(event)) void shell.openExternal(uri); - }) + this.term.registerLinkProvider( + new LinkProvider( + this.term, + URLMatcher.regex, + (event, text) => { + if (shallActivateWebLink(event)) void shell.openExternal(text); + }, + {}, + URLMatcher.matchIndex + ) + ); + this.term.registerLinkProvider( + new LinkProvider( + this.term, + FileURLMatcher.regex, + (event, text) => { + if (shallActivateWebLink(event)) void shell.openExternal(text); + }, + {}, + FileURLMatcher.matchIndex + ) ); this.term.open(this.termRef); diff --git a/lib/utils/link-matchers.ts b/lib/utils/link-matchers.ts new file mode 100644 index 000000000000..3ab75a6e0e38 --- /dev/null +++ b/lib/utils/link-matchers.ts @@ -0,0 +1,41 @@ +interface linkMatcher { + regex: RegExp; + matchIndex: number; +} + +// Based on the regex used in xterm-addon-web-links +export const URLMatcher: linkMatcher = (() => { + const protocolClause = '(https?:\\/\\/)'; + const domainCharacterSet = '[\\da-z\\.-]+'; + const negatedDomainCharacterSet = '[^\\da-z\\.-]+'; + const domainBodyClause = '(' + domainCharacterSet + ')'; + const tldClause = '([a-z\\.]{2,6})'; + const ipClause = '((\\d{1,3}\\.){3}\\d{1,3})'; + const localHostClause = '(localhost)'; + const portClause = '(:\\d{1,5})'; + const hostClause = + '((' + domainBodyClause + '\\.' + tldClause + ')|' + ipClause + '|' + localHostClause + ')' + portClause + '?'; + const pathCharacterSet = '(\\/[\\/\\w\\.\\-%~:+@]*)*([^:"\'\\s])'; + const pathClause = '(' + pathCharacterSet + ')?'; + const queryStringHashFragmentCharacterSet = "[0-9\\w\\[\\]\\(\\)\\/\\?\\!#@$%&'*+,:;~\\=\\.\\-]*"; + const queryStringClause = '(\\?' + queryStringHashFragmentCharacterSet + ')?'; + const hashFragmentClause = '(#' + queryStringHashFragmentCharacterSet + ')?'; + const negatedPathCharacterSet = '[^\\/\\w\\.\\-%]+'; + const bodyClause = hostClause + pathClause + queryStringClause + hashFragmentClause; + const start = '(?:^|' + negatedDomainCharacterSet + ')('; + const end = ')($|' + negatedPathCharacterSet + ')'; + return {regex: new RegExp(start + protocolClause + bodyClause + end), matchIndex: 1}; +})(); + +// Simple file url matcher +export const FileURLMatcher: linkMatcher = (() => { + const protocolClause = '(file:\\/\\/)'; + const negatedDomainCharacterSet = '[^\\da-z\\.-]+'; + const pathCharacterSet = '(\\/[\\/\\w\\.\\-%~:+@]*)*([^:"\'\\s])'; + const pathClause = '(' + pathCharacterSet + ')'; + const negatedPathCharacterSet = '[^\\/\\w\\.\\-%]+'; + const bodyClause = pathClause; + const start = '(?:^|' + negatedDomainCharacterSet + ')('; + const end = ')($|' + negatedPathCharacterSet + ')'; + return {regex: new RegExp(start + protocolClause + bodyClause + end), matchIndex: 1}; +})(); diff --git a/package.json b/package.json index 75249e3e13b9..4371aa08e124 100644 --- a/package.json +++ b/package.json @@ -67,8 +67,8 @@ "xterm-addon-ligatures": "0.6.0", "xterm-addon-search": "0.12.0", "xterm-addon-unicode11": "0.5.0", - "xterm-addon-web-links": "0.8.0", - "xterm-addon-webgl": "0.15.0" + "xterm-addon-webgl": "0.15.0", + "xterm-link-provider": "1.3.1" }, "devDependencies": { "@ava/babel": "2.0.0", diff --git a/webpack.config.ts b/webpack.config.ts index bfa4ddd7f1a5..8090541b0b6e 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -123,7 +123,6 @@ const config: webpack.Configuration[] = [ 'xterm-addon-fit': 'require("./node_modules/xterm-addon-fit/lib/xterm-addon-fit.js")', 'xterm-addon-image': 'require("./node_modules/xterm-addon-image/lib/xterm-addon-image.js")', 'xterm-addon-search': 'require("./node_modules/xterm-addon-search/lib/xterm-addon-search.js")', - 'xterm-addon-web-links': 'require("./node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js")', 'xterm-addon-webgl': 'require("./node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js")', 'xterm-addon-canvas': 'require("./node_modules/xterm-addon-canvas/lib/xterm-addon-canvas.js")', xterm: 'require("./node_modules/xterm/lib/xterm.js")' diff --git a/yarn.lock b/yarn.lock index 1bb12b48be60..beac06085aee 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7740,21 +7740,28 @@ xterm-addon-unicode11@0.5.0: resolved "https://registry.npmjs.org/xterm-addon-unicode11/-/xterm-addon-unicode11-0.5.0.tgz#41c0d96acc1e3bb6c6596eee64e163b6bca74be7" integrity sha512-Jm4/g4QiTxiKiTbYICQgC791ubhIZyoIwxAIgOW8z8HWFNY+lwk+dwaKEaEeGBfM48Vk8fklsUW9u/PlenYEBg== -xterm-addon-web-links@0.8.0: - version "0.8.0" - resolved "https://registry.npmjs.org/xterm-addon-web-links/-/xterm-addon-web-links-0.8.0.tgz#2cb1d57129271022569208578b0bf4774e7e6ea9" - integrity sha512-J4tKngmIu20ytX9SEJjAP3UGksah7iALqBtfTwT9ZnmFHVplCumYQsUJfKuS+JwMhjsjH61YXfndenLNvjRrEw== - xterm-addon-webgl@0.15.0: version "0.15.0" resolved "https://registry.npmjs.org/xterm-addon-webgl/-/xterm-addon-webgl-0.15.0.tgz#c10f93ca619524f5a470eaac44258bab0ae8e3c7" integrity sha512-ZLcqogMFHr4g/YRhcCh3xE8tTklnyut/M+O/XhVsFBRB/YCvYhPdLQ5/AQk54V0wjWAQpa8CF3W8DVR9OqyMCg== +xterm-link-provider@1.3.1: + version "1.3.1" + resolved "https://registry.npmjs.org/xterm-link-provider/-/xterm-link-provider-1.3.1.tgz#69727223220dfa8758056ad6b2b5394a8454b9cb" + integrity sha512-uOlaIeUED6kJeL2nIIf5YwreO0obMhsC0RWypEUmWkz7SAQewzgwdWFjQ2He7NGcT93c4KUf8bRgAu8cV9bAYA== + dependencies: + xterm "^4.6.0" + xterm@5.2.1: version "5.2.1" resolved "https://registry.npmjs.org/xterm/-/xterm-5.2.1.tgz#b3fea7bdb55b9be1d4b31f4cd1091f26ac42afb8" integrity sha512-cs5Y1fFevgcdoh2hJROMVIWwoBHD80P1fIP79gopLHJIE4kTzzblanoivxTiQ4+92YM9IxS36H1q0MxIJXQBcA== +xterm@^4.6.0: + version "4.14.1" + resolved "https://registry.npmjs.org/xterm/-/xterm-4.14.1.tgz#6884cb8fb3b83353b1a98139ea23daedf8e35796" + integrity sha512-jgzNg5BuGPwq5/M4dGnmbghZvHx2jaj+9crSEt15bV34Za49VziBmCu7zIy88zUKKiGTxeo7aVzirFSJArIMFw== + y18n@^5.0.5: version "5.0.5" resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz#8769ec08d03b1ea2df2500acef561743bbb9ab18"