<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

// @flow

import React, { Component } from "react";
import { bindActionCreators } from "redux";
import { connect } from "react-redux";
import actions from "../../actions";
import { getSelectedSource, getSymbols } from "../../selectors";
import Svg from "../shared/Svg";

import "./Outline.css";
import PreviewFunction from "../shared/PreviewFunction";
import { uniq } from "lodash";
import type {
  SymbolDeclarations,
  SymbolDeclaration
} from "../../workers/parser/getSymbols";
import type { AstLocation } from "../../workers/parser/types";
import type { SourceRecord } from "../../reducers/sources";

type Props = {
  symbols: SymbolDeclarations,
  selectLocation: ({ sourceId: string, line: number }) => void,
  selectedSource: ?SourceRecord
};

export class Outline extends Component<Props> {
  selectItem(location: AstLocation) {
    const { selectedSource, selectLocation } = this.props;
    if (!selectedSource) {
      return;
    }
    const selectedSourceId = selectedSource.get("id");
    const startLine = location.start.line;
    selectLocation({ sourceId: selectedSourceId, line: startLine });
  }

  renderPlaceholder() {
    return (
      <div className="outline-pane-info">
        {L10N.getStr("outline.noFunctions")}
      </div>
    );
  }

  renderFunction(func: SymbolDeclaration) {
    const { name, location, parameterNames } = func;

    return (
      <li
        key={`${name}:${location.start.line}:${location.start.column}`}
        className="outline-list__element"
        onClick={() => this.selectItem(location)}
      >
        <Svg name="function" />
        <PreviewFunction func={{ name, parameterNames }} />
      </li>
    );
  }

  renderClassFunctions(functions: SymbolDeclaration[]) {
    const classFunctions = functions.filter(
      func => func.name != "anonymous" && !!func.klass
    );

    if (classFunctions.length == 0) {
      return null;
    }

    const klass = classFunctions[0].klass;
    const klassFunc = functions.find(func => func.name === klass);

    return (
      <div className="outline-list__class">
        <h2>{klassFunc ? this.renderFunction(klassFunc) : klass}</h2>
        <ul className="outline-list__class-list">
          {classFunctions.map(func => this.renderFunction(func))}
        </ul>
      </div>
    );
  }

  renderFunctions(functions: Array<SymbolDeclaration>) {
    const classes = uniq(functions.map(func => func.klass));

    const namedFunctions = functions.filter(
      func =>
        func.name != "anonymous" && !func.klass && !classes.includes(func.name)
    );

    return (
      <ul className="outline-list">
        {namedFunctions.map(func => this.renderFunction(func))}
        {this.renderClassFunctions(functions)}<commit_msg>Group class functions within Outline Panel (#5290)<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

// @flow

import React, { Component } from "react";
import { bindActionCreators } from "redux";
import { connect } from "react-redux";
import actions from "../../actions";
import { getSelectedSource, getSymbols } from "../../selectors";
import Svg from "../shared/Svg";

import "./Outline.css";
import PreviewFunction from "../shared/PreviewFunction";
import { uniq } from "lodash";
import type {
  SymbolDeclarations,
  SymbolDeclaration
} from "../../workers/parser/getSymbols";
import type { AstLocation } from "../../workers/parser/types";
import type { SourceRecord } from "../../reducers/sources";

type Props = {
  symbols: SymbolDeclarations,
  selectLocation: ({ sourceId: string, line: number }) => void,
  selectedSource: ?SourceRecord
};

export class Outline extends Component<Props> {
  selectItem(location: AstLocation) {
    const { selectedSource, selectLocation } = this.props;
    if (!selectedSource) {
      return;
    }
    const selectedSourceId = selectedSource.get("id");
    const startLine = location.start.line;
    selectLocation({ sourceId: selectedSourceId, line: startLine });
  }

  renderPlaceholder() {
    return (
      <div className="outline-pane-info">
        {L10N.getStr("outline.noFunctions")}
      </div>
    );
  }

  renderFunction(func: SymbolDeclaration) {
    const { name, location, parameterNames } = func;

    return (
      <li
        key={`${name}:${location.start.line}:${location.start.column}`}
        className="outline-list__element"
        onClick={() => this.selectItem(location)}
      >
        <Svg name="function" />
        <PreviewFunction func={{ name, parameterNames }} />
      </li>
    );
  }

  renderClassFunctions(klass: string, functions: SymbolDeclaration[]) {
    if (klass == null || functions.length == 0) {
      return null;
    }

    const classFunc = functions.find(func => func.name === klass);
    const classFunctions = functions.filter(func => func.klass === klass);

    return (
      <div className="outline-list__class" key={klass}>
        <h2>{classFunc ? this.renderFunction(classFunc) : `class ${klass}`}</h2>
        <ul className="outline-list__class-list">
          {classFunctions.map(func => this.renderFunction(func))}
        </ul>
      </div>
    );
  }

  renderFunctions(functions: Array<SymbolDeclaration>) {
    const classes = uniq(functions.map(func => func.klass));
    const namedFunctions = functions.filter(
      func =>
        func.name != "anonymous" && !func.klass && !classes.includes(func.name)
    );

    const classFunctions = functions.filter(
      func => func.name != "anonymous" && !!func.klass
    );

    return (
      <ul className="outline-list">
        {namedFunctions.map(func => this.renderFunction(func))}
        {classes.map(klass => this.renderClassFunctions(klass, classFunctions))}
{'fraction_non_alphanumeric': 0.14797096667766413, 'fraction_numerical': 0.003794127350709337, 'mean_word_length': 3.207494795281055, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 53, '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': '116022', 'n_tokens_mistral': 1764, 'n_tokens_neox': 1765, 'n_words': 425}