<commit_before>"use strict";

import * as vs from "vscode";
import { Analyzer } from "../analysis/analyzer";
import * as as from "../analysis/analysis_server_types";
import { isAnalyzable } from "../utils";

export class ClosingLabelsDecorations implements vs.Disposable {
	private analyzer: Analyzer;
	private subscriptions: vs.Disposable[] = [];
	private trackingFile: string;
	private activeEditor: vs.TextEditor;
	private closingLabels: as.ClosingLabel[];
	private updateTimeout: NodeJS.Timer;

	private readonly decorationType = vs.window.createTextEditorDecorationType({
		after: {
			margin: "2px",
			// TODO: Pick a good base, but have a new colour for theming?
			color: new vs.ThemeColor("tab.inactiveForeground"),
		},
		rangeBehavior: vs.DecorationRangeBehavior.ClosedClosed
	});

	constructor(analyzer: Analyzer) {
		this.analyzer = analyzer;

		this.subscriptions.push(vs.window.onDidChangeActiveTextEditor(e => this.setTrackingFile(e)));
		if (vs.window.activeTextEditor)
			this.setTrackingFile(vs.window.activeTextEditor);

		this.subscriptions.push(this.analyzer.registerForAnalysisClosingLabels(n => {
			if (n.file == this.activeEditor.document.fileName) {
				this.closingLabels = n.labels;
				// Delay this so if we're getting lots of updates we don't flicker.
				clearTimeout(this.updateTimeout);
				this.updateTimeout = setTimeout(() => this.update(), 500);
			}
		}));
	}

	private update() {
		const decorations: { [key: number]: vs.DecorationOptions } = [];
		// Becuase syntax errors result in lots of labels ending on the same character, we'll
		// track any offsets that have been used and use them to remove the labels.
		const offsetUsed: { [key: number]: boolean } = [];

		this.closingLabels.forEach((r) => {
			const endOfLine = this.activeEditor.document.lineAt(this.activeEditor.document.positionAt(r.offset + r.length)).range.end;

			// If this offset already had a label, this is likely an error and we should discount both.
			if (offsetUsed[r.offset + r.length]) {
				delete decorations[endOfLine.line];
				return;
			}
			else
				offsetUsed[r.offset + r.length] = true;

			const existingDecorationForLine = decorations[endOfLine.line];
			if (existingDecorationForLine) {
				existingDecorationForLine.renderOptions.after.contentText = " // " + r.label + " " + existingDecorationForLine.renderOptions.after.contentText;
			} else {<commit_msg>Don't update labels if the editor has changed during the timeout.<commit_after>"use strict";

import * as vs from "vscode";
import { Analyzer } from "../analysis/analyzer";
import * as as from "../analysis/analysis_server_types";
import { isAnalyzable } from "../utils";

export class ClosingLabelsDecorations implements vs.Disposable {
	private analyzer: Analyzer;
	private subscriptions: vs.Disposable[] = [];
	private trackingFile: string;
	private activeEditor: vs.TextEditor;
	private closingLabels: as.AnalysisClosingLabelsNotification;
	private updateTimeout: NodeJS.Timer;

	private readonly decorationType = vs.window.createTextEditorDecorationType({
		after: {
			margin: "2px",
			// TODO: Pick a good base, but have a new colour for theming?
			color: new vs.ThemeColor("tab.inactiveForeground"),
		},
		rangeBehavior: vs.DecorationRangeBehavior.ClosedClosed
	});

	constructor(analyzer: Analyzer) {
		this.analyzer = analyzer;

		this.subscriptions.push(vs.window.onDidChangeActiveTextEditor(e => this.setTrackingFile(e)));
		if (vs.window.activeTextEditor)
			this.setTrackingFile(vs.window.activeTextEditor);

		this.subscriptions.push(this.analyzer.registerForAnalysisClosingLabels(n => {
			if (n.file == this.activeEditor.document.fileName) {
				this.closingLabels = n;
				// Delay this so if we're getting lots of updates we don't flicker.
				clearTimeout(this.updateTimeout);
				this.updateTimeout = setTimeout(() => this.update(), 500);
			}
		}));
	}

	private update() {
		if (this.closingLabels.file != this.activeEditor.document.fileName)
			return;

		const decorations: { [key: number]: vs.DecorationOptions } = [];
		// Becuase syntax errors result in lots of labels ending on the same character, we'll
		// track any offsets that have been used and use them to remove the labels.
		const offsetUsed: { [key: number]: boolean } = [];

		this.closingLabels.labels.forEach((r) => {
			const endOfLine = this.activeEditor.document.lineAt(this.activeEditor.document.positionAt(r.offset + r.length)).range.end;

			// If this offset already had a label, this is likely an error and we should discount both.
			if (offsetUsed[r.offset + r.length]) {
				delete decorations[endOfLine.line];
				return;
			}
			else
				offsetUsed[r.offset + r.length] = true;

			const existingDecorationForLine = decorations[endOfLine.line];
			if (existingDecorationForLine) {
				existingDecorationForLine.renderOptions.after.contentText = " // " + r.label + " " + existingDecorationForLine.renderOptions.after.contentText;
			} else {
{'fraction_non_alphanumeric': 0.11698036834648856, 'fraction_numerical': 0.0016191054442420564, 'mean_word_length': 4.336933045356371, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 11, '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': '125075', 'n_tokens_mistral': 1634, 'n_tokens_neox': 1441, 'n_words': 374}