<commit_before>import {
  ChangeDetectionStrategy,
  ChangeDetectorRef,
  Component,
  EventEmitter,
  HostListener,
  Input,
  OnInit,
  Output,
  ViewChild,
  OnDestroy,
} from '@angular/core';
import { DevToolsNode, ElementPosition, Events, MessageBus } from 'protocol';
import { FlatTreeControl } from '@angular/cdk/tree';
import { ComponentDataSource, FlatNode } from './component-data-source';
import { isChildOf, parentCollapsed } from './directive-forest-utils';
import { IndexedNode } from './index-forest';
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
import { TabUpdate } from '../../tab-update';
import { Subscription } from 'rxjs';

@Component({
  selector: 'ng-directive-forest',
  templateUrl: './directive-forest.component.html',
  styleUrls: ['./directive-forest.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DirectiveForestComponent implements OnInit, OnDestroy {
  @Input() set forest(forest: DevToolsNode[]) {
    const result = this._updateForest(forest);
    const changed = result.movedItems.length || result.newItems.length || result.removedItems.length;
    if (this.currentSelectedElement && changed) {
      this._reselectNodeOnUpdate();
    }
  }
  @Input() currentSelectedElement: IndexedNode;

  @Output() selectNode = new EventEmitter<IndexedNode | null>();
  @Output() selectDomElement = new EventEmitter<IndexedNode>();
  @Output() setParents = new EventEmitter<FlatNode[] | null>();
  @Output() highlightComponent = new EventEmitter<ElementPosition>();
  @Output() removeComponentHighlight = new EventEmitter<void>();
  @Output() toggleInspector = new EventEmitter<void>();

  @ViewChild(CdkVirtualScrollViewport) viewport: CdkVirtualScrollViewport;

  filterRegex = new RegExp('.^');
  currentlyMatchedIndex = -1;

  selectedNode: FlatNode | null = null;
  parents: FlatNode[];

  private _highlightIDinTreeFromElement: number | null = null;
  private _tabUpdateSubscription: Subscription;

  set highlightIDinTreeFromElement(id: number | null) {
    this._highlightIDinTreeFromElement = id;
    this._cdr.markForCheck();
  }

  readonly treeControl = new FlatTreeControl<FlatNode>(
    (node) => node.level,
    (node) => node.expandable
  );
  readonly dataSource = new ComponentDataSource(this.treeControl);
  readonly itemHeight = 21;

  private _initialized = false;

  constructor(
    private _tabUpdate: TabUpdate,
    private _messageBus: MessageBus<Events>,
    private _cdr: ChangeDetectorRef
  ) {}

  ngOnInit(): void {
    this.subscribeToInspectorEvents();
    this._tabUpdateSubscription = this._tabUpdate.tabUpdate$.subscribe(() => {
      if (this.viewport) {
        setTimeout(() => {
          this.viewport.scrollToIndex(0);
          this.viewport.checkViewportSize();
        });
      }
    });
  }

  ngOnDestroy(): void {
    if (this._tabUpdateSubscription) {
      this._tabUpdateSubscription.unsubscribe();
    }
  }

  subscribeToInspectorEvents(): void {
    this._messageBus.on('selectComponent', (id: number) => {
      this.selectNodeByComponentId(id);
      this.toggleInspector.emit();
    });

    this._messageBus.on('highlightComponent', (id: number) => {
      this.highlightIDinTreeFromElement = id;
    });

    this._messageBus.on('removeComponentHighlight', () => {
      this.highlightIDinTreeFromElement = null;
    });
  }

  selectNodeByComponentId(id: number): void {
    const foundNode = this.dataSource.data.find((node) => node.original.component?.id === id);
    if (foundNode) {
      this.handleSelect(foundNode);
    }
  }

  handleSelect(node: FlatNode): void {
    this.currentlyMatchedIndex = this.dataSource.data.findIndex((matchedNode) => matchedNode.id === node.id);
    this.selectAndEnsureVisible(node);
  }

  handleSelectDomElement(node: FlatNode): void {
    this.selectDomElement.emit(node.original);
  }

  selectAndEnsureVisible(node: FlatNode): void {
    this.select(node);

    const scrollParent = this.viewport.elementRef.nativeElement;
    // The top most point we see an element
    const top = scrollParent.scrollTop;
    // That's the bottom most point we currently see an element.
    const bottom = top + scrollParent.offsetHeight;
    const idx = this.dataSource.expandedDataValues.findIndex((el) => el.id === node.id);
    // The node might be hidden.
    if (idx < 0) {
      return;
    }
    const itemTop = idx * this.itemHeight;
    if (itemTop < top) {
      scrollParent.scrollTo({ top: itemTop });
    } else if (bottom < itemTop + this.itemHeight) {
      scrollParent.scrollTo({ top: itemTop - scrollParent.offsetHeight + this.itemHeight });
    }
  }<commit_msg>fix(devtools): set proper directive item and explorer height<commit_after>import {
  ChangeDetectionStrategy,
  ChangeDetectorRef,
  Component,
  EventEmitter,
  HostListener,
  Input,
  OnInit,
  Output,
  ViewChild,
  OnDestroy,
} from '@angular/core';
import { DevToolsNode, ElementPosition, Events, MessageBus } from 'protocol';
import { FlatTreeControl } from '@angular/cdk/tree';
import { ComponentDataSource, FlatNode } from './component-data-source';
import { isChildOf, parentCollapsed } from './directive-forest-utils';
import { IndexedNode } from './index-forest';
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
import { TabUpdate } from '../../tab-update';
import { Subscription } from 'rxjs';

@Component({
  selector: 'ng-directive-forest',
  templateUrl: './directive-forest.component.html',
  styleUrls: ['./directive-forest.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DirectiveForestComponent implements OnInit, OnDestroy {
  @Input() set forest(forest: DevToolsNode[]) {
    const result = this._updateForest(forest);
    const changed = result.movedItems.length || result.newItems.length || result.removedItems.length;
    if (this.currentSelectedElement && changed) {
      this._reselectNodeOnUpdate();
    }
  }
  @Input() currentSelectedElement: IndexedNode;

  @Output() selectNode = new EventEmitter<IndexedNode | null>();
  @Output() selectDomElement = new EventEmitter<IndexedNode>();
  @Output() setParents = new EventEmitter<FlatNode[] | null>();
  @Output() highlightComponent = new EventEmitter<ElementPosition>();
  @Output() removeComponentHighlight = new EventEmitter<void>();
  @Output() toggleInspector = new EventEmitter<void>();

  @ViewChild(CdkVirtualScrollViewport) viewport: CdkVirtualScrollViewport;

  filterRegex = new RegExp('.^');
  currentlyMatchedIndex = -1;

  selectedNode: FlatNode | null = null;
  parents: FlatNode[];

  private _highlightIDinTreeFromElement: number | null = null;
  private _tabUpdateSubscription: Subscription;

  set highlightIDinTreeFromElement(id: number | null) {
    this._highlightIDinTreeFromElement = id;
    this._cdr.markForCheck();
  }

  readonly treeControl = new FlatTreeControl<FlatNode>(
    (node) => node.level,
    (node) => node.expandable
  );
  readonly dataSource = new ComponentDataSource(this.treeControl);
  readonly itemHeight = 24;

  private _initialized = false;

  constructor(
    private _tabUpdate: TabUpdate,
    private _messageBus: MessageBus<Events>,
    private _cdr: ChangeDetectorRef
  ) {}

  ngOnInit(): void {
    this.subscribeToInspectorEvents();
    this._tabUpdateSubscription = this._tabUpdate.tabUpdate$.subscribe(() => {
      if (this.viewport) {
        setTimeout(() => {
          this.viewport.scrollToIndex(0);
          this.viewport.checkViewportSize();
        });
      }
    });
  }

  ngOnDestroy(): void {
    if (this._tabUpdateSubscription) {
      this._tabUpdateSubscription.unsubscribe();
    }
  }

  subscribeToInspectorEvents(): void {
    this._messageBus.on('selectComponent', (id: number) => {
      this.selectNodeByComponentId(id);
      this.toggleInspector.emit();
    });

    this._messageBus.on('highlightComponent', (id: number) => {
      this.highlightIDinTreeFromElement = id;
    });

    this._messageBus.on('removeComponentHighlight', () => {
      this.highlightIDinTreeFromElement = null;
    });
  }

  selectNodeByComponentId(id: number): void {
    const foundNode = this.dataSource.data.find((node) => node.original.component?.id === id);
    if (foundNode) {
      this.handleSelect(foundNode);
    }
  }

  handleSelect(node: FlatNode): void {
    this.currentlyMatchedIndex = this.dataSource.data.findIndex((matchedNode) => matchedNode.id === node.id);
    this.selectAndEnsureVisible(node);
  }

  handleSelectDomElement(node: FlatNode): void {
    this.selectDomElement.emit(node.original);
  }

  selectAndEnsureVisible(node: FlatNode): void {
    this.select(node);

    const scrollParent = this.viewport.elementRef.nativeElement;
    // The top most point we see an element
    const top = scrollParent.scrollTop;
    // That's the bottom most point we currently see an element.
    const parentHeight = scrollParent.offsetHeight;
    const bottom = top + parentHeight;
    const idx = this.dataSource.expandedDataValues.findIndex((el) => el.id === node.id);
    // The node might be hidden.
    if (idx < 0) {
      return;
    }
    const itemTop = idx * this.itemHeight;
    if (itemTop < top) {
      scrollParent.scrollTo({ top: itemTop });
    } else if (bottom < itemTop + this.itemHeight) {
      scrollParent.scrollTo({ top: itemTop - parentHeight + this.itemHeight });
    }
  }
{'fraction_non_alphanumeric': 0.12666595585883356, 'fraction_numerical': 0.0010662117496534813, 'mean_word_length': 3.9814126394052045, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 39, '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': '1379718', 'n_tokens_mistral': 2822, 'n_tokens_neox': 2762, 'n_words': 582}