Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/microsoft/vscode.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/markdown-language-features/src/languageFeatures/pathCompletions.ts')
-rw-r--r--extensions/markdown-language-features/src/languageFeatures/pathCompletions.ts369
1 files changed, 0 insertions, 369 deletions
diff --git a/extensions/markdown-language-features/src/languageFeatures/pathCompletions.ts b/extensions/markdown-language-features/src/languageFeatures/pathCompletions.ts
deleted file mode 100644
index 82e28faf3a4..00000000000
--- a/extensions/markdown-language-features/src/languageFeatures/pathCompletions.ts
+++ /dev/null
@@ -1,369 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import { dirname, resolve } from 'path';
-import * as vscode from 'vscode';
-import { IMdParser } from '../markdownEngine';
-import { TableOfContents } from '../tableOfContents';
-import { getLine, ITextDocument } from '../types/textDocument';
-import { resolveUriToMarkdownFile } from '../util/openDocumentLink';
-import { Schemes } from '../util/schemes';
-import { IMdWorkspace } from '../workspace';
-import { MdLinkProvider } from './documentLinks';
-
-enum CompletionContextKind {
- /** `[...](|)` */
- Link,
-
- /** `[...][|]` */
- ReferenceLink,
-
- /** `[]: |` */
- LinkDefinition,
-}
-
-interface AnchorContext {
- /**
- * Link text before the `#`.
- *
- * For `[text](xy#z|abc)` this is `xy`.
- */
- readonly beforeAnchor: string;
-
- /**
- * Text of the anchor before the current position.
- *
- * For `[text](xy#z|abc)` this is `z`.
- */
- readonly anchorPrefix: string;
-}
-
-interface CompletionContext {
- readonly kind: CompletionContextKind;
-
- /**
- * Text of the link before the current position
- *
- * For `[text](xy#z|abc)` this is `xy#z`.
- */
- readonly linkPrefix: string;
-
- /**
- * Position of the start of the link.
- *
- * For `[text](xy#z|abc)` this is the position before `xy`.
- */
- readonly linkTextStartPosition: vscode.Position;
-
- /**
- * Text of the link after the current position.
- *
- * For `[text](xy#z|abc)` this is `abc`.
- */
- readonly linkSuffix: string;
-
- /**
- * Info if the link looks like it is for an anchor: `[](#header)`
- */
- readonly anchorInfo?: AnchorContext;
-
- /**
- * Indicates that the completion does not require encoding.
- */
- readonly skipEncoding?: boolean;
-}
-
-function tryDecodeUriComponent(str: string): string {
- try {
- return decodeURIComponent(str);
- } catch {
- return str;
- }
-}
-
-/**
- * Adds path completions in markdown files by implementing {@link vscode.CompletionItemProvider}.
- */
-export class MdVsCodePathCompletionProvider implements vscode.CompletionItemProvider {
-
- constructor(
- private readonly workspace: IMdWorkspace,
- private readonly parser: IMdParser,
- private readonly linkProvider: MdLinkProvider,
- ) { }
-
- public async provideCompletionItems(document: ITextDocument, position: vscode.Position, _token: vscode.CancellationToken, _context: vscode.CompletionContext): Promise<vscode.CompletionItem[]> {
- if (!this.arePathSuggestionEnabled(document)) {
- return [];
- }
-
- const context = this.getPathCompletionContext(document, position);
- if (!context) {
- return [];
- }
-
- switch (context.kind) {
- case CompletionContextKind.ReferenceLink: {
- const items: vscode.CompletionItem[] = [];
- for await (const item of this.provideReferenceSuggestions(document, position, context)) {
- items.push(item);
- }
- return items;
- }
-
- case CompletionContextKind.LinkDefinition:
- case CompletionContextKind.Link: {
- const items: vscode.CompletionItem[] = [];
-
- const isAnchorInCurrentDoc = context.anchorInfo && context.anchorInfo.beforeAnchor.length === 0;
-
- // Add anchor #links in current doc
- if (context.linkPrefix.length === 0 || isAnchorInCurrentDoc) {
- const insertRange = new vscode.Range(context.linkTextStartPosition, position);
- for await (const item of this.provideHeaderSuggestions(document, position, context, insertRange)) {
- items.push(item);
- }
- }
-
- if (!isAnchorInCurrentDoc) {
- if (context.anchorInfo) { // Anchor to a different document
- const rawUri = this.resolveReference(document, context.anchorInfo.beforeAnchor);
- if (rawUri) {
- const otherDoc = await resolveUriToMarkdownFile(this.workspace, rawUri);
- if (otherDoc) {
- const anchorStartPosition = position.translate({ characterDelta: -(context.anchorInfo.anchorPrefix.length + 1) });
- const range = new vscode.Range(anchorStartPosition, position);
- for await (const item of this.provideHeaderSuggestions(otherDoc, position, context, range)) {
- items.push(item);
- }
- }
- }
- } else { // Normal path suggestions
- for await (const item of this.providePathSuggestions(document, position, context)) {
- items.push(item);
- }
- }
- }
-
- return items;
- }
- }
- }
-
- private arePathSuggestionEnabled(document: ITextDocument): boolean {
- const config = vscode.workspace.getConfiguration('markdown', document.uri);
- return config.get('suggest.paths.enabled', true);
- }
-
- /// [...](...|
- private readonly linkStartPattern = /\[([^\]]*?)\]\(\s*(<[^\>\)]*|[^\s\(\)]*)$/;
-
- /// [...][...|
- private readonly referenceLinkStartPattern = /\[([^\]]*?)\]\[\s*([^\s\(\)]*)$/;
-
- /// [id]: |
- private readonly definitionPattern = /^\s*\[[\w\-]+\]:\s*([^\s]*)$/m;
-
- private getPathCompletionContext(document: ITextDocument, position: vscode.Position): CompletionContext | undefined {
- const line = getLine(document, position.line);
-
- const linePrefixText = line.slice(0, position.character);
- const lineSuffixText = line.slice(position.character);
-
- const linkPrefixMatch = linePrefixText.match(this.linkStartPattern);
- if (linkPrefixMatch) {
- const isAngleBracketLink = linkPrefixMatch[2].startsWith('<');
- const prefix = linkPrefixMatch[2].slice(isAngleBracketLink ? 1 : 0);
- if (this.refLooksLikeUrl(prefix)) {
- return undefined;
- }
-
- const suffix = lineSuffixText.match(/^[^\)\s][^\)\s\>]*/);
- return {
- kind: CompletionContextKind.Link,
- linkPrefix: tryDecodeUriComponent(prefix),
- linkTextStartPosition: position.translate({ characterDelta: -prefix.length }),
- linkSuffix: suffix ? suffix[0] : '',
- anchorInfo: this.getAnchorContext(prefix),
- skipEncoding: isAngleBracketLink,
- };
- }
-
- const definitionLinkPrefixMatch = linePrefixText.match(this.definitionPattern);
- if (definitionLinkPrefixMatch) {
- const isAngleBracketLink = definitionLinkPrefixMatch[1].startsWith('<');
- const prefix = definitionLinkPrefixMatch[1].slice(isAngleBracketLink ? 1 : 0);
- if (this.refLooksLikeUrl(prefix)) {
- return undefined;
- }
-
- const suffix = lineSuffixText.match(/^[^\s]*/);
- return {
- kind: CompletionContextKind.LinkDefinition,
- linkPrefix: tryDecodeUriComponent(prefix),
- linkTextStartPosition: position.translate({ characterDelta: -prefix.length }),
- linkSuffix: suffix ? suffix[0] : '',
- anchorInfo: this.getAnchorContext(prefix),
- skipEncoding: isAngleBracketLink,
- };
- }
-
- const referenceLinkPrefixMatch = linePrefixText.match(this.referenceLinkStartPattern);
- if (referenceLinkPrefixMatch) {
- const prefix = referenceLinkPrefixMatch[2];
- const suffix = lineSuffixText.match(/^[^\]\s]*/);
- return {
- kind: CompletionContextKind.ReferenceLink,
- linkPrefix: prefix,
- linkTextStartPosition: position.translate({ characterDelta: -prefix.length }),
- linkSuffix: suffix ? suffix[0] : '',
- };
- }
-
- return undefined;
- }
-
- /**
- * Check if {@param ref} looks like a 'http:' style url.
- */
- private refLooksLikeUrl(prefix: string): boolean {
- return /^\s*[\w\d\-]+:/.test(prefix);
- }
-
- private getAnchorContext(prefix: string): AnchorContext | undefined {
- const anchorMatch = prefix.match(/^(.*)#([\w\d\-]*)$/);
- if (!anchorMatch) {
- return undefined;
- }
- return {
- beforeAnchor: anchorMatch[1],
- anchorPrefix: anchorMatch[2],
- };
- }
-
- private async *provideReferenceSuggestions(document: ITextDocument, position: vscode.Position, context: CompletionContext): AsyncIterable<vscode.CompletionItem> {
- const insertionRange = new vscode.Range(context.linkTextStartPosition, position);
- const replacementRange = new vscode.Range(insertionRange.start, position.translate({ characterDelta: context.linkSuffix.length }));
-
- const { definitions } = await this.linkProvider.getLinks(document);
- for (const [_, def] of definitions) {
- yield {
- kind: vscode.CompletionItemKind.Reference,
- label: def.ref.text,
- range: {
- inserting: insertionRange,
- replacing: replacementRange,
- },
- };
- }
- }
-
- private async *provideHeaderSuggestions(document: ITextDocument, position: vscode.Position, context: CompletionContext, insertionRange: vscode.Range): AsyncIterable<vscode.CompletionItem> {
- const toc = await TableOfContents.createForDocumentOrNotebook(this.parser, document);
- for (const entry of toc.entries) {
- const replacementRange = new vscode.Range(insertionRange.start, position.translate({ characterDelta: context.linkSuffix.length }));
- yield {
- kind: vscode.CompletionItemKind.Reference,
- label: '#' + decodeURIComponent(entry.slug.value),
- range: {
- inserting: insertionRange,
- replacing: replacementRange,
- },
- };
- }
- }
-
- private async *providePathSuggestions(document: ITextDocument, position: vscode.Position, context: CompletionContext): AsyncIterable<vscode.CompletionItem> {
- const valueBeforeLastSlash = context.linkPrefix.substring(0, context.linkPrefix.lastIndexOf('/') + 1); // keep the last slash
-
- const parentDir = this.resolveReference(document, valueBeforeLastSlash || '.');
- if (!parentDir) {
- return;
- }
-
- const pathSegmentStart = position.translate({ characterDelta: valueBeforeLastSlash.length - context.linkPrefix.length });
- const insertRange = new vscode.Range(pathSegmentStart, position);
-
- const pathSegmentEnd = position.translate({ characterDelta: context.linkSuffix.length });
- const replacementRange = new vscode.Range(pathSegmentStart, pathSegmentEnd);
-
- let dirInfo: [string, vscode.FileType][];
- try {
- dirInfo = await this.workspace.readDirectory(parentDir);
- } catch {
- return;
- }
-
- for (const [name, type] of dirInfo) {
- // Exclude paths that start with `.`
- if (name.startsWith('.')) {
- continue;
- }
-
- const isDir = type === vscode.FileType.Directory;
- yield {
- label: isDir ? name + '/' : name,
- insertText: (context.skipEncoding ? name : encodeURIComponent(name)) + (isDir ? '/' : ''),
- kind: isDir ? vscode.CompletionItemKind.Folder : vscode.CompletionItemKind.File,
- range: {
- inserting: insertRange,
- replacing: replacementRange,
- },
- command: isDir ? { command: 'editor.action.triggerSuggest', title: '' } : undefined,
- };
- }
- }
-
- private resolveReference(document: ITextDocument, ref: string): vscode.Uri | undefined {
- const docUri = this.getFileUriOfTextDocument(document);
-
- if (ref.startsWith('/')) {
- const workspaceFolder = vscode.workspace.getWorkspaceFolder(docUri);
- if (workspaceFolder) {
- return vscode.Uri.joinPath(workspaceFolder.uri, ref);
- } else {
- return this.resolvePath(docUri, ref.slice(1));
- }
- }
-
- return this.resolvePath(docUri, ref);
- }
-
- private resolvePath(root: vscode.Uri, ref: string): vscode.Uri | undefined {
- try {
- if (root.scheme === Schemes.file) {
- return vscode.Uri.file(resolve(dirname(root.fsPath), ref));
- } else {
- return root.with({
- path: resolve(dirname(root.path), ref),
- });
- }
- } catch {
- return undefined;
- }
- }
-
- private getFileUriOfTextDocument(document: ITextDocument) {
- if (document.uri.scheme === 'vscode-notebook-cell') {
- const notebook = vscode.workspace.notebookDocuments
- .find(notebook => notebook.getCells().some(cell => cell.document === document));
-
- if (notebook) {
- return notebook.uri;
- }
- }
-
- return document.uri;
- }
-}
-
-export function registerPathCompletionSupport(
- selector: vscode.DocumentSelector,
- workspace: IMdWorkspace,
- parser: IMdParser,
- linkProvider: MdLinkProvider,
-): vscode.Disposable {
- return vscode.languages.registerCompletionItemProvider(selector, new MdVsCodePathCompletionProvider(workspace, parser, linkProvider), '.', '/', '#');
-}