blob: 0ca929a976bb578ca47c2ff7d0ae80e5b4382261 [file] [log] [blame]
* @license
* Copyright (C) 2020 The Android Open Source Project
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* See the License for the specific language governing permissions and
* limitations under the License.
import {CommentRange} from '../../../types/common';
import {FILE, LineNumber} from './gr-diff-line';
import {Side} from '../../../constants/constants';
* Compare two ranges. Either argument may be falsy, but will only return
* true if both are falsy or if neither are falsy and have the same position
* values.
export function rangesEqual(a?: CommentRange, b?: CommentRange): boolean {
if (!a && !b) {
return true;
if (!a || !b) {
return false;
return (
a.start_line === b.start_line &&
a.start_character === b.start_character &&
a.end_line === b.end_line &&
a.end_character === b.end_character
export function isLongCommentRange(range: CommentRange): boolean {
return range.end_line - range.start_line > 10;
export function getLineNumber(lineEl?: Element | null): LineNumber | null {
if (!lineEl) return null;
const lineNumberStr = lineEl.getAttribute('data-value');
if (!lineNumberStr) return null;
if (lineNumberStr === FILE) return FILE;
if (lineNumberStr === 'LOST') return 'LOST';
const lineNumber = Number(lineNumberStr);
return Number.isInteger(lineNumber) ? lineNumber : null;
export function getLine(threadEl: HTMLElement): LineNumber {
const lineAtt = threadEl.getAttribute('line-num');
if (lineAtt === 'LOST') return lineAtt;
if (!lineAtt || lineAtt === 'FILE') return FILE;
const line = Number(lineAtt);
if (isNaN(line)) throw new Error(`cannot parse line number: ${lineAtt}`);
if (line < 1) throw new Error(`line number smaller than 1: ${line}`);
return line;
export function getSide(threadEl: HTMLElement): Side | undefined {
// TODO(dhruvsri): Remove check for comment-side once all users of gr-diff
// start setting diff-side
const sideAtt =
threadEl.getAttribute('diff-side') || threadEl.getAttribute('comment-side');
if (!sideAtt) {
console.warn('comment thread without side');
return undefined;
if (sideAtt !== Side.LEFT && sideAtt !== Side.RIGHT)
throw Error(`unexpected value for side: ${sideAtt}`);
return sideAtt as Side;
export function getRange(threadEl: HTMLElement): CommentRange | undefined {
const rangeAtt = threadEl.getAttribute('range');
if (!rangeAtt) return undefined;
const range = JSON.parse(rangeAtt) as CommentRange;
if (!range.start_line) throw new Error(`invalid range: ${rangeAtt}`);
return range;
// TODO: This type should be exposed to gr-diff clients in a separate type file.
// For Gerrit these are instances of GrCommentThread, but other gr-diff users
// have different HTML elements in use for comment threads.
// TODO: Also document the required HTML attributes that thread elements must
// have, e.g. 'diff-side', 'range', 'line-num', 'data-value'.
export interface GrDiffThreadElement extends HTMLElement {
rootId: string;
export function isThreadEl(node: Node): node is GrDiffThreadElement {
return (
node.nodeType === Node.ELEMENT_NODE &&
(node as Element).classList.contains('comment-thread')