debian-mirror-gitlab/app/assets/javascripts/diffs/store/utils.js

509 lines
15 KiB
JavaScript
Raw Normal View History

2018-11-08 19:23:39 +05:30
import _ from 'underscore';
2019-03-02 22:35:43 +05:30
import { truncatePathMiddleToLength } from '~/lib/utils/text_utility';
2019-07-07 11:18:12 +05:30
import { diffModes, diffViewerModes } from '~/ide/constants';
2018-11-08 19:23:39 +05:30
import {
LINE_POSITION_LEFT,
LINE_POSITION_RIGHT,
TEXT_DIFF_POSITION_TYPE,
2018-12-05 23:21:45 +05:30
LEGACY_DIFF_NOTE_TYPE,
2018-11-08 19:23:39 +05:30
DIFF_NOTE_TYPE,
NEW_LINE_TYPE,
OLD_LINE_TYPE,
MATCH_LINE_TYPE,
2018-11-20 20:47:30 +05:30
LINES_TO_BE_RENDERED_DIRECTLY,
MAX_LINES_TO_BE_RENDERED,
2019-03-02 22:35:43 +05:30
TREE_TYPE,
2018-11-08 19:23:39 +05:30
} from '../constants';
2019-07-07 11:18:12 +05:30
export function findDiffFile(files, match, matchKey = 'file_hash') {
return files.find(file => file[matchKey] === match);
2018-11-08 19:23:39 +05:30
}
export const getReversePosition = linePosition => {
if (linePosition === LINE_POSITION_RIGHT) {
return LINE_POSITION_LEFT;
}
return LINE_POSITION_RIGHT;
};
2018-12-05 23:21:45 +05:30
export function getFormData(params) {
2018-11-08 19:23:39 +05:30
const {
2019-02-15 15:39:39 +05:30
commit,
2018-11-08 19:23:39 +05:30
note,
noteableType,
noteableData,
diffFile,
noteTargetLine,
diffViewType,
linePosition,
2018-12-13 13:39:08 +05:30
positionType,
2018-11-08 19:23:39 +05:30
} = params;
const position = JSON.stringify({
2019-02-15 15:39:39 +05:30
base_sha: diffFile.diff_refs.base_sha,
start_sha: diffFile.diff_refs.start_sha,
head_sha: diffFile.diff_refs.head_sha,
old_path: diffFile.old_path,
new_path: diffFile.new_path,
2018-12-13 13:39:08 +05:30
position_type: positionType || TEXT_DIFF_POSITION_TYPE,
2019-02-15 15:39:39 +05:30
old_line: noteTargetLine ? noteTargetLine.old_line : null,
new_line: noteTargetLine ? noteTargetLine.new_line : null,
2018-12-13 13:39:08 +05:30
x: params.x,
y: params.y,
width: params.width,
height: params.height,
2018-11-08 19:23:39 +05:30
});
const postData = {
view: diffViewType,
line_type: linePosition === LINE_POSITION_RIGHT ? NEW_LINE_TYPE : OLD_LINE_TYPE,
2019-02-15 15:39:39 +05:30
merge_request_diff_head_sha: diffFile.diff_refs.head_sha,
2018-11-08 19:23:39 +05:30
in_reply_to_discussion_id: '',
note_project_id: '',
target_type: noteableData.targetType,
target_id: noteableData.id,
2018-12-05 23:21:45 +05:30
return_discussion: true,
2018-11-08 19:23:39 +05:30
note: {
note,
position,
noteable_type: noteableType,
noteable_id: noteableData.id,
2019-02-15 15:39:39 +05:30
commit_id: commit && commit.id,
2018-12-05 23:21:45 +05:30
type:
2019-02-15 15:39:39 +05:30
diffFile.diff_refs.start_sha && diffFile.diff_refs.head_sha
2018-12-05 23:21:45 +05:30
? DIFF_NOTE_TYPE
: LEGACY_DIFF_NOTE_TYPE,
2019-02-15 15:39:39 +05:30
line_code: noteTargetLine ? noteTargetLine.line_code : null,
2018-11-08 19:23:39 +05:30
},
};
2018-12-05 23:21:45 +05:30
return postData;
}
export function getNoteFormData(params) {
const data = getFormData(params);
2018-11-08 19:23:39 +05:30
return {
2018-12-05 23:21:45 +05:30
endpoint: params.noteableData.create_note_path,
data,
2018-11-08 19:23:39 +05:30
};
}
export const findIndexInInlineLines = (lines, lineNumbers) => {
const { oldLineNumber, newLineNumber } = lineNumbers;
return _.findIndex(
lines,
2019-02-15 15:39:39 +05:30
line => line.old_line === oldLineNumber && line.new_line === newLineNumber,
2018-11-08 19:23:39 +05:30
);
};
export const findIndexInParallelLines = (lines, lineNumbers) => {
const { oldLineNumber, newLineNumber } = lineNumbers;
return _.findIndex(
lines,
line =>
line.left &&
line.right &&
2019-02-15 15:39:39 +05:30
line.left.old_line === oldLineNumber &&
line.right.new_line === newLineNumber,
2018-11-08 19:23:39 +05:30
);
};
export function removeMatchLine(diffFile, lineNumbers, bottom) {
2019-02-15 15:39:39 +05:30
const indexForInline = findIndexInInlineLines(diffFile.highlighted_diff_lines, lineNumbers);
const indexForParallel = findIndexInParallelLines(diffFile.parallel_diff_lines, lineNumbers);
2018-11-08 19:23:39 +05:30
const factor = bottom ? 1 : -1;
2019-02-15 15:39:39 +05:30
diffFile.highlighted_diff_lines.splice(indexForInline + factor, 1);
diffFile.parallel_diff_lines.splice(indexForParallel + factor, 1);
2018-11-08 19:23:39 +05:30
}
2019-10-12 21:52:04 +05:30
export function addLineReferences(lines, lineNumbers, bottom, isExpandDown, nextLineNumbers) {
2018-11-08 19:23:39 +05:30
const { oldLineNumber, newLineNumber } = lineNumbers;
const lineCount = lines.length;
let matchLineIndex = -1;
const linesWithNumbers = lines.map((l, index) => {
2019-02-15 15:39:39 +05:30
if (l.type === MATCH_LINE_TYPE) {
2018-11-08 19:23:39 +05:30
matchLineIndex = index;
} else {
2019-02-15 15:39:39 +05:30
Object.assign(l, {
old_line: bottom ? oldLineNumber + index + 1 : oldLineNumber + index - lineCount,
new_line: bottom ? newLineNumber + index + 1 : newLineNumber + index - lineCount,
2018-11-08 19:23:39 +05:30
});
}
2019-02-15 15:39:39 +05:30
return l;
2018-11-08 19:23:39 +05:30
});
if (matchLineIndex > -1) {
const line = linesWithNumbers[matchLineIndex];
2019-10-12 21:52:04 +05:30
let targetLine;
if (isExpandDown) {
targetLine = nextLineNumbers;
} else if (bottom) {
targetLine = linesWithNumbers[matchLineIndex - 1];
} else {
targetLine = linesWithNumbers[matchLineIndex + 1];
}
2018-11-08 19:23:39 +05:30
Object.assign(line, {
2019-02-15 15:39:39 +05:30
meta_data: {
old_pos: targetLine.old_line,
new_pos: targetLine.new_line,
2018-11-08 19:23:39 +05:30
},
});
}
return linesWithNumbers;
}
export function addContextLines(options) {
2019-10-12 21:52:04 +05:30
const { inlineLines, parallelLines, contextLines, lineNumbers, isExpandDown } = options;
2018-11-08 19:23:39 +05:30
const normalizedParallelLines = contextLines.map(line => ({
left: line,
right: line,
2019-07-07 11:18:12 +05:30
line_code: line.line_code,
2018-11-08 19:23:39 +05:30
}));
2019-10-12 21:52:04 +05:30
const factor = isExpandDown ? 1 : 0;
2018-11-08 19:23:39 +05:30
2019-10-12 21:52:04 +05:30
if (!isExpandDown && options.bottom) {
2018-11-08 19:23:39 +05:30
inlineLines.push(...contextLines);
parallelLines.push(...normalizedParallelLines);
} else {
const inlineIndex = findIndexInInlineLines(inlineLines, lineNumbers);
const parallelIndex = findIndexInParallelLines(parallelLines, lineNumbers);
2019-10-12 21:52:04 +05:30
inlineLines.splice(inlineIndex + factor, 0, ...contextLines);
parallelLines.splice(parallelIndex + factor, 0, ...normalizedParallelLines);
2018-11-08 19:23:39 +05:30
}
}
/**
* Trims the first char of the `richText` property when it's either a space or a diff symbol.
* @param {Object} line
* @returns {Object}
*/
export function trimFirstCharOfLineContent(line = {}) {
2018-11-20 20:47:30 +05:30
// eslint-disable-next-line no-param-reassign
delete line.text;
2018-11-08 19:23:39 +05:30
const parsedLine = Object.assign({}, line);
2019-02-15 15:39:39 +05:30
if (line.rich_text) {
const firstChar = parsedLine.rich_text.charAt(0);
2018-11-08 19:23:39 +05:30
if (firstChar === ' ' || firstChar === '+' || firstChar === '-') {
2019-02-15 15:39:39 +05:30
parsedLine.rich_text = line.rich_text.substring(1);
2018-11-08 19:23:39 +05:30
}
}
return parsedLine;
}
2018-11-18 11:00:15 +05:30
2019-02-15 15:39:39 +05:30
function getLineCode({ left, right }, index) {
if (left && left.line_code) {
return left.line_code;
} else if (right && right.line_code) {
return right.line_code;
}
return index;
}
2018-11-20 20:47:30 +05:30
// This prepares and optimizes the incoming diff data from the server
// by setting up incremental rendering and removing unneeded data
export function prepareDiffData(diffData) {
2019-02-15 15:39:39 +05:30
const filesLength = diffData.diff_files.length;
2018-11-20 20:47:30 +05:30
let showingLines = 0;
for (let i = 0; i < filesLength; i += 1) {
2019-02-15 15:39:39 +05:30
const file = diffData.diff_files[i];
2018-11-20 20:47:30 +05:30
2019-02-15 15:39:39 +05:30
if (file.parallel_diff_lines) {
const linesLength = file.parallel_diff_lines.length;
2018-11-20 20:47:30 +05:30
for (let u = 0; u < linesLength; u += 1) {
2019-02-15 15:39:39 +05:30
const line = file.parallel_diff_lines[u];
line.line_code = getLineCode(line, u);
2018-11-20 20:47:30 +05:30
if (line.left) {
line.left = trimFirstCharOfLineContent(line.left);
2019-03-02 22:35:43 +05:30
line.left.discussions = [];
2019-02-15 15:39:39 +05:30
line.left.hasForm = false;
2018-11-20 20:47:30 +05:30
}
if (line.right) {
line.right = trimFirstCharOfLineContent(line.right);
2019-03-02 22:35:43 +05:30
line.right.discussions = [];
2019-02-15 15:39:39 +05:30
line.right.hasForm = false;
2018-11-20 20:47:30 +05:30
}
}
}
2019-02-15 15:39:39 +05:30
if (file.highlighted_diff_lines) {
const linesLength = file.highlighted_diff_lines.length;
2018-11-20 20:47:30 +05:30
for (let u = 0; u < linesLength; u += 1) {
2019-02-15 15:39:39 +05:30
const line = file.highlighted_diff_lines[u];
2019-03-02 22:35:43 +05:30
Object.assign(line, {
...trimFirstCharOfLineContent(line),
discussions: [],
hasForm: false,
});
2018-11-20 20:47:30 +05:30
}
2019-02-15 15:39:39 +05:30
showingLines += file.parallel_diff_lines.length;
2018-11-20 20:47:30 +05:30
}
2020-01-01 13:55:28 +05:30
const name = (file.viewer && file.viewer.name) || diffViewerModes.text;
2018-11-20 20:47:30 +05:30
Object.assign(file, {
renderIt: showingLines < LINES_TO_BE_RENDERED_DIRECTLY,
2020-01-01 13:55:28 +05:30
collapsed: name === diffViewerModes.text && showingLines > MAX_LINES_TO_BE_RENDERED,
2019-07-07 11:18:12 +05:30
isShowingFullFile: false,
isLoadingFullFile: false,
2018-12-13 13:39:08 +05:30
discussions: [],
2019-07-31 22:56:46 +05:30
renderingLines: false,
2018-11-20 20:47:30 +05:30
});
}
}
export function getDiffPositionByLineCode(diffFiles) {
2018-11-18 11:00:15 +05:30
return diffFiles.reduce((acc, diffFile) => {
// We can only use highlightedDiffLines to create the map of diff lines because
// highlightedDiffLines will also include every parallel diff line in it.
2019-02-15 15:39:39 +05:30
if (diffFile.highlighted_diff_lines) {
diffFile.highlighted_diff_lines.forEach(line => {
if (line.line_code) {
acc[line.line_code] = {
base_sha: diffFile.diff_refs.base_sha,
head_sha: diffFile.diff_refs.head_sha,
start_sha: diffFile.diff_refs.start_sha,
new_path: diffFile.new_path,
old_path: diffFile.old_path,
old_line: line.old_line,
new_line: line.new_line,
line_code: line.line_code,
position_type: 'text',
2018-12-05 23:21:45 +05:30
};
2018-11-18 11:00:15 +05:30
}
});
}
return acc;
}, {});
}
2018-11-20 20:47:30 +05:30
// This method will check whether the discussion is still applicable
// to the diff line in question regarding different versions of the MR
2018-12-05 23:21:45 +05:30
export function isDiscussionApplicableToLine({ discussion, diffPosition, latestDiff }) {
2019-02-15 15:39:39 +05:30
const { line_code, ...diffPositionCopy } = diffPosition;
2018-12-05 23:21:45 +05:30
if (discussion.original_position && discussion.position) {
2019-02-15 15:39:39 +05:30
const originalRefs = discussion.original_position;
const refs = discussion.position;
2018-12-05 23:21:45 +05:30
return _.isEqual(refs, diffPositionCopy) || _.isEqual(originalRefs, diffPositionCopy);
}
2018-11-20 20:47:30 +05:30
2019-02-15 15:39:39 +05:30
// eslint-disable-next-line
return latestDiff && discussion.active && line_code === discussion.line_code;
2018-11-20 20:47:30 +05:30
}
2018-12-05 23:21:45 +05:30
2019-03-02 22:35:43 +05:30
export const getLowestSingleFolder = folder => {
const getFolder = (blob, start = []) =>
blob.tree.reduce(
(acc, file) => {
const shouldGetFolder = file.tree.length === 1 && file.tree[0].type === TREE_TYPE;
const currentFileTypeTree = file.type === TREE_TYPE;
const path = shouldGetFolder || currentFileTypeTree ? acc.path.concat(file.name) : acc.path;
const tree = shouldGetFolder || currentFileTypeTree ? acc.tree.concat(file) : acc.tree;
if (shouldGetFolder) {
const firstFolder = getFolder(file);
path.push(...firstFolder.path);
tree.push(...firstFolder.tree);
}
return {
...acc,
path,
tree,
};
},
{ path: start, tree: [] },
);
const { path, tree } = getFolder(folder, [folder.name]);
return {
path: truncatePathMiddleToLength(path.join('/'), 40),
treeAcc: tree.length ? tree[tree.length - 1].tree : null,
};
};
export const flattenTree = tree => {
const flatten = blobTree =>
blobTree.reduce((acc, file) => {
const blob = file;
let treeToFlatten = blob.tree;
if (file.type === TREE_TYPE && file.tree.length === 1) {
const { treeAcc, path } = getLowestSingleFolder(file);
if (treeAcc) {
blob.name = path;
treeToFlatten = flatten(treeAcc);
}
}
blob.tree = flatten(treeToFlatten);
return acc.concat(blob);
}, []);
return flatten(tree);
};
export const generateTreeList = files => {
const { treeEntries, tree } = files.reduce(
2018-12-05 23:21:45 +05:30
(acc, file) => {
2019-02-15 15:39:39 +05:30
const split = file.new_path.split('/');
2018-12-05 23:21:45 +05:30
split.forEach((name, i) => {
const parent = acc.treeEntries[split.slice(0, i).join('/')];
const path = `${parent ? `${parent.path}/` : ''}${name}`;
if (!acc.treeEntries[path]) {
2019-02-15 15:39:39 +05:30
const type = path === file.new_path ? 'blob' : 'tree';
2018-12-05 23:21:45 +05:30
acc.treeEntries[path] = {
key: path,
path,
name,
type,
tree: [],
};
const entry = acc.treeEntries[path];
if (type === 'blob') {
Object.assign(entry, {
changed: true,
2019-02-15 15:39:39 +05:30
tempFile: file.new_file,
deleted: file.deleted_file,
fileHash: file.file_hash,
addedLines: file.added_lines,
removedLines: file.removed_lines,
2019-03-02 22:35:43 +05:30
parentPath: parent ? `${parent.path}/` : '/',
2018-12-05 23:21:45 +05:30
});
} else {
Object.assign(entry, {
opened: true,
});
}
(parent ? parent.tree : acc.tree).push(entry);
}
});
return acc;
},
{ treeEntries: {}, tree: [] },
);
2018-12-13 13:39:08 +05:30
2019-03-02 22:35:43 +05:30
return { treeEntries, tree: flattenTree(tree) };
};
2018-12-13 13:39:08 +05:30
export const getDiffMode = diffFile => {
2019-02-15 15:39:39 +05:30
const diffModeKey = Object.keys(diffModes).find(key => diffFile[`${key}_file`]);
return (
diffModes[diffModeKey] ||
2019-07-07 11:18:12 +05:30
(diffFile.viewer &&
diffFile.viewer.name === diffViewerModes.mode_changed &&
diffViewerModes.mode_changed) ||
2019-02-15 15:39:39 +05:30
diffModes.replaced
);
2018-12-13 13:39:08 +05:30
};
2019-07-07 11:18:12 +05:30
export const convertExpandLines = ({
diffLines,
data,
typeKey,
oldLineKey,
newLineKey,
mapLine,
}) => {
const dataLength = data.length;
2019-07-31 22:56:46 +05:30
const lines = [];
for (let i = 0, diffLinesLength = diffLines.length; i < diffLinesLength; i += 1) {
const line = diffLines[i];
2019-07-07 11:18:12 +05:30
if (_.property(typeKey)(line) === 'match') {
const beforeLine = diffLines[i - 1];
const afterLine = diffLines[i + 1];
2019-07-31 22:56:46 +05:30
const newLineProperty = _.property(newLineKey);
const beforeLineIndex = newLineProperty(beforeLine) || 0;
const afterLineIndex = newLineProperty(afterLine) - 1 || dataLength;
lines.push(
...data.slice(beforeLineIndex, afterLineIndex).map((l, index) =>
mapLine({
line: Object.assign(l, { hasForm: false, discussions: [] }),
2019-07-07 11:18:12 +05:30
oldLine: (_.property(oldLineKey)(beforeLine) || 0) + index + 1,
2019-07-31 22:56:46 +05:30
newLine: (newLineProperty(beforeLine) || 0) + index + 1,
2019-07-07 11:18:12 +05:30
}),
2019-07-31 22:56:46 +05:30
),
2019-07-07 11:18:12 +05:30
);
} else {
2019-07-31 22:56:46 +05:30
lines.push(line);
2019-07-07 11:18:12 +05:30
}
2019-07-31 22:56:46 +05:30
}
2019-07-07 11:18:12 +05:30
2019-07-31 22:56:46 +05:30
return lines;
2019-07-07 11:18:12 +05:30
};
2019-07-31 22:56:46 +05:30
export const idleCallback = cb => requestIdleCallback(cb);
2019-09-30 21:07:59 +05:30
export const updateLineInFile = (selectedFile, lineCode, updateFn) => {
if (selectedFile.parallel_diff_lines) {
const targetLine = selectedFile.parallel_diff_lines.find(
line =>
(line.left && line.left.line_code === lineCode) ||
(line.right && line.right.line_code === lineCode),
);
if (targetLine) {
const side = targetLine.left && targetLine.left.line_code === lineCode ? 'left' : 'right';
updateFn(targetLine[side]);
}
}
if (selectedFile.highlighted_diff_lines) {
const targetInlineLine = selectedFile.highlighted_diff_lines.find(
line => line.line_code === lineCode,
);
if (targetInlineLine) {
updateFn(targetInlineLine);
}
}
};
export const allDiscussionWrappersExpanded = diff => {
const discussionsExpandedArray = [];
if (diff.parallel_diff_lines) {
diff.parallel_diff_lines.forEach(line => {
if (line.left && line.left.discussions.length) {
discussionsExpandedArray.push(line.left.discussionsExpanded);
}
if (line.right && line.right.discussions.length) {
discussionsExpandedArray.push(line.right.discussionsExpanded);
}
});
} else if (diff.highlighted_diff_lines) {
2020-01-01 13:55:28 +05:30
diff.highlighted_diff_lines.forEach(line => {
2019-09-30 21:07:59 +05:30
if (line.discussions.length) {
discussionsExpandedArray.push(line.discussionsExpanded);
}
});
}
return discussionsExpandedArray.every(el => el);
};