<commit_before>import * as path from "path";
import { CancellationToken, DocumentSymbolProvider, Location, SymbolInformation, TextDocument, Uri, WorkspaceSymbolProvider, workspace } from "vscode";
import * as as from "../analysis/analysis_server_types";
import { Analyzer, getSymbolKindForElementKind } from "../analysis/analyzer";
import { fsPath, toRange } from "../utils";

export class DartSymbolProvider implements WorkspaceSymbolProvider, DocumentSymbolProvider {
	private analyzer: Analyzer;
	private badChars: RegExp = new RegExp("[^0-9a-z\-]", "gi");
	constructor(analyzer: Analyzer) {
		this.analyzer = analyzer;
	}

	public async provideWorkspaceSymbols(query: string, token: CancellationToken): Promise<SymbolInformation[]> {
		if (query.length === 0)
			return null;

		// Turn query into a case-insensitive fuzzy search.
		const pattern = ".*" + query.replace(this.badChars, "").split("").map((c) => `[${c.toUpperCase()}${c.toLowerCase()}]`).join(".*") + ".*";
		const results = await this.analyzer.searchGetElementDeclarations({ pattern, maxResults: 500 });

		return results.declarations.map((d) => this.convertResult(undefined, d, results.files[d.fileIndex], true));
	}

	public async provideDocumentSymbols(document: TextDocument, token: CancellationToken): Promise<SymbolInformation[]> {
		const results = await this.analyzer.searchGetElementDeclarations({ file: fsPath(document.uri) });
		return results.declarations.map((d) => this.convertResult(document, d, results.files[d.fileIndex], false));
	}

	// Needs testing when https://github.com/Microsoft/vscode/issues/51695 is fixed
	$$$
	public async resolveWorkspaceSymbol(symbol: SymbolInformation, token: CancellationToken): Promise<SymbolInformation> {
		if (!(symbol instanceof PartialSymbolInformation))
			return;

		const document = await workspace.openTextDocument(Uri.file(symbol.locationData.file));
		symbol.location = new Location(
			document.uri,
			toRange(document, symbol.locationData.offset, symbol.locationData.length),
		);
	}

	private convertResult(document: TextDocument | undefined, result: as.ElementDeclaration, file: string, includeFilename: boolean): SymbolInformation {
		let name = result.name;

		// Constructors don't come prefixed with class name, so add them for a nice display:
		//    () => MyClass()
		//    named() => MyClass.named()
		let nameIsPrefixedWithClass = false;
		if (result.kind === "CONSTRUCTOR" && result.className) {
			if (name) {
				nameIsPrefixedWithClass = true;
				name = `${result.className}.${name}`;
			} else {
				name = result.className;
			}
		}

		if (result.parameters && result.kind !== "SETTER")
			name += result.parameters;

		let containerName: string;
		if (includeFilename) {
			containerName = this.createDisplayPath(file);
			if (result.className && !nameIsPrefixedWithClass)
				name = `${result.className}.${name}`;
		} else {
			containerName = result.className;
		}

		const symbol: any = new PartialSymbolInformation(
			name,
			getSymbolKindForElementKind(result.kind),
			containerName,
			document
				? new Location(
					Uri.file(file),
					toRange(document, result.codeOffset, result.codeLength),
				)
				: undefined,
		);

		if (!document) {
			symbol.locationData = {
				file,
				length: result.codeLength,
				offset: result.codeOffset,
			};
		}

		return symbol;
	}

	private createDisplayPath(inputPath: string): string {
		// HACK: The AS returns paths to the PUB_CACHE folder, which Code can't
		// convert to relative paths (so they look terrible). If the file exists in
		// workspace.rootPath we rewrite the path to there which gives us a nice
		// relative path.
<commit_msg>Fix workspace symbol provide based on current resolve implementation<commit_after>import * as path from "path";
import { CancellationToken, DocumentSymbolProvider, Location, SymbolInformation, TextDocument, Uri, WorkspaceSymbolProvider, workspace } from "vscode";
import * as as from "../analysis/analysis_server_types";
import { Analyzer, getSymbolKindForElementKind } from "../analysis/analyzer";
import { fsPath, toRange } from "../utils";

export class DartSymbolProvider implements WorkspaceSymbolProvider, DocumentSymbolProvider {
	private analyzer: Analyzer;
	private badChars: RegExp = new RegExp("[^0-9a-z\-]", "gi");
	constructor(analyzer: Analyzer) {
		this.analyzer = analyzer;
	}

	public async provideWorkspaceSymbols(query: string, token: CancellationToken): Promise<SymbolInformation[]> {
		if (query.length === 0)
			return null;

		// Turn query into a case-insensitive fuzzy search.
		const pattern = ".*" + query.replace(this.badChars, "").split("").map((c) => `[${c.toUpperCase()}${c.toLowerCase()}]`).join(".*") + ".*";
		const results = await this.analyzer.searchGetElementDeclarations({ pattern, maxResults: 500 });

		return results.declarations.map((d) => this.convertResult(undefined, d, results.files[d.fileIndex], true));
	}

	public async provideDocumentSymbols(document: TextDocument, token: CancellationToken): Promise<SymbolInformation[]> {
		const results = await this.analyzer.searchGetElementDeclarations({ file: fsPath(document.uri) });
		return results.declarations.map((d) => this.convertResult(document, d, results.files[d.fileIndex], false));
	}

	public async resolveWorkspaceSymbol(symbol: SymbolInformation, token: CancellationToken): Promise<SymbolInformation> {
		if (!(symbol instanceof PartialSymbolInformation))
			return;

		const document = await workspace.openTextDocument(Uri.file(symbol.locationData.file));
		symbol.location = new Location(
			document.uri,
			toRange(document, symbol.locationData.offset, symbol.locationData.length),
		);
	}

	private convertResult(document: TextDocument | undefined, result: as.ElementDeclaration, file: string, includeFilename: boolean): SymbolInformation {
		let name = result.name;

		// Constructors don't come prefixed with class name, so add them for a nice display:
		//    () => MyClass()
		//    named() => MyClass.named()
		let nameIsPrefixedWithClass = false;
		if (result.kind === "CONSTRUCTOR" && result.className) {
			if (name) {
				nameIsPrefixedWithClass = true;
				name = `${result.className}.${name}`;
			} else {
				name = result.className;
			}
		}

		if (result.parameters && result.kind !== "SETTER")
			name += result.parameters;

		let containerName: string;
		if (includeFilename) {
			containerName = this.createDisplayPath(file);
			if (result.className && !nameIsPrefixedWithClass)
				name = `${result.className}.${name}`;
		} else {
			containerName = result.className;
		}

		const symbol: any = new PartialSymbolInformation(
			name,
			getSymbolKindForElementKind(result.kind),
			containerName,
			new Location(
				Uri.file(file),
				document ? toRange(document, result.codeOffset, result.codeLength) : undefined,
			),
		);

		if (!document) {
			symbol.locationData = {
				file,
				length: result.codeLength,
				offset: result.codeOffset,
			};
		}

		return symbol;
	}

	private createDisplayPath(inputPath: string): string {
		// HACK: The AS returns paths to the PUB_CACHE folder, which Code can't
		// convert to relative paths (so they look terrible). If the file exists in
		// workspace.rootPath we rewrite the path to there which gives us a nice
		// relative path.

{'fraction_non_alphanumeric': 0.12789096916299558, 'fraction_numerical': 0.002340308370044053, 'mean_word_length': 4.5415713196033565, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 19, 'https://': 1, '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': '5276998', 'n_tokens_mistral': 2316, 'n_tokens_neox': 2079, 'n_words': 546}