<commit_before>import * as vscode from 'vscode'
import * as fs from 'fs-extra'
import {latexParser} from 'latex-utensils'

import {Extension} from '../../main'
import {IProvider} from './interface'

export interface EnvItemEntry {
    name: string, // Name of the environment, what comes inside \begin{...}
    snippet?: string, // To be inserted after \begin{..}
    package?: string, // The package providing the environment
    detail?: string
}

export enum EnvSnippetType { AsName, AsCommand, ForBegin, }

export interface Suggestion extends vscode.CompletionItem {
    package: string
}

export class Environment implements IProvider {
    private readonly extension: Extension
    private defaultEnvsAsName: Suggestion[] = []
    private defaultEnvsAsCommand: Suggestion[] = []
    private defaultEnvsForBegin: Suggestion[] = []
    private packageEnvsAsName: {[pkg: string]: Suggestion[]} = {}
    private packageEnvsAsCommand: {[pkg: string]: Suggestion[]} = {}
    private packageEnvsForBegin: {[pkg: string]: Suggestion[]} = {}

    constructor(extension: Extension) {
        this.extension = extension
    }

    initialize(envs: {[key: string]: EnvItemEntry}) {
        this.defaultEnvsAsCommand = []
        this.defaultEnvsForBegin = []
        this.defaultEnvsAsName = []
        const endCompletion: Suggestion = {
            label: 'Complete with \\end',
            sortText: ' ',
            insertText: new vscode.SnippetString('$1}\n\t$0\n\\end{$1}'),
            command: { title: 'Post-Action', command: 'editor.action.triggerSuggest' },
            kind: vscode.CompletionItemKind.Module,
            package: ''
        }
        this.defaultEnvsForBegin.push(endCompletion)
        Object.keys(envs).forEach(key => {
           this.defaultEnvsAsCommand.push(this.entryEnvToCompletion(key, envs[key], EnvSnippetType.AsCommand))
           this.defaultEnvsForBegin.push(this.entryEnvToCompletion(key, envs[key], EnvSnippetType.ForBegin))
           this.defaultEnvsAsName.push(this.entryEnvToCompletion(key, envs[key], EnvSnippetType.AsName))
        })
    }

    getDefaultEnvs(type: EnvSnippetType): Suggestion[] {
        switch (type) {
            case EnvSnippetType.AsName:
                return this.defaultEnvsAsName
                break
            case EnvSnippetType.AsCommand:
                return this.defaultEnvsAsCommand
                break
            case EnvSnippetType.ForBegin:
                return this.defaultEnvsForBegin
                break
            default:
                return []
        }
    }

    private getPackageEnvs(type: EnvSnippetType): {[pkg: string]: Suggestion[]} {
        switch (type) {
            case EnvSnippetType.AsName:
                return this.packageEnvsAsName
                break
            case EnvSnippetType.AsCommand:
                return this.packageEnvsAsCommand
                break
            case EnvSnippetType.ForBegin:
                return this.packageEnvsForBegin
                break
            default:
                return {}
        }
    }

    provideFrom(_type: string, _result: RegExpMatchArray, args: {document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext}) {
        const payload = {document: args.document, position: args.position}
        return this.provide(payload)
    }

    private provide(args: {document: vscode.TextDocument, position: vscode.Position}): vscode.CompletionItem[] {
        if (vscode.window.activeTextEditor === undefined) {
            return []
        }
        let snippetType: EnvSnippetType = EnvSnippetType.ForBegin
        if (vscode.window.activeTextEditor.selections.length > 1) {
            snippetType = EnvSnippetType.AsName
        }

        // Extract cached envs and add to default ones
        const suggestions: vscode.CompletionItem[] = Array.from(this.getDefaultEnvs(snippetType))
        const envList: string[] = this.getDefaultEnvs(snippetType).map(env => env.label)

        // Insert package environments
        const configuration = vscode.workspace.getConfiguration('latex-workshop')
        if (configuration.get('intellisense.package.enabled')) {
            const extraPackages = this.extension.completer.command.getExtraPkgs(args.document.languageId)
            if (extraPackages) {
                extraPackages.forEach(pkg => {
                    this.getEnvFromPkg(pkg, snippetType).forEach(env => {
                        if (!envList.includes(env.label)) {
                            suggestions.push(env)
                            envList.push(env.label)
                        }
                    })
                })
            }
            this.extension.manager.getIncludedTeX().forEach(tex => {
                const pkgs = this.extension.manager.cachedContent[tex].element.package
                if (pkgs !== undefined) {
                    pkgs.forEach(pkg => {
                        this.getEnvFromPkg(pkg, snippetType).forEach(env => {
                            if (!envList.includes(env.label)) {
                                suggestions.push(env)
                                envList.push(env.label)
                            }
                        })
                    })
                }
            })
        }

        // Insert environments defined in tex
        this.extension.manager.getIncludedTeX().forEach(cachedFile => {
            const cachedEnvs = this.extension.manager.cachedContent[cachedFile].element.environment
            if (cachedEnvs !== undefined) {
                cachedEnvs.forEach(env => {
                    if (! envList.includes(env.label)) {
                        if (snippetType === EnvSnippetType.ForBegin) {
                            env.insertText = new vscode.SnippetString(`${env.label}}\n\t$0\n\\end{${env.label}}`)
                        } else {
                            env.insertText = env.label
                        }
                        suggestions.push(env)
                        envList.push(env.label)
                    }
                })
            }
        })

        if (snippetType === EnvSnippetType.ForBegin) {
            // If a closing '}' after '\begin{' has already been inserted, we need to remove it as it is already included in the snippets
            const line = args.document.lineAt(args.position).text
            const word = line.slice(line.lastIndexOf('\\', args.position.character), args.position.character + 2)
            if (word.match(/\\begin{[a-zA-Z]*}/)) {
                const rangeBegin = line.lastIndexOf('{', args.position.character) + 1
                const snippetRange = new vscode.Range(args.position.line, rangeBegin, args.position.line, args.position.character + 1)
                suggestions.forEach(c => {c.range = snippetRange} )
            } else {<commit_msg>Only handle double }} when autoClosingBrackets is on<commit_after>import * as vscode from 'vscode'
import * as fs from 'fs-extra'
import {latexParser} from 'latex-utensils'

import {Extension} from '../../main'
import {IProvider} from './interface'

export interface EnvItemEntry {
    name: string, // Name of the environment, what comes inside \begin{...}
    snippet?: string, // To be inserted after \begin{..}
    package?: string, // The package providing the environment
    detail?: string
}

export enum EnvSnippetType { AsName, AsCommand, ForBegin, }

export interface Suggestion extends vscode.CompletionItem {
    package: string
}

export class Environment implements IProvider {
    private readonly extension: Extension
    private defaultEnvsAsName: Suggestion[] = []
    private defaultEnvsAsCommand: Suggestion[] = []
    private defaultEnvsForBegin: Suggestion[] = []
    private packageEnvsAsName: {[pkg: string]: Suggestion[]} = {}
    private packageEnvsAsCommand: {[pkg: string]: Suggestion[]} = {}
    private packageEnvsForBegin: {[pkg: string]: Suggestion[]} = {}

    constructor(extension: Extension) {
        this.extension = extension
    }

    initialize(envs: {[key: string]: EnvItemEntry}) {
        this.defaultEnvsAsCommand = []
        this.defaultEnvsForBegin = []
        this.defaultEnvsAsName = []
        const endCompletion: Suggestion = {
            label: 'Complete with \\end',
            sortText: ' ',
            insertText: new vscode.SnippetString('$1}\n\t$0\n\\end{$1}'),
            command: { title: 'Post-Action', command: 'editor.action.triggerSuggest' },
            kind: vscode.CompletionItemKind.Module,
            package: ''
        }
        this.defaultEnvsForBegin.push(endCompletion)
        Object.keys(envs).forEach(key => {
           this.defaultEnvsAsCommand.push(this.entryEnvToCompletion(key, envs[key], EnvSnippetType.AsCommand))
           this.defaultEnvsForBegin.push(this.entryEnvToCompletion(key, envs[key], EnvSnippetType.ForBegin))
           this.defaultEnvsAsName.push(this.entryEnvToCompletion(key, envs[key], EnvSnippetType.AsName))
        })
    }

    getDefaultEnvs(type: EnvSnippetType): Suggestion[] {
        switch (type) {
            case EnvSnippetType.AsName:
                return this.defaultEnvsAsName
                break
            case EnvSnippetType.AsCommand:
                return this.defaultEnvsAsCommand
                break
            case EnvSnippetType.ForBegin:
                return this.defaultEnvsForBegin
                break
            default:
                return []
        }
    }

    private getPackageEnvs(type: EnvSnippetType): {[pkg: string]: Suggestion[]} {
        switch (type) {
            case EnvSnippetType.AsName:
                return this.packageEnvsAsName
                break
            case EnvSnippetType.AsCommand:
                return this.packageEnvsAsCommand
                break
            case EnvSnippetType.ForBegin:
                return this.packageEnvsForBegin
                break
            default:
                return {}
        }
    }

    provideFrom(_type: string, _result: RegExpMatchArray, args: {document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext}) {
        const payload = {document: args.document, position: args.position}
        return this.provide(payload)
    }

    private provide(args: {document: vscode.TextDocument, position: vscode.Position}): vscode.CompletionItem[] {
        if (vscode.window.activeTextEditor === undefined) {
            return []
        }
        let snippetType: EnvSnippetType = EnvSnippetType.ForBegin
        if (vscode.window.activeTextEditor.selections.length > 1) {
            snippetType = EnvSnippetType.AsName
        }

        // Extract cached envs and add to default ones
        const suggestions: vscode.CompletionItem[] = Array.from(this.getDefaultEnvs(snippetType))
        const envList: string[] = this.getDefaultEnvs(snippetType).map(env => env.label)

        // Insert package environments
        const configuration = vscode.workspace.getConfiguration('latex-workshop')
        if (configuration.get('intellisense.package.enabled')) {
            const extraPackages = this.extension.completer.command.getExtraPkgs(args.document.languageId)
            if (extraPackages) {
                extraPackages.forEach(pkg => {
                    this.getEnvFromPkg(pkg, snippetType).forEach(env => {
                        if (!envList.includes(env.label)) {
                            suggestions.push(env)
                            envList.push(env.label)
                        }
                    })
                })
            }
            this.extension.manager.getIncludedTeX().forEach(tex => {
                const pkgs = this.extension.manager.cachedContent[tex].element.package
                if (pkgs !== undefined) {
                    pkgs.forEach(pkg => {
                        this.getEnvFromPkg(pkg, snippetType).forEach(env => {
                            if (!envList.includes(env.label)) {
                                suggestions.push(env)
                                envList.push(env.label)
                            }
                        })
                    })
                }
            })
        }

        // Insert environments defined in tex
        this.extension.manager.getIncludedTeX().forEach(cachedFile => {
            const cachedEnvs = this.extension.manager.cachedContent[cachedFile].element.environment
            if (cachedEnvs !== undefined) {
                cachedEnvs.forEach(env => {
                    if (! envList.includes(env.label)) {
                        if (snippetType === EnvSnippetType.ForBegin) {
                            env.insertText = new vscode.SnippetString(`${env.label}}\n\t$0\n\\end{${env.label}}`)
                        } else {
                            env.insertText = env.label
                        }
                        suggestions.push(env)
                        envList.push(env.label)
                    }
                })
            }
        })

        if (snippetType === EnvSnippetType.ForBegin) {
            // If a closing '}' after '\begin{' has already been inserted, we need to remove it as it is already included in the snippets
            const autoClosing = vscode.workspace.getConfiguration('editor').get('autoClosingBrackets') as string
            const line = args.document.lineAt(args.position).text
            const word = line.slice(line.lastIndexOf('\\', args.position.character), args.position.character + 2)
            if (autoClosing !== 'never' && word.match(/\\begin{[a-zA-Z]*}/)) {
                const rangeBegin = line.lastIndexOf('{', args.position.character) + 1
                const snippetRange = new vscode.Range(args.position.line, rangeBegin, args.position.line, args.position.character + 1)
                suggestions.forEach(c => {c.range = snippetRange} )
            } else {
{'fraction_non_alphanumeric': 0.10521421929761819, 'fraction_numerical': 0.001144410271082183, 'mean_word_length': 1.9147383781530123, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 25, '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': '2963480', 'n_tokens_mistral': 3769, 'n_tokens_neox': 3640, 'n_words': 725}