mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 04:49:35 +00:00
markdown link smart pasting (#188437)
* making markdown link pasting feature smarter * update validateLink
This commit is contained in:
parent
f991a1ad7b
commit
87afa166d0
|
@ -60,7 +60,7 @@ export class InsertImageFromWorkspace implements Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDefaultUri(document: vscode.TextDocument) {
|
function getDefaultUri(document: vscode.TextDocument) {
|
||||||
const docUri = getParentDocumentUri(document);
|
const docUri = getParentDocumentUri(document.uri);
|
||||||
if (docUri.scheme === Schemes.untitled) {
|
if (docUri.scheme === Schemes.untitled) {
|
||||||
return vscode.workspace.workspaceFolders?.[0]?.uri;
|
return vscode.workspace.workspaceFolders?.[0]?.uri;
|
||||||
}
|
}
|
||||||
|
@ -76,10 +76,10 @@ async function insertLink(activeEditor: vscode.TextEditor, selectedFiles: vscode
|
||||||
await vscode.workspace.applyEdit(edit);
|
await vscode.workspace.applyEdit(edit);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createInsertLinkEdit(activeEditor: vscode.TextEditor, selectedFiles: vscode.Uri[], insertAsMedia: boolean, title = '', placeholderValue = 0, smartPaste = false, isExternalLink = false) {
|
function createInsertLinkEdit(activeEditor: vscode.TextEditor, selectedFiles: vscode.Uri[], insertAsMedia: boolean, title = '', placeholderValue = 0, pasteAsMarkdownLink = true, isExternalLink = false) {
|
||||||
const snippetEdits = coalesce(activeEditor.selections.map((selection, i): vscode.SnippetTextEdit | undefined => {
|
const snippetEdits = coalesce(activeEditor.selections.map((selection, i): vscode.SnippetTextEdit | undefined => {
|
||||||
const selectionText = activeEditor.document.getText(selection);
|
const selectionText = activeEditor.document.getText(selection);
|
||||||
const snippet = createUriListSnippet(activeEditor.document, selectedFiles, title, placeholderValue, smartPaste, isExternalLink, {
|
const snippet = createUriListSnippet(activeEditor.document, selectedFiles, title, placeholderValue, pasteAsMarkdownLink, isExternalLink, {
|
||||||
insertAsMedia,
|
insertAsMedia,
|
||||||
placeholderText: selectionText,
|
placeholderText: selectionText,
|
||||||
placeholderStartIndex: (i + 1) * selectedFiles.length,
|
placeholderStartIndex: (i + 1) * selectedFiles.length,
|
||||||
|
|
|
@ -81,7 +81,7 @@ export class NewFilePathGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDesiredNewFilePath(config: CopyFileConfiguration, document: vscode.TextDocument, file: vscode.DataTransferFile): vscode.Uri {
|
function getDesiredNewFilePath(config: CopyFileConfiguration, document: vscode.TextDocument, file: vscode.DataTransferFile): vscode.Uri {
|
||||||
const docUri = getParentDocumentUri(document);
|
const docUri = getParentDocumentUri(document.uri);
|
||||||
for (const [rawGlob, rawDest] of Object.entries(config.destination)) {
|
for (const [rawGlob, rawDest] of Object.entries(config.destination)) {
|
||||||
for (const glob of parseGlob(rawGlob)) {
|
for (const glob of parseGlob(rawGlob)) {
|
||||||
if (picomatch.isMatch(docUri.path, glob, { dot: true })) {
|
if (picomatch.isMatch(docUri.path, glob, { dot: true })) {
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { Schemes } from '../../util/schemes';
|
import { Schemes } from '../../util/schemes';
|
||||||
import { createEditForMediaFiles, createEditAddingLinksForUriList, mediaMimes } from './shared';
|
import { createEditForMediaFiles, createEditAddingLinksForUriList, mediaMimes, getPasteUrlAsFormattedLinkSetting, PasteUrlAsFormattedLink } from './shared';
|
||||||
|
|
||||||
class PasteEditProvider implements vscode.DocumentPasteEditProvider {
|
class PasteEditProvider implements vscode.DocumentPasteEditProvider {
|
||||||
|
|
||||||
|
@ -32,7 +32,8 @@ class PasteEditProvider implements vscode.DocumentPasteEditProvider {
|
||||||
if (!urlList) {
|
if (!urlList) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const pasteEdit = await createEditAddingLinksForUriList(document, ranges, urlList, token, false);
|
const pasteUrlSetting = await getPasteUrlAsFormattedLinkSetting(document);
|
||||||
|
const pasteEdit = await createEditAddingLinksForUriList(document, ranges, urlList, false, pasteUrlSetting === PasteUrlAsFormattedLink.Smart, token);
|
||||||
if (!pasteEdit) {
|
if (!pasteEdit) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { externalUriSchemes, createEditAddingLinksForUriList } from './shared';
|
import { externalUriSchemes, createEditAddingLinksForUriList, getPasteUrlAsFormattedLinkSetting, PasteUrlAsFormattedLink } from './shared';
|
||||||
class PasteLinkEditProvider implements vscode.DocumentPasteEditProvider {
|
class PasteLinkEditProvider implements vscode.DocumentPasteEditProvider {
|
||||||
|
|
||||||
readonly id = 'insertMarkdownLink';
|
readonly id = 'insertMarkdownLink';
|
||||||
|
@ -14,8 +14,8 @@ class PasteLinkEditProvider implements vscode.DocumentPasteEditProvider {
|
||||||
dataTransfer: vscode.DataTransfer,
|
dataTransfer: vscode.DataTransfer,
|
||||||
token: vscode.CancellationToken,
|
token: vscode.CancellationToken,
|
||||||
): Promise<vscode.DocumentPasteEdit | undefined> {
|
): Promise<vscode.DocumentPasteEdit | undefined> {
|
||||||
const enabled = vscode.workspace.getConfiguration('markdown', document).get<'always' | 'smart' | 'never'>('editor.pasteUrlAsFormattedLink.enabled', 'smart');
|
const pasteUrlSetting = await getPasteUrlAsFormattedLinkSetting(document);
|
||||||
if (enabled === 'never') {
|
if (pasteUrlSetting === PasteUrlAsFormattedLink.Never) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ class PasteLinkEditProvider implements vscode.DocumentPasteEditProvider {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!validateLink(urlList)) {
|
if (!validateLink(urlList).isValid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,8 @@ class PasteLinkEditProvider implements vscode.DocumentPasteEditProvider {
|
||||||
if (!urlList) {
|
if (!urlList) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const pasteEdit = await createEditAddingLinksForUriList(document, ranges, urlList, token, true);
|
|
||||||
|
const pasteEdit = await createEditAddingLinksForUriList(document, ranges, validateLink(urlList).cleanedUrlList, true, pasteUrlSetting === PasteUrlAsFormattedLink.Smart, token);
|
||||||
if (!pasteEdit) {
|
if (!pasteEdit) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -45,12 +46,20 @@ class PasteLinkEditProvider implements vscode.DocumentPasteEditProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function validateLink(urlList: string): boolean {
|
export function validateLink(urlList: string): { isValid: boolean; cleanedUrlList: string } {
|
||||||
const url = urlList?.split(/\s+/);
|
let isValid = false;
|
||||||
if (url.length > 1 || !externalUriSchemes.includes(vscode.Uri.parse(url[0]).scheme)) {
|
let uri = undefined;
|
||||||
return false;
|
const trimmedUrlList = urlList?.trim(); //remove leading and trailing whitespace and new lines
|
||||||
|
try {
|
||||||
|
uri = vscode.Uri.parse(trimmedUrlList);
|
||||||
|
} catch (error) {
|
||||||
|
return { isValid: false, cleanedUrlList: urlList };
|
||||||
}
|
}
|
||||||
return true;
|
const splitUrlList = trimmedUrlList.split(' ').filter(item => item !== ''); //split on spaces and remove empty strings
|
||||||
|
if (uri) {
|
||||||
|
isValid = splitUrlList.length === 1 && !splitUrlList[0].includes('\n') && externalUriSchemes.includes(vscode.Uri.parse(splitUrlList[0]).scheme);
|
||||||
|
}
|
||||||
|
return { isValid, cleanedUrlList: splitUrlList[0] };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function registerLinkPasteSupport(selector: vscode.DocumentSelector,) {
|
export function registerLinkPasteSupport(selector: vscode.DocumentSelector,) {
|
||||||
|
|
|
@ -21,7 +21,6 @@ export const externalUriSchemes = [
|
||||||
'http',
|
'http',
|
||||||
'https',
|
'https',
|
||||||
'mailto',
|
'mailto',
|
||||||
'ftp',
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export const mediaFileExtensions = new Map<string, MediaKind>([
|
export const mediaFileExtensions = new Map<string, MediaKind>([
|
||||||
|
@ -64,14 +63,21 @@ export const mediaMimes = new Set([
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const smartPasteRegexes = [
|
const smartPasteRegexes = [
|
||||||
{ regex: /\[.*\]\(.*\)/g, is_markdown_link: true }, // Is a Markdown Link
|
{ regex: /\[.*\]\(.*\)/g, isMarkdownLink: true, isInline: true }, // Is a Markdown Link
|
||||||
{ regex: /!\[.*\]\(.*\)/g, is_markdown_link: true }, // Is a Markdown Image Link
|
{ regex: /!\[.*\]\(.*\)/g, isMarkdownLink: true, isInline: true }, // Is a Markdown Image Link
|
||||||
{ regex: /\[([^\]]*)\]\(([^)]*)\)/g, is_markdown_link: false }, // In a Markdown link
|
{ regex: /\[([^\]]*)\]\(([^)]*)\)/g, isMarkdownLink: false, isInline: true }, // In a Markdown link
|
||||||
{ regex: /^```[\s\S]*?```$/gm, is_markdown_link: false }, // In a fenced code block
|
{ regex: /^```[\s\S]*?```$/gm, isMarkdownLink: false, isInline: false }, // In a fenced code block
|
||||||
{ regex: /^\$\$[\s\S]*?\$\$$/gm, is_markdown_link: false }, // In a fenced math block
|
{ regex: /^\$\$[\s\S]*?\$\$$/gm, isMarkdownLink: false, isInline: false }, // In a fenced math block
|
||||||
{ regex: /`[^`]*`/g, is_markdown_link: false }, // In inline code
|
{ regex: /`[^`]*`/g, isMarkdownLink: false, isInline: true }, // In inline code
|
||||||
{ regex: /\$[^$]*\$/g, is_markdown_link: false }, // In inline math
|
{ regex: /\$[^$]*\$/g, isMarkdownLink: false, isInline: true }, // In inline math
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export interface SkinnyTextDocument {
|
||||||
|
offsetAt(position: vscode.Position): number;
|
||||||
|
getText(range?: vscode.Range): string;
|
||||||
|
readonly uri: vscode.Uri;
|
||||||
|
}
|
||||||
|
|
||||||
export interface SmartPaste {
|
export interface SmartPaste {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -86,22 +92,43 @@ export interface SmartPaste {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createEditAddingLinksForUriList(document: vscode.TextDocument, ranges: readonly vscode.Range[], urlList: string, token: vscode.CancellationToken, isExternalLink: boolean): Promise<{ additionalEdits: vscode.WorkspaceEdit; label: string } | undefined> {
|
export enum PasteUrlAsFormattedLink {
|
||||||
|
Always = 'always',
|
||||||
|
Smart = 'smart',
|
||||||
|
Never = 'never'
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getPasteUrlAsFormattedLinkSetting(document: vscode.TextDocument): Promise<PasteUrlAsFormattedLink> {
|
||||||
|
return vscode.workspace.getConfiguration('markdown', document).get<PasteUrlAsFormattedLink>('editor.pasteUrlAsFormattedLink.enabled', PasteUrlAsFormattedLink.Smart);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createEditAddingLinksForUriList(
|
||||||
|
document: SkinnyTextDocument,
|
||||||
|
ranges: readonly vscode.Range[],
|
||||||
|
urlList: string,
|
||||||
|
isExternalLink: boolean,
|
||||||
|
useSmartPaste: boolean,
|
||||||
|
token: vscode.CancellationToken,
|
||||||
|
): Promise<{ additionalEdits: vscode.WorkspaceEdit; label: string } | undefined> {
|
||||||
|
|
||||||
if (ranges.length === 0) {
|
if (ranges.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const enabled = vscode.workspace.getConfiguration('markdown', document).get<'always' | 'smart' | 'never'>('editor.pasteUrlAsFormattedLink.enabled', 'always');
|
|
||||||
const edits: vscode.SnippetTextEdit[] = [];
|
const edits: vscode.SnippetTextEdit[] = [];
|
||||||
let placeHolderValue: number = ranges.length;
|
let placeHolderValue: number = ranges.length;
|
||||||
let label: string = '';
|
let label: string = '';
|
||||||
let smartPaste = { pasteAsMarkdownLink: true, updateTitle: false };
|
let smartPaste = { pasteAsMarkdownLink: true, updateTitle: false };
|
||||||
|
|
||||||
for (let i = 0; i < ranges.length; i++) {
|
for (const range of ranges) {
|
||||||
|
let title = document.getText(range);
|
||||||
|
const selectedRange: vscode.Range = new vscode.Range(
|
||||||
|
new vscode.Position(range.start.line, document.offsetAt(range.start)),
|
||||||
|
new vscode.Position(range.end.line, document.offsetAt(range.end))
|
||||||
|
);
|
||||||
|
|
||||||
let title = document.getText(ranges[i]);
|
if (useSmartPaste) {
|
||||||
if (enabled === 'smart') {
|
smartPaste = checkSmartPaste(document, selectedRange);
|
||||||
smartPaste = checkSmartPaste(document.getText(), document.offsetAt(ranges[i].start), document.offsetAt(ranges[i].end));
|
title = smartPaste.updateTitle ? '' : document.getText(range);
|
||||||
title = smartPaste.updateTitle ? '' : document.getText(ranges[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const snippet = await tryGetUriListSnippet(document, urlList, token, title, placeHolderValue, smartPaste.pasteAsMarkdownLink, isExternalLink);
|
const snippet = await tryGetUriListSnippet(document, urlList, token, title, placeHolderValue, smartPaste.pasteAsMarkdownLink, isExternalLink);
|
||||||
|
@ -111,7 +138,7 @@ export async function createEditAddingLinksForUriList(document: vscode.TextDocum
|
||||||
|
|
||||||
smartPaste.pasteAsMarkdownLink = true;
|
smartPaste.pasteAsMarkdownLink = true;
|
||||||
placeHolderValue--;
|
placeHolderValue--;
|
||||||
edits.push(new vscode.SnippetTextEdit(ranges[i], snippet.snippet));
|
edits.push(new vscode.SnippetTextEdit(range, snippet.snippet));
|
||||||
label = snippet.label;
|
label = snippet.label;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,15 +148,15 @@ export async function createEditAddingLinksForUriList(document: vscode.TextDocum
|
||||||
return { additionalEdits, label };
|
return { additionalEdits, label };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function checkSmartPaste(documentText: string, start: number, end: number): SmartPaste {
|
export function checkSmartPaste(document: SkinnyTextDocument, selectedRange: vscode.Range): SmartPaste {
|
||||||
const SmartPaste: SmartPaste = { pasteAsMarkdownLink: true, updateTitle: false };
|
const SmartPaste: SmartPaste = { pasteAsMarkdownLink: true, updateTitle: false };
|
||||||
for (const regex of smartPasteRegexes) {
|
for (const regex of smartPasteRegexes) {
|
||||||
const matches = [...documentText.matchAll(regex.regex)];
|
const matches = [...document.getText().matchAll(regex.regex)];
|
||||||
for (const match of matches) {
|
for (const match of matches) {
|
||||||
if (match.index !== undefined) {
|
if (match.index !== undefined) {
|
||||||
const useDefaultPaste = start > match.index && end < match.index + match[0].length;
|
const useDefaultPaste = selectedRange.start.character > match.index && selectedRange.end.character < match.index + match[0].length;
|
||||||
SmartPaste.pasteAsMarkdownLink = !useDefaultPaste;
|
SmartPaste.pasteAsMarkdownLink = !useDefaultPaste;
|
||||||
SmartPaste.updateTitle = regex.is_markdown_link && start === match.index && end === match.index + match[0].length;
|
SmartPaste.updateTitle = regex.isMarkdownLink && selectedRange.start.character === match.index && selectedRange.end.character === match.index + match[0].length;
|
||||||
if (!SmartPaste.pasteAsMarkdownLink || SmartPaste.updateTitle) {
|
if (!SmartPaste.pasteAsMarkdownLink || SmartPaste.updateTitle) {
|
||||||
return SmartPaste;
|
return SmartPaste;
|
||||||
}
|
}
|
||||||
|
@ -139,7 +166,7 @@ export function checkSmartPaste(documentText: string, start: number, end: number
|
||||||
return SmartPaste;
|
return SmartPaste;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function tryGetUriListSnippet(document: vscode.TextDocument, urlList: String, token: vscode.CancellationToken, title = '', placeHolderValue = 0, pasteAsMarkdownLink = true, isExternalLink = false): Promise<{ snippet: vscode.SnippetString; label: string } | undefined> {
|
export async function tryGetUriListSnippet(document: SkinnyTextDocument, urlList: String, token: vscode.CancellationToken, title = '', placeHolderValue = 0, pasteAsMarkdownLink = true, isExternalLink = false): Promise<{ snippet: vscode.SnippetString; label: string } | undefined> {
|
||||||
if (token.isCancellationRequested) {
|
if (token.isCancellationRequested) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
@ -171,7 +198,8 @@ interface UriListSnippetOptions {
|
||||||
readonly separator?: string;
|
readonly separator?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createLinkSnippet(
|
export function appendToLinkSnippet(
|
||||||
|
snippet: vscode.SnippetString,
|
||||||
pasteAsMarkdownLink: boolean,
|
pasteAsMarkdownLink: boolean,
|
||||||
mdPath: string,
|
mdPath: string,
|
||||||
title: string,
|
title: string,
|
||||||
|
@ -180,7 +208,6 @@ export function createLinkSnippet(
|
||||||
isExternalLink: boolean,
|
isExternalLink: boolean,
|
||||||
): vscode.SnippetString {
|
): vscode.SnippetString {
|
||||||
const uriString = uri.toString(true);
|
const uriString = uri.toString(true);
|
||||||
const snippet = new vscode.SnippetString();
|
|
||||||
if (pasteAsMarkdownLink) {
|
if (pasteAsMarkdownLink) {
|
||||||
snippet.appendText('[');
|
snippet.appendText('[');
|
||||||
snippet.appendPlaceholder(escapeBrackets(title) || 'Title', placeholderValue);
|
snippet.appendPlaceholder(escapeBrackets(title) || 'Title', placeholderValue);
|
||||||
|
@ -192,7 +219,7 @@ export function createLinkSnippet(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createUriListSnippet(
|
export function createUriListSnippet(
|
||||||
document: vscode.TextDocument,
|
document: SkinnyTextDocument,
|
||||||
uris: readonly vscode.Uri[],
|
uris: readonly vscode.Uri[],
|
||||||
title = '',
|
title = '',
|
||||||
placeholderValue = 0,
|
placeholderValue = 0,
|
||||||
|
@ -204,7 +231,7 @@ export function createUriListSnippet(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const documentDir = getDocumentDir(document);
|
const documentDir = getDocumentDir(document.uri);
|
||||||
|
|
||||||
let snippet = new vscode.SnippetString();
|
let snippet = new vscode.SnippetString();
|
||||||
let insertedLinkCount = 0;
|
let insertedLinkCount = 0;
|
||||||
|
@ -244,7 +271,7 @@ export function createUriListSnippet(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
insertedLinkCount++;
|
insertedLinkCount++;
|
||||||
snippet = createLinkSnippet(pasteAsMarkdownLink, mdPath, title, uri, placeholderValue, isExternalLink);
|
snippet = appendToLinkSnippet(snippet, pasteAsMarkdownLink, mdPath, title, uri, placeholderValue, isExternalLink);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i < uris.length - 1 && uris.length > 1) {
|
if (i < uris.length - 1 && uris.length > 1) {
|
||||||
|
|
|
@ -5,128 +5,215 @@
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
import 'mocha';
|
import 'mocha';
|
||||||
import { checkSmartPaste, createLinkSnippet } from '../languageFeatures/copyFiles/shared';
|
import { SkinnyTextDocument, checkSmartPaste, createEditAddingLinksForUriList, appendToLinkSnippet } from '../languageFeatures/copyFiles/shared';
|
||||||
import { validateLink } from '../languageFeatures/copyFiles/copyPasteLinks';
|
import { validateLink } from '../languageFeatures/copyFiles/copyPasteLinks';
|
||||||
|
|
||||||
suite('createEditAddingLinksForUriList', () => {
|
suite('createEditAddingLinksForUriList', () => {
|
||||||
|
|
||||||
// end to end test of checkSmartPaste & createLinkSnippet
|
test('Markdown Link Pasting should occur for a valid link (end to end)', async () => {
|
||||||
// check multicursor (end to end)
|
// createEditAddingLinksForUriList -> checkSmartPaste -> tryGetUriListSnippet -> createUriListSnippet -> createLinkSnippet
|
||||||
|
|
||||||
|
const skinnyDocument: SkinnyTextDocument = {
|
||||||
|
uri: vscode.Uri.parse('file:///path/to/your/file'),
|
||||||
|
offsetAt: function () { return 0; },
|
||||||
|
getText: function () { return 'hello world!'; },
|
||||||
|
// lineAt: function (position: vscode.Position) {
|
||||||
|
// return {
|
||||||
|
// lineNumber: 0,
|
||||||
|
// text: 'hello world!',
|
||||||
|
// range: new vscode.Range(position, position),
|
||||||
|
// rangeIncludingLineBreak: new vscode.Range(position, position),
|
||||||
|
// firstNonWhitespaceCharacterIndex: 0,
|
||||||
|
// isEmptyOrWhitespace: false
|
||||||
|
// } as vscode.TextLine;
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await createEditAddingLinksForUriList(skinnyDocument, [new vscode.Range(0, 0, 0, 12)], 'https://www.microsoft.com/', true, true, new vscode.CancellationTokenSource().token);
|
||||||
|
// need to check the actual result -> snippet value
|
||||||
|
assert.strictEqual(result?.label, 'Insert Markdown Link');
|
||||||
|
});
|
||||||
|
|
||||||
suite('validateLink', () => {
|
suite('validateLink', () => {
|
||||||
|
|
||||||
test('Markdown pasting should occur for a valid link.', () => {
|
test('Markdown pasting should occur for a valid link.', () => {
|
||||||
const isLink = validateLink('https://www.microsoft.com');
|
const isLink = validateLink('https://www.microsoft.com/').isValid;
|
||||||
|
assert.strictEqual(isLink, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Markdown pasting should occur for a valid link preceded by a new line.', () => {
|
||||||
|
const isLink = validateLink('\r\nhttps://www.microsoft.com/').isValid;
|
||||||
|
assert.strictEqual(isLink, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Markdown pasting should occur for a valid link followed by a new line.', () => {
|
||||||
|
const isLink = validateLink('https://www.microsoft.com/\r\n').isValid;
|
||||||
assert.strictEqual(isLink, true);
|
assert.strictEqual(isLink, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Markdown pasting should not occur for a valid hostname and invalid protool.', () => {
|
test('Markdown pasting should not occur for a valid hostname and invalid protool.', () => {
|
||||||
const isLink = validateLink('invalid://www.microsoft.com');
|
const isLink = validateLink('invalid:www.microsoft.com').isValid;
|
||||||
assert.strictEqual(isLink, false);
|
assert.strictEqual(isLink, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Markdown pasting should not occur for plain text.', () => {
|
test('Markdown pasting should not occur for plain text.', () => {
|
||||||
const isLink = validateLink('hello world!');
|
const isLink = validateLink('hello world!').isValid;
|
||||||
assert.strictEqual(isLink, false);
|
assert.strictEqual(isLink, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Markdown pasting should not occur for plain text including a colon.', () => {
|
test('Markdown pasting should not occur for plain text including a colon.', () => {
|
||||||
const isLink = validateLink('hello: world!');
|
const isLink = validateLink('hello: world!').isValid;
|
||||||
assert.strictEqual(isLink, false);
|
assert.strictEqual(isLink, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Markdown pasting should not occur for plain text including a slashes.', () => {
|
test('Markdown pasting should not occur for plain text including a slashes.', () => {
|
||||||
const isLink = validateLink('hello//world!');
|
const isLink = validateLink('helloworld!').isValid;
|
||||||
assert.strictEqual(isLink, false);
|
assert.strictEqual(isLink, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Markdown pasting should not occur for a link followed by text.', () => {
|
test('Markdown pasting should not occur for a link followed by text.', () => {
|
||||||
const isLink = validateLink('https://www.microsoft.com hello world!');
|
const isLink = validateLink('https://www.microsoft.com/ hello world!').isValid;
|
||||||
assert.strictEqual(isLink, false);
|
assert.strictEqual(isLink, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Markdown pasting should not occur for a link preceded or followed by spaces.', () => {
|
test('Markdown pasting should occur for a link preceded or followed by spaces.', () => {
|
||||||
const isLink = validateLink(' https://www.microsoft.com ');
|
const isLink = validateLink(' https://www.microsoft.com/ ').isValid;
|
||||||
assert.strictEqual(isLink, false);
|
assert.strictEqual(isLink, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Markdown pasting should not occur for a link with an invalid scheme.', () => {
|
test('Markdown pasting should not occur for a link with an invalid scheme.', () => {
|
||||||
const isLink = validateLink('hello://www.microsoft.com');
|
const isLink = validateLink('hello:www.microsoft.com').isValid;
|
||||||
assert.strictEqual(isLink, false);
|
assert.strictEqual(isLink, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Markdown pasting should not occur for multiple links being pasted.', () => {
|
test('Markdown pasting should not occur for multiple links being pasted.', () => {
|
||||||
const isLink = validateLink('https://www.microsoft.com\r\nhttps://www.microsoft.com\r\nhttps://www.microsoft.com\r\nhttps://www.microsoft.com');
|
const isLink = validateLink('https://www.microsoft.com/\r\nhttps://www.microsoft.com/\r\nhttps://www.microsoft.com/\r\nhttps://www.microsoft.com/').isValid;
|
||||||
assert.strictEqual(isLink, false);
|
assert.strictEqual(isLink, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Markdown pasting should not occur for multiple links with spaces being pasted.', () => {
|
||||||
|
const isLink = validateLink('https://www.microsoft.com/ \r\nhttps://www.microsoft.com/\r\nhttps://www.microsoft.com/\r\n hello \r\nhttps://www.microsoft.com/').isValid;
|
||||||
|
assert.strictEqual(isLink, false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
suite('createLinkSnippet', () => {
|
suite('appendToLinkSnippet', () => {
|
||||||
test('Should not create Markdown link snippet when pasteAsMarkdownLink is false', () => {
|
test('Should not create Markdown link snippet when pasteAsMarkdownLink is false', () => {
|
||||||
const uri = vscode.Uri.parse('https://www.microsoft.com');
|
const uri = vscode.Uri.parse('https://www.microsoft.com/');
|
||||||
const snippet = createLinkSnippet(false, 'https://www.microsoft.com', '', uri, 0, true);
|
const snippet = appendToLinkSnippet(new vscode.SnippetString(''), false, 'https:/www.microsoft.com', '', uri, 0, true);
|
||||||
assert.strictEqual(snippet?.value, 'https://www.microsoft.com/');
|
assert.strictEqual(snippet?.value, 'https://www.microsoft.com/');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Should create Markdown link snippet when pasteAsMarkdownLink is true', () => {
|
test('Should create Markdown link snippet when pasteAsMarkdownLink is true', () => {
|
||||||
const uri = vscode.Uri.parse('https://www.microsoft.com');
|
const uri = vscode.Uri.parse('https://www.microsoft.com/');
|
||||||
const snippet = createLinkSnippet(true, 'https://www.microsoft.com', '', uri, 0, true);
|
const snippet = appendToLinkSnippet(new vscode.SnippetString(''), true, 'https:/www.microsoft.com', '', uri, 0, true);
|
||||||
assert.strictEqual(snippet?.value, '[${0:Title}](https://www.microsoft.com/)');
|
assert.strictEqual(snippet?.value, '[${0:Title}](https://www.microsoft.com/)');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Should use an unencoded URI string in Markdown link when passing in an external browser link', () => {
|
test('Should use an unencoded URI string in Markdown link when passing in an external browser link', () => {
|
||||||
const uri = vscode.Uri.parse('https://www.microsoft.com');
|
const uri = vscode.Uri.parse('https://www.microsoft.com/');
|
||||||
const snippet = createLinkSnippet(true, 'https://www.microsoft.com', '', uri, 0, true);
|
const snippet = appendToLinkSnippet(new vscode.SnippetString(''), true, 'https:/www.microsoft.com', '', uri, 0, true);
|
||||||
assert.strictEqual(snippet?.value, '[${0:Title}](https://www.microsoft.com/)');
|
assert.strictEqual(snippet?.value, '[${0:Title}](https://www.microsoft.com/)');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
suite('pasteAsMarkdownLink', () => {
|
|
||||||
|
suite('checkSmartPaste', () => {
|
||||||
|
|
||||||
|
const skinnyDocument: SkinnyTextDocument = {
|
||||||
|
uri: vscode.Uri.file('/path/to/your/file'),
|
||||||
|
offsetAt: function () { return 0; },
|
||||||
|
getText: function () { return 'hello world!'; },
|
||||||
|
// lineAt: function (position: vscode.Position) {
|
||||||
|
// return {
|
||||||
|
// lineNumber: 0,
|
||||||
|
// text: 'hello world!',
|
||||||
|
// range: new vscode.Range(position, position),
|
||||||
|
// rangeIncludingLineBreak: new vscode.Range(position, position),
|
||||||
|
// firstNonWhitespaceCharacterIndex: 0,
|
||||||
|
// isEmptyOrWhitespace: false
|
||||||
|
// } as vscode.TextLine;
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
|
||||||
test('Should evaluate pasteAsMarkdownLink as true for selected plain text', () => {
|
test('Should evaluate pasteAsMarkdownLink as true for selected plain text', () => {
|
||||||
const smartPaste = checkSmartPaste("hello! world", 0, 5);
|
const range = new vscode.Range(0, 5, 0, 5);
|
||||||
|
const smartPaste = checkSmartPaste(skinnyDocument, range);
|
||||||
assert.strictEqual(smartPaste.pasteAsMarkdownLink, true);
|
assert.strictEqual(smartPaste.pasteAsMarkdownLink, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('Should evaluate updateTitle as true for pasting over a Markdown link', () => {
|
|
||||||
const smartPaste = checkSmartPaste("[a](bc)", 0, 7);
|
|
||||||
assert.strictEqual(smartPaste.updateTitle, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Should evaluate updateTitle as true for pasting over a Markdown image link', () => {
|
|
||||||
const smartPaste = checkSmartPaste("![a](bc)", 0, 8);
|
|
||||||
assert.strictEqual(smartPaste.updateTitle, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Should evaluate pasteAsMarkdownLink as false for pasting within a Markdown link', () => {
|
|
||||||
const smartPaste = checkSmartPaste("[a](bcdef)", 4, 6);
|
|
||||||
assert.strictEqual(smartPaste.pasteAsMarkdownLink, false);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Should evaluate pasteAsMarkdownLink as false for pasting within a Markdown image link', () => {
|
|
||||||
const smartPaste = checkSmartPaste('![alt](https://)', 7, 15);
|
|
||||||
assert.strictEqual(smartPaste.pasteAsMarkdownLink, false);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Should evaluate pasteAsMarkdownLink as false for pasting within a code block', () => {
|
test('Should evaluate pasteAsMarkdownLink as false for pasting within a code block', () => {
|
||||||
const smartPaste = checkSmartPaste('```\r\n\r\n```', 5, 5);
|
skinnyDocument.getText = function () { return '```\r\n\r\n```'; };
|
||||||
assert.strictEqual(smartPaste.pasteAsMarkdownLink, false);
|
const range = new vscode.Range(0, 5, 0, 5);
|
||||||
});
|
const smartPaste = checkSmartPaste(skinnyDocument, range);
|
||||||
|
|
||||||
test('Should evaluate pasteAsMarkdownLink as false for pasting within inline code', () => {
|
|
||||||
const smartPaste = checkSmartPaste('``', 1, 1);
|
|
||||||
assert.strictEqual(smartPaste.pasteAsMarkdownLink, false);
|
assert.strictEqual(smartPaste.pasteAsMarkdownLink, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Should evaluate pasteAsMarkdownLink as false for pasting within a math block', () => {
|
test('Should evaluate pasteAsMarkdownLink as false for pasting within a math block', () => {
|
||||||
const smartPaste = checkSmartPaste('$$$\r\n\r\n$$$', 5, 5);
|
skinnyDocument.getText = function () { return '$$$\r\n\r\n$$$'; };
|
||||||
|
const range = new vscode.Range(0, 5, 0, 5);
|
||||||
|
const smartPaste = checkSmartPaste(skinnyDocument, range);
|
||||||
assert.strictEqual(smartPaste.pasteAsMarkdownLink, false);
|
assert.strictEqual(smartPaste.pasteAsMarkdownLink, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const linkSkinnyDoc: SkinnyTextDocument = {
|
||||||
|
uri: vscode.Uri.file('/path/to/your/file'),
|
||||||
|
offsetAt: function () { return 0; },
|
||||||
|
getText: function () { return '[a](bcdef)'; },
|
||||||
|
};
|
||||||
|
|
||||||
|
test('Should evaluate updateTitle as true for pasting over a Markdown link', () => {
|
||||||
|
const range = new vscode.Range(0, 0, 0, 10);
|
||||||
|
const smartPaste = checkSmartPaste(linkSkinnyDoc, range);
|
||||||
|
assert.strictEqual(smartPaste.updateTitle, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Should evaluate pasteAsMarkdownLink as false for pasting within a Markdown link', () => {
|
||||||
|
const range = new vscode.Range(0, 4, 0, 6);
|
||||||
|
const smartPaste = checkSmartPaste(linkSkinnyDoc, range);
|
||||||
|
|
||||||
|
assert.strictEqual(smartPaste.pasteAsMarkdownLink, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const imageLinkSkinnyDoc: SkinnyTextDocument = {
|
||||||
|
uri: vscode.Uri.file('/path/to/your/file'),
|
||||||
|
offsetAt: function () { return 0; },
|
||||||
|
getText: function () { return '![a](bcdef)'; },
|
||||||
|
};
|
||||||
|
|
||||||
|
test('Should evaluate updateTitle as true for pasting over a Markdown image link', () => {
|
||||||
|
const range = new vscode.Range(0, 0, 0, 11);
|
||||||
|
const smartPaste = checkSmartPaste(imageLinkSkinnyDoc, range);
|
||||||
|
assert.strictEqual(smartPaste.updateTitle, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Should evaluate pasteAsMarkdownLink as false for pasting within a Markdown image link', () => {
|
||||||
|
const range = new vscode.Range(0, 5, 0, 10);
|
||||||
|
const smartPaste = checkSmartPaste(imageLinkSkinnyDoc, range);
|
||||||
|
assert.strictEqual(smartPaste.pasteAsMarkdownLink, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
const inlineCodeSkinnyCode: SkinnyTextDocument = {
|
||||||
|
uri: vscode.Uri.file('/path/to/your/file'),
|
||||||
|
offsetAt: function () { return 0; },
|
||||||
|
getText: function () { return '``'; },
|
||||||
|
};
|
||||||
|
|
||||||
|
test('Should evaluate pasteAsMarkdownLink as false for pasting within inline code', () => {
|
||||||
|
const range = new vscode.Range(0, 1, 0, 1);
|
||||||
|
const smartPaste = checkSmartPaste(inlineCodeSkinnyCode, range);
|
||||||
|
assert.strictEqual(smartPaste.pasteAsMarkdownLink, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
const inlineMathSkinnyDoc: SkinnyTextDocument = {
|
||||||
|
uri: vscode.Uri.file('/path/to/your/file'),
|
||||||
|
offsetAt: function () { return 0; },
|
||||||
|
getText: function () { return '$$'; },
|
||||||
|
};
|
||||||
|
|
||||||
test('Should evaluate pasteAsMarkdownLink as false for pasting within inline math', () => {
|
test('Should evaluate pasteAsMarkdownLink as false for pasting within inline math', () => {
|
||||||
const smartPaste = checkSmartPaste('$$', 1, 1);
|
const range = new vscode.Range(0, 1, 0, 1);
|
||||||
|
const smartPaste = checkSmartPaste(inlineMathSkinnyDoc, range);
|
||||||
assert.strictEqual(smartPaste.pasteAsMarkdownLink, false);
|
assert.strictEqual(smartPaste.pasteAsMarkdownLink, false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,24 +7,24 @@ import * as vscode from 'vscode';
|
||||||
import { Schemes } from './schemes';
|
import { Schemes } from './schemes';
|
||||||
import { Utils } from 'vscode-uri';
|
import { Utils } from 'vscode-uri';
|
||||||
|
|
||||||
export function getDocumentDir(document: vscode.TextDocument): vscode.Uri | undefined {
|
export function getDocumentDir(uri: vscode.Uri): vscode.Uri | undefined {
|
||||||
const docUri = getParentDocumentUri(document);
|
const docUri = getParentDocumentUri(uri);
|
||||||
if (docUri.scheme === Schemes.untitled) {
|
if (docUri.scheme === Schemes.untitled) {
|
||||||
return vscode.workspace.workspaceFolders?.[0]?.uri;
|
return vscode.workspace.workspaceFolders?.[0]?.uri;
|
||||||
}
|
}
|
||||||
return Utils.dirname(docUri);
|
return Utils.dirname(docUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getParentDocumentUri(document: vscode.TextDocument): vscode.Uri {
|
export function getParentDocumentUri(uri: vscode.Uri): vscode.Uri {
|
||||||
if (document.uri.scheme === Schemes.notebookCell) {
|
if (uri.scheme === Schemes.notebookCell) {
|
||||||
for (const notebook of vscode.workspace.notebookDocuments) {
|
for (const notebook of vscode.workspace.notebookDocuments) {
|
||||||
for (const cell of notebook.getCells()) {
|
for (const cell of notebook.getCells()) {
|
||||||
if (cell.document === document) {
|
if (cell.document.uri.toString() === uri.toString()) {
|
||||||
return notebook.uri;
|
return notebook.uri;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return document.uri;
|
return uri;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue