All files / views/trees/definition-tree/nodes/concepts concepts-node.ts

96.15% Statements 25/26
92.85% Branches 13/14
100% Functions 10/10
96.15% Lines 25/26

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104                    1x       1x       7x                             98x             136x               16x   16x 7x     9x         11x   11x       98x   98x 31x   67x         1x         22x       22x 22x         22x 22x 22x   22x         22x   22x    
import * as vscode from "vscode";
import { DefinitionTreeNode } from "../../definition-tree-node";
import { ConceptClassNode } from "./concept-class-node";
 
/**
 * Node that lists the concepts of a concept scheme, or the concepts that are not displayed below
 * any concept scheme if the `inScheme` query option is `null`.
 */
export class ConceptsNode extends ConceptClassNode {
	override getContextValue(): string {
		return 'concepts';
	}
 
	override getIcon(): vscode.ThemeIcon | undefined {
		return undefined;
	}
 
	override getLabel(): vscode.TreeItemLabel {
		return { label: 'Concepts' };
	}
 
	/**
	 * The number of concepts below this node, computed once per node. Counting them traverses the
	 * whole concept scheme, and the provider recreates all nodes on refresh, so it is safe and
	 * worthwhile to memoize here — see the note on {@link DefinitionTreeNode.getLabel}.
	 */
	private _conceptCount: number | undefined;
 
	/**
	 * Get the URI of the concept scheme whose concepts are displayed below this node.
	 * @returns The URI of a concept scheme.
	 */
	private _getSchemeUri(): string {
		return this.getQueryOptions().inScheme ?? this.uri;
	}
 
	/**
	 * Indicates whether this node lists the concepts that are not displayed below any concept scheme.
	 */
	private _isUnscoped(): boolean {
		return this.getQueryOptions().inScheme === null;
	}
 
	/**
	 * Get the concepts that can be reached by expanding this node.
	 * @returns An iterator of concept URIs.
	 */
	private _getConcepts(): IterableIterator<string> {
		const graphs = this.getDocumentGraphs();
 
		if (this._isUnscoped()) {
			return this.vocabulary.getUnscopedConcepts(graphs, this.getQueryOptions());
		}
 
		return this.vocabulary.getAllConceptsInScheme(graphs, this._getSchemeUri());
	}
 
	override getDescription(): string {
		// Note: This counts exactly the set of concepts that can be reached by expanding this node.
		this._conceptCount ??= [...this._getConcepts()].length;
 
		return this._conceptCount.toString();
	}
 
	override *getSubClassIris(): IterableIterator<string> {
		const graphs = this.getDocumentGraphs();
 
		if (this._isUnscoped()) {
			yield* this.vocabulary.getUnscopedRootConcepts(graphs, this.getQueryOptions());
		} else {
			yield* this.vocabulary.getNarrowerConcepts(graphs, this._getSchemeUri(), this.getQueryOptions());
		}
	}
 
	override getTooltip(): vscode.MarkdownString | undefined {
		return undefined;
	}
 
	override resolveNodeForUri(iri: string): DefinitionTreeNode | undefined {
		// Note: Concepts below a concept scheme are not part of the unscoped concepts and vice versa.
		Iif (this._isUnscoped() && !new Set(this._getConcepts()).has(iri)) {
			return undefined;
		}
 
		const graphs = this.getDocumentGraphs();
		const path = this.vocabulary.getConceptSchemePath(graphs, iri) ?? [];
 
		// The path contains the broader concepts of the concept, ordered from the closest broader
		// concept to the scheme root, so it needs to be reversed. The scheme itself is dropped
		// because it is the ConceptSchemeNode parent, not a child here.
		const rootToTarget = [...path].reverse();
		const schemeUri = this._getSchemeUri();
		const schemeIndex = rootToTarget.indexOf(schemeUri);
 
		const walkPath = schemeIndex >= 0
			? rootToTarget.slice(schemeIndex + 1)
			: rootToTarget;
 
		// The path does not contain the concept itself, which is the node we are looking for.
		walkPath.push(iri);
 
		return this.walkHierarchyPath(walkPath);
	}
}