<commit_before>import * as vscode from 'vscode';
import { Plugin } from './plugin';

const ENABLED = false;

export interface ComboMeterConfig {
    enableEditorComboCounter?: boolean;
}

export class ComboMeter implements Plugin {

    private config: ComboMeterConfig = {};
    private comboTitle: vscode.TextEditorDecorationType;
    private comboCount: vscode.TextEditorDecorationType;

    private combo: number = 0;
    // TODO: Currently unused. Use this to style the combo
    private powermode: boolean = false;

    private static readonly DEFAULT_CSS = ComboMeter.objectToCssString({
        position: 'absolute',
        right: "5%",
        // width: "50px",
        ['z-index']: 1,
        ['pointer-events']: 'none',
        ["text-align"]: "right",
    });

    constructor() {
        this.activate();
    }

    public activate = () => {
        vscode.window.onDidChangeTextEditorVisibleRanges((e: vscode.TextEditorVisibleRangesChangeEvent) => {
            this.updateDecorations(e.textEditor);
        });
    }

    dispose = () => {
        if (this.comboCount) {
            this.comboCount.dispose();
            this.comboCount = null;
        }

        if (this.comboTitle) {
            this.comboTitle.dispose();
            this.comboTitle = null;
        }
    }

    public onPowermodeStart = (combo: number) => {
        this.powermode = true;
    }

    public onPowermodeStop = (combo: number) => {
        this.powermode = false;
    }

    public onComboStart = (combo: number) => {
        this.combo = combo;
        this.updateDecorations();
    }

    public onComboStop = (combo: number) => {
        this.combo = combo;
        this.updateDecorations();
    }

    public onDidChangeTextDocument = (combo: number, powermode: boolean, event: vscode.TextDocumentChangeEvent) => {
        this.combo = combo;
        this.powermode = powermode;
        this.updateDecorations();
    }

    public onDidChangeConfiguration = (config: vscode.WorkspaceConfiguration) => {
        this.config.enableEditorComboCounter = config.get<boolean>('enableStatusBarComboCounter', true);
        if (this.config.enableEditorComboCounter) {
            this.activate();
        } else {
            this.dispose();
        }
    }

    private updateDecorations = (editor: vscode.TextEditor = vscode.window.activeTextEditor) => {
        this.dispose();

        this.createComboCountDecoration(this.combo);
        this.createComboTitleDecoration();

        const ranges = [editor.visibleRanges.sort()[0]];
        editor.setDecorations(this.comboTitle, ranges);
        editor.setDecorations(this.comboCount, ranges);
    }

    private createComboTitleDecoration() {
        const titleCss = ComboMeter.objectToCssString({
            ["font-size"]: "2em"
        });

        this.comboTitle = vscode.window.createTextEditorDecorationType({
            before: {
                contentText: "Combo:",
                color: "white",
                textDecoration: `none; ${ComboMeter.DEFAULT_CSS} ${titleCss}`,
            },
            rangeBehavior: vscode.DecorationRangeBehavior.ClosedClosed,
        });
    }

    private createComboCountDecoration(count: number) {
        const countCss = ComboMeter.objectToCssString({
            ["font-size"]: "4em"
        });

        this.comboCount = vscode.window.createTextEditorDecorationType({
            before: {
                margin: ".6em 0 0 0",
                contentText: count.toString(),
                color: "#9cdcfe",
                textDecoration: `none; ${ComboMeter.DEFAULT_CSS} ${countCss}`,
            },
            rangeBehavior: vscode.DecorationRangeBehavior.ClosedClosed,
        });
    }

    private static objectToCssString(settings: any): string {
        let value = '';
        const cssString = Object.keys(settings).map(setting => {
            value = settings[setting];
            if (typeof value === 'string' || typeof value === 'number') {
                return `${setting}: ${value};`
            }
        }).join(' ');

        return cssString;
    }
}<commit_msg>Use different pseudoelements so combo and title both work when the first line has no content<commit_after>import * as vscode from 'vscode';
import { Plugin } from './plugin';

const ENABLED = false;

export interface ComboMeterConfig {
    enableEditorComboCounter?: boolean;
}

export class ComboMeter implements Plugin {

    private config: ComboMeterConfig = {};
    private comboTitle: vscode.TextEditorDecorationType;
    private comboCount: vscode.TextEditorDecorationType;

    private combo: number = 0;
    // TODO: Currently unused. Use this to style the combo
    private powermode: boolean = false;

    private static readonly DEFAULT_CSS = ComboMeter.objectToCssString({
        position: 'absolute',
        right: "5%",
        // width: "50px",
        ['z-index']: 1,
        ['pointer-events']: 'none',
        ["text-align"]: "right",
    });

    constructor() {
        this.activate();
    }

    public activate = () => {
        vscode.window.onDidChangeTextEditorVisibleRanges((e: vscode.TextEditorVisibleRangesChangeEvent) => {
            this.updateDecorations(e.textEditor);
        });
    }

    dispose = () => {
        if (this.comboCount) {
            this.comboCount.dispose();
            this.comboCount = null;
        }

        if (this.comboTitle) {
            this.comboTitle.dispose();
            this.comboTitle = null;
        }
    }

    public onPowermodeStart = (combo: number) => {
        this.powermode = true;
    }

    public onPowermodeStop = (combo: number) => {
        this.powermode = false;
    }

    public onComboStart = (combo: number) => {
        this.combo = combo;
        this.updateDecorations();
    }

    public onComboStop = (combo: number) => {
        this.combo = combo;
        this.updateDecorations();
    }

    public onDidChangeTextDocument = (combo: number, powermode: boolean, event: vscode.TextDocumentChangeEvent) => {
        this.combo = combo;
        this.powermode = powermode;
        this.updateDecorations();
    }

    public onDidChangeConfiguration = (config: vscode.WorkspaceConfiguration) => {
        this.config.enableEditorComboCounter = config.get<boolean>('enableStatusBarComboCounter', true);
        if (this.config.enableEditorComboCounter) {
            this.activate();
        } else {
            this.dispose();
        }
    }

    private updateDecorations = (editor: vscode.TextEditor = vscode.window.activeTextEditor) => {
        this.dispose();

        const firstVisibleRange = editor.visibleRanges.sort()[0];
        if (!firstVisibleRange) {
            return;
        }

        this.createComboCountDecoration(this.combo);
        this.createComboTitleDecoration();

        const position = firstVisibleRange.start;
        const ranges = [new vscode.Range(position, position)];
        editor.setDecorations(this.comboTitle, ranges);
        editor.setDecorations(this.comboCount, ranges);
    }

    private createComboTitleDecoration() {
        const titleCss = ComboMeter.objectToCssString({
            ["font-size"]: "2em"
        });

        this.comboTitle = vscode.window.createTextEditorDecorationType({
            // Title and Count cannot use the same pseudoelement
            before: {
                contentText: "Combo:",
                color: "white",
                textDecoration: `none; ${ComboMeter.DEFAULT_CSS} ${titleCss}`,
            },
            rangeBehavior: vscode.DecorationRangeBehavior.ClosedClosed,
        });
    }

    private createComboCountDecoration(count: number) {
        const countCss = ComboMeter.objectToCssString({
            ["font-size"]: "4em"
        });

        this.comboCount = vscode.window.createTextEditorDecorationType({
            // Title and Count cannot use the same pseudoelement
            after: {
                margin: ".6em 0 0 0",
                contentText: count.toString(),
                color: "#9cdcfe",
                textDecoration: `none; ${ComboMeter.DEFAULT_CSS} ${countCss}`,
            },
            rangeBehavior: vscode.DecorationRangeBehavior.ClosedClosed,
        });
    }

    private static objectToCssString(settings: any): string {
        let value = '';
        const cssString = Object.keys(settings).map(setting => {
            value = settings[setting];
            if (typeof value === 'string' || typeof value === 'number') {
                return `${setting}: ${value};`
            }
        }).join(' ');

        return cssString;
    }
}
{'fraction_non_alphanumeric': 0.11369014084507043, 'fraction_numerical': 0.0029295774647887323, 'mean_word_length': 2.0211027910142954, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 27, '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': '2085256', 'n_tokens_mistral': 2599, 'n_tokens_neox': 2453, 'n_words': 484}