<commit_before>import fsAsync from 'fs/promises';
import path from 'path';

import vs from 'vscode';
import minimatch from 'minimatch';
import { FileInfo } from './FileInfo';
import PathConfiguration from './PathConfiguration';
import { isDirectory, pathExists } from './FsUtils';

interface MappingItem {
    currentDir: string;
    insertedPath: string;
}

const configuration = PathConfiguration.configuration;

export class PathAutocomplete implements vs.CompletionItemProvider {
    private currentFile: string;
    private currentLine: string;
    private currentPosition: number;
    private namePrefix: string;

    async provideCompletionItems(
        document: vs.TextDocument,
        position: vs.Position,
        _token: vs.CancellationToken,
    ): Promise<vs.CompletionItem[]> {
        configuration.update(document.uri);

        this.currentFile = document.fileName;
        const currentLine = document.getText(document.lineAt(position).range);
        this.currentLine = currentLine;
        this.currentPosition = position.character;
        this.namePrefix = this.getNamePrefix();

        if (!this.shouldProvide()) {
            return [];
        }

        const foldersPath = await this.getFoldersPath(
            document.fileName,
            currentLine,
            position.character,
        );

        if (foldersPath.length === 0) {
            return [];
        }

        const folderItems = await this.getFolderItems(foldersPath);

        // build the list of the completion items
        const result = folderItems.filter(this.filter, this).map((file) => {
            const completion = new vs.CompletionItem(file.name);

            // correct suggestion Item icon, ref issue#100
            completion.detail = file.path;
            completion.insertText = this.getInsertText(file);

            // show folders before files
            if (file.isDirectory) {
                if (configuration.data.useBackslash) {
                    completion.label += '\\';
                } else {
                    completion.label += '/';
                }

                if (configuration.data.enableFolderTrailingSlash) {
                    let commandText = '/';

                    if (configuration.data.useBackslash) {
                        commandText = this.isInsideQuotes() ? '\\\\' : '\\';
                    }

                    completion.command = {
                        command: 'default:type',
                        title: 'triggerSuggest',
                        arguments: [
                            {
                                text: commandText,
                            },
                        ],
                    };
                }

                completion.sortText = 'd';
                completion.kind = vs.CompletionItemKind.Folder;
            } else {
                completion.sortText = 'f';
                completion.kind = vs.CompletionItemKind.File;
            }

            // this is deprecated but still needed for the completion to work
            // in json files
            completion.textEdit = new vs.TextEdit(
                new vs.Range(position, position),
                completion.insertText,
            );

            return completion;
        });

        // add the `up one folder` item
        if (!configuration.data.disableUpOneFolder) {
            result.unshift(new vs.CompletionItem('..'));
        }

        return result;
    }

    /**
     * Gets the name prefix for the completion item.
     * This is used when the path that the user typed so far
     * contains part of the file/folder name
     * Examples:
     *      /folder/Fi => complete path is /folder/File => will return Fi
     *      /folder/subfo => complete path is /folder/subfolder => will return subfo
     */
    getNamePrefix(): string {
        const userPath = this.getUserPath(this.currentLine, this.currentPosition);
        if (userPath.endsWith('/') || userPath.endsWith('\\')) {
            return '';
        }

        return path.basename(userPath);
    }

    /**
     * Determines if the file extension should be included in the selected options when
     * the selection is made.
     */
    isExtensionEnabled(): boolean {
        if (this.currentLine.match(/require|import/)) {
            return configuration.data.withExtensionOnImport;
        }

        return configuration.data.withExtension;
    }

    getInsertText(file: FileInfo): string {
        let insertText = '';

        if (this.isExtensionEnabled() || file.isDirectory) {
            insertText = path.basename(file.name);
        } else {
            // remove the extension
            insertText = path.basename(file.name, path.extname(file.name));
        }<commit_msg>refactor: completion label should be consistent with insertText<commit_after>import fsAsync from 'fs/promises';
import path from 'path';

import vs from 'vscode';
import minimatch from 'minimatch';
import { FileInfo } from './FileInfo';
import PathConfiguration from './PathConfiguration';
import { isDirectory, pathExists } from './FsUtils';

interface MappingItem {
    currentDir: string;
    insertedPath: string;
}

const configuration = PathConfiguration.configuration;

export class PathAutocomplete implements vs.CompletionItemProvider {
    private currentFile: string;
    private currentLine: string;
    private currentPosition: number;
    private namePrefix: string;

    async provideCompletionItems(
        document: vs.TextDocument,
        position: vs.Position,
        _token: vs.CancellationToken,
    ): Promise<vs.CompletionItem[]> {
        configuration.update(document.uri);

        this.currentFile = document.fileName;
        const currentLine = document.getText(document.lineAt(position).range);
        this.currentLine = currentLine;
        this.currentPosition = position.character;
        this.namePrefix = this.getNamePrefix();

        if (!this.shouldProvide()) {
            return [];
        }

        const foldersPath = await this.getFoldersPath(
            document.fileName,
            currentLine,
            position.character,
        );

        if (foldersPath.length === 0) {
            return [];
        }

        const folderItems = await this.getFolderItems(foldersPath);

        // build the list of the completion items
        const result = folderItems.filter(this.filter, this).map((file) => {
            const insertText = this.getInsertText(file);
            const completion = new vs.CompletionItem(insertText);

            // correct suggestion Item icon, ref issue#100
            completion.detail = file.path;
            completion.insertText = insertText;

            // show folders before files
            if (file.isDirectory) {
                if (configuration.data.useBackslash) {
                    completion.label += '\\';
                } else {
                    completion.label += '/';
                }

                if (configuration.data.enableFolderTrailingSlash) {
                    let commandText = '/';

                    if (configuration.data.useBackslash) {
                        commandText = this.isInsideQuotes() ? '\\\\' : '\\';
                    }

                    completion.command = {
                        command: 'default:type',
                        title: 'triggerSuggest',
                        arguments: [
                            {
                                text: commandText,
                            },
                        ],
                    };
                }

                completion.sortText = 'd';
                completion.kind = vs.CompletionItemKind.Folder;
            } else {
                completion.sortText = 'f';
                completion.kind = vs.CompletionItemKind.File;
            }

            // this is deprecated but still needed for the completion to work
            // in json files
            completion.textEdit = new vs.TextEdit(
                new vs.Range(position, position),
                completion.insertText,
            );

            return completion;
        });

        // add the `up one folder` item
        if (!configuration.data.disableUpOneFolder) {
            result.unshift(new vs.CompletionItem('..'));
        }

        return result;
    }

    /**
     * Gets the name prefix for the completion item.
     * This is used when the path that the user typed so far
     * contains part of the file/folder name
     * Examples:
     *      /folder/Fi => complete path is /folder/File => will return Fi
     *      /folder/subfo => complete path is /folder/subfolder => will return subfo
     */
    getNamePrefix(): string {
        const userPath = this.getUserPath(this.currentLine, this.currentPosition);
        if (userPath.endsWith('/') || userPath.endsWith('\\')) {
            return '';
        }

        return path.basename(userPath);
    }

    /**
     * Determines if the file extension should be included in the selected options when
     * the selection is made.
     */
    isExtensionEnabled(): boolean {
        if (this.currentLine.match(/require|import/)) {
            return configuration.data.withExtensionOnImport;
        }

        return configuration.data.withExtension;
    }

    getInsertText(file: FileInfo): string {
        let insertText = '';

        if (this.isExtensionEnabled() || file.isDirectory) {
            insertText = file.name;
        } else {
            // remove the extension
            insertText = path.basename(file.name, path.extname(file.name));
        }
{'fraction_non_alphanumeric': 0.09726980172324302, 'fraction_numerical': 0.0008304785632720855, 'mean_word_length': 1.706179775280899, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1873445', 'n_tokens_mistral': 2382, 'n_tokens_neox': 2270, 'n_words': 584}