import { findPositionsFromEvents } from '@sourcegraph/codeintellify';
import { isEqual } from 'lodash';
import * as React from 'react';
import { NEVER, Subject, Subscription } from 'rxjs';
import { filter } from 'rxjs/operators';
import { isDefined } from '../../util/types';
const DiffBoundary = props => (React.createElement("tr", { className: "diff-boundary" },
    props.lineNumbers && React.createElement("td", { className: `diff-boundary__num ${props.lineNumberClassName}`, colSpan: 2 }),
    React.createElement("td", { className: `diff-boundary__content ${props.contentClassName}` }, props.oldRange.lines !== undefined &&
        props.newRange.lines !== undefined && (React.createElement("code", null,
        "@@ -",
        props.oldRange.startLine,
        ",",
        props.oldRange.lines,
        " +",
        props.newRange.startLine,
        ",",
        props.newRange.lines,
        ' ',
        props.section && `@@ ${props.section}`)))));
const DiffHunk = ({ fileDiffAnchor, hunk, lineNumbers, location, history }) => {
    let oldLine = hunk.oldRange.startLine;
    let newLine = hunk.newRange.startLine;
    return (React.createElement(React.Fragment, null,
        React.createElement(DiffBoundary, Object.assign({}, hunk, { lineNumberClassName: "diff-hunk__num--both", contentClassName: "diff-hunk__content", lineNumbers: lineNumbers })),
        hunk.body
            .split('\n')
            .slice(0, -1)
            .map((line, i) => {
            if (line[0] !== '+') {
                oldLine++;
            }
            if (line[0] !== '-') {
                newLine++;
            }
            const oldAnchor = `${fileDiffAnchor}L${oldLine - 1}`;
            const newAnchor = `${fileDiffAnchor}R${newLine - 1}`;
            return (React.createElement("tr", { key: i, className: `diff-hunk__line ${line[0] === ' ' ? 'diff-hunk__line--both' : ''} ${line[0] === '-' ? 'diff-hunk__line--deletion' : ''} ${line[0] === '+' ? 'diff-hunk__line--addition' : ''} ${(line[0] !== '+' && location.hash === '#' + oldAnchor) ||
                    (line[0] !== '-' && location.hash === '#' + newAnchor)
                    ? 'diff-hunk__line--active'
                    : ''}` },
                lineNumbers && (React.createElement(React.Fragment, null,
                    line[0] !== '+' ? (React.createElement("td", { className: "diff-hunk__num", "data-line": oldLine - 1, "data-part": "base", id: oldAnchor, 
                        // tslint:disable-next-line:jsx-no-lambda need access to props
                        onClick: () => history.push({ hash: oldAnchor }) })) : (React.createElement("td", { className: "diff-hunk__num diff-hunk__num--empty" })),
                    line[0] !== '-' ? (React.createElement("td", { className: "diff-hunk__num", "data-line": newLine - 1, "data-part": "head", id: newAnchor, 
                        // tslint:disable-next-line:jsx-no-lambda need access to props
                        onClick: () => history.push({ hash: newAnchor }) })) : (React.createElement("td", { className: "diff-hunk__num diff-hunk__num--empty" })))),
                React.createElement("td", { className: "diff-hunk__content" }, line)));
        })));
};
const diffDomFunctions = {
    getCodeElementFromTarget: (target) => {
        const row = target.closest('tr');
        if (!row) {
            return null;
        }
        return row.cells[2];
    },
    getCodeElementFromLineNumber: (codeView, line, part) => {
        // For unchanged lines, prefer line number in head
        const lineNumberCell = codeView.querySelector(`[data-line="${line}"][data-part="${part || 'head'}"]`);
        if (!lineNumberCell) {
            return null;
        }
        const row = lineNumberCell.parentElement;
        const codeCell = row.cells[2];
        return codeCell;
    },
    getLineNumberFromCodeElement: (codeCell) => {
        const row = codeCell.closest('tr');
        if (!row) {
            throw new Error('Could not find closest row for codeCell');
        }
        const [baseLineNumberCell, headLineNumberCell] = row.cells;
        // For unchanged lines, prefer line number in head
        if (headLineNumberCell.dataset.line) {
            return +headLineNumberCell.dataset.line;
        }
        if (baseLineNumberCell.dataset.line) {
            return +baseLineNumberCell.dataset.line;
        }
        throw new Error('Neither head or base line number cell have data-line set');
    },
    getDiffCodePart: (codeCell) => {
        const row = codeCell.parentElement;
        const [baseLineNumberCell, headLineNumberCell] = row.cells;
        if (baseLineNumberCell.dataset.part && headLineNumberCell.dataset.part) {
            return null;
        }
        if (baseLineNumberCell.dataset.part) {
            return 'base';
        }
        if (headLineNumberCell.dataset.part) {
            return 'head';
        }
        throw new Error('Could not figure out diff part for code element');
    },
    isFirstCharacterDiffIndicator: (codeElement) => true,
};
/** Displays hunks in a unified file diff. */
export class FileDiffHunks extends React.Component {
    constructor(props) {
        super(props);
        /** Emits whenever the ref callback for the code element is called */
        this.codeElements = new Subject();
        this.nextCodeElement = (element) => this.codeElements.next(element);
        /** Emits whenever the ref callback for the blob element is called */
        this.blobElements = new Subject();
        this.nextBlobElement = (element) => this.blobElements.next(element);
        /** Emits whenever something is hovered in the code */
        this.codeMouseOvers = new Subject();
        this.nextCodeMouseOver = (event) => this.codeMouseOvers.next(event);
        /** Emits whenever something is hovered in the code */
        this.codeMouseMoves = new Subject();
        this.nextCodeMouseMove = (event) => this.codeMouseMoves.next(event);
        /** Emits whenever something is clicked in the code */
        this.codeClicks = new Subject();
        this.nextCodeClick = (event) => {
            event.persist();
            this.codeClicks.next(event);
        };
        /** Emits with the latest Props on every componentDidUpdate and on componentDidMount */
        this.componentUpdates = new Subject();
        /** Subscriptions to be disposed on unmout */
        this.subscriptions = new Subscription();
        this.state = {
            hoverOverlayIsFixed: false,
            clickedGoToDefinition: false,
            mouseIsMoving: false,
        };
        this.subscriptions.add(this.props.hoverifier.hoverify({
            dom: diffDomFunctions,
            positionEvents: this.codeElements.pipe(filter(isDefined), findPositionsFromEvents(diffDomFunctions)),
            positionJumps: NEVER,
            resolveContext: hoveredToken => {
                // if part is undefined, it doesn't matter whether we chose head or base, the line stayed the same
                const { repoPath, rev, filePath, commitID } = this.props[hoveredToken.part || 'head'];
                // If a hover or go-to-definition was invoked on this part, we know the file path must exist
                return { repoPath, filePath: filePath, rev, commitID };
            },
        }));
    }
    componentDidUpdate() {
        this.componentUpdates.next();
    }
    shouldComponentUpdate(nextProps, nextState) {
        return !isEqual(this.props, nextProps) || !isEqual(this.state, nextState);
    }
    componentWillUnmount() {
        this.subscriptions.unsubscribe();
    }
    render() {
        return (React.createElement("div", { className: `file-diff-hunks ${this.props.className}`, ref: this.nextBlobElement }, this.props.hunks.length === 0 ? (React.createElement("div", { className: "text-muted m-2" }, "No changes")) : (React.createElement("div", { className: "file-diff-hunks__container", ref: this.nextCodeElement, onMouseOver: this.nextCodeMouseOver, onMouseMove: this.nextCodeMouseMove, onClick: this.nextCodeClick },
            React.createElement("table", { className: "file-diff-hunks__table" },
                this.props.lineNumbers && (React.createElement("colgroup", null,
                    React.createElement("col", { width: "40" }),
                    React.createElement("col", { width: "40" }),
                    React.createElement("col", null))),
                React.createElement("tbody", null, this.props.hunks.map((hunk, i) => (React.createElement(DiffHunk, { key: i, hunk: hunk, fileDiffAnchor: this.props.fileDiffAnchor, lineNumbers: this.props.lineNumbers, location: this.props.location, history: this.props.history })))))))));
    }
}
//# sourceMappingURL=FileDiffHunks.js.map