Commit 15459a31 authored by Thomas Randolph's avatar Thomas Randolph Committed by Paul Slaughter

Move utilities used for the Diffs app Tree Worker into a separate file

https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63449
parent aa564dc9
import { property, isEqual } from 'lodash';
import { diffModes, diffViewerModes } from '~/ide/constants';
import { truncatePathMiddleToLength } from '~/lib/utils/text_utility';
import {
LINE_POSITION_LEFT,
LINE_POSITION_RIGHT,
......@@ -11,7 +10,6 @@ import {
OLD_LINE_TYPE,
MATCH_LINE_TYPE,
LINES_TO_BE_RENDERED_DIRECTLY,
TREE_TYPE,
INLINE_DIFF_LINES_KEY,
SHOW_WHITESPACE,
NO_SHOW_WHITESPACE,
......@@ -485,111 +483,6 @@ export function isDiscussionApplicableToLine({ discussion, diffPosition, latestD
return latestDiff && discussion.active && line_code === discussion.line_code;
}
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(
(acc, file) => {
const split = file.new_path.split('/');
split.forEach((name, i) => {
const parent = acc.treeEntries[split.slice(0, i).join('/')];
const path = `${parent ? `${parent.path}/` : ''}${name}`;
if (!acc.treeEntries[path]) {
const type = path === file.new_path ? 'blob' : 'tree';
acc.treeEntries[path] = {
key: path,
path,
name,
type,
tree: [],
};
const entry = acc.treeEntries[path];
if (type === 'blob') {
Object.assign(entry, {
changed: true,
tempFile: file.new_file,
deleted: file.deleted_file,
fileHash: file.file_hash,
addedLines: file.added_lines,
removedLines: file.removed_lines,
parentPath: parent ? `${parent.path}/` : '/',
submodule: file.submodule,
});
} else {
Object.assign(entry, {
opened: true,
});
}
(parent ? parent.tree : acc.tree).push(entry);
}
});
return acc;
},
{ treeEntries: {}, tree: [] },
);
return { treeEntries, tree: flattenTree(tree) };
};
export const getDiffMode = (diffFile) => {
const diffModeKey = Object.keys(diffModes).find((key) => diffFile[`${key}_file`]);
return (
......
import { truncatePathMiddleToLength } from '~/lib/utils/text_utility';
import { TREE_TYPE } from '../constants';
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(
(acc, file) => {
const split = file.new_path.split('/');
split.forEach((name, i) => {
const parent = acc.treeEntries[split.slice(0, i).join('/')];
const path = `${parent ? `${parent.path}/` : ''}${name}`;
if (!acc.treeEntries[path]) {
const type = path === file.new_path ? 'blob' : 'tree';
acc.treeEntries[path] = {
key: path,
path,
name,
type,
tree: [],
};
const entry = acc.treeEntries[path];
if (type === 'blob') {
Object.assign(entry, {
changed: true,
tempFile: file.new_file,
deleted: file.deleted_file,
fileHash: file.file_hash,
addedLines: file.added_lines,
removedLines: file.removed_lines,
parentPath: parent ? `${parent.path}/` : '/',
submodule: file.submodule,
});
} else {
Object.assign(entry, {
opened: true,
});
}
(parent ? parent.tree : acc.tree).push(entry);
}
});
return acc;
},
{ treeEntries: {}, tree: [] },
);
return { treeEntries, tree: flattenTree(tree) };
};
import { sortTree } from '~/ide/stores/utils';
import { generateTreeList } from '../store/utils';
import { generateTreeList } from '../utils/workers';
// eslint-disable-next-line no-restricted-globals
self.addEventListener('message', (e) => {
......
......@@ -54,6 +54,7 @@ import {
} from '~/diffs/store/actions';
import * as types from '~/diffs/store/mutation_types';
import * as utils from '~/diffs/store/utils';
import * as workerUtils from '~/diffs/utils/workers';
import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils';
import * as commonUtils from '~/lib/utils/common_utils';
......@@ -252,7 +253,10 @@ describe('DiffsStoreActions', () => {
{ type: types.SET_MERGE_REQUEST_DIFFS, payload: diffMetadata.merge_request_diffs },
{ type: types.SET_DIFF_METADATA, payload: noFilesData },
// Workers are synchronous in Jest environment (see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/58805)
{ type: types.SET_TREE_DATA, payload: utils.generateTreeList(diffMetadata.diff_files) },
{
type: types.SET_TREE_DATA,
payload: workerUtils.generateTreeList(diffMetadata.diff_files),
},
],
[],
() => {
......
......@@ -685,141 +685,6 @@ describe('DiffsStoreUtils', () => {
});
});
describe('generateTreeList', () => {
let files;
beforeAll(() => {
files = [
{
new_path: 'app/index.js',
deleted_file: false,
new_file: false,
removed_lines: 10,
added_lines: 0,
file_hash: 'test',
},
{
new_path: 'app/test/index.js',
deleted_file: false,
new_file: true,
removed_lines: 0,
added_lines: 0,
file_hash: 'test',
},
{
new_path: 'app/test/filepathneedstruncating.js',
deleted_file: false,
new_file: true,
removed_lines: 0,
added_lines: 0,
file_hash: 'test',
},
{
new_path: 'package.json',
deleted_file: true,
new_file: false,
removed_lines: 0,
added_lines: 0,
file_hash: 'test',
},
];
});
it('creates a tree of files', () => {
const { tree } = utils.generateTreeList(files);
expect(tree).toEqual([
{
key: 'app',
path: 'app',
name: 'app',
type: 'tree',
tree: [
{
addedLines: 0,
changed: true,
deleted: false,
fileHash: 'test',
key: 'app/index.js',
name: 'index.js',
parentPath: 'app/',
path: 'app/index.js',
removedLines: 10,
tempFile: false,
type: 'blob',
tree: [],
},
{
key: 'app/test',
path: 'app/test',
name: 'test',
type: 'tree',
opened: true,
tree: [
{
addedLines: 0,
changed: true,
deleted: false,
fileHash: 'test',
key: 'app/test/index.js',
name: 'index.js',
parentPath: 'app/test/',
path: 'app/test/index.js',
removedLines: 0,
tempFile: true,
type: 'blob',
tree: [],
},
{
addedLines: 0,
changed: true,
deleted: false,
fileHash: 'test',
key: 'app/test/filepathneedstruncating.js',
name: 'filepathneedstruncating.js',
parentPath: 'app/test/',
path: 'app/test/filepathneedstruncating.js',
removedLines: 0,
tempFile: true,
type: 'blob',
tree: [],
},
],
},
],
opened: true,
},
{
key: 'package.json',
parentPath: '/',
path: 'package.json',
name: 'package.json',
type: 'blob',
changed: true,
tempFile: false,
deleted: true,
fileHash: 'test',
addedLines: 0,
removedLines: 0,
tree: [],
},
]);
});
it('creates flat list of blobs & folders', () => {
const { treeEntries } = utils.generateTreeList(files);
expect(Object.keys(treeEntries)).toEqual([
'app',
'app/index.js',
'app/test',
'app/test/index.js',
'app/test/filepathneedstruncating.js',
'package.json',
]);
});
});
describe('getDiffMode', () => {
it('returns mode when matched in file', () => {
expect(
......@@ -842,177 +707,6 @@ describe('DiffsStoreUtils', () => {
});
});
describe('getLowestSingleFolder', () => {
it('returns path and tree of lowest single folder tree', () => {
const folder = {
name: 'app',
type: 'tree',
tree: [
{
name: 'javascripts',
type: 'tree',
tree: [
{
type: 'blob',
name: 'index.js',
},
],
},
],
};
const { path, treeAcc } = utils.getLowestSingleFolder(folder);
expect(path).toEqual('app/javascripts');
expect(treeAcc).toEqual([
{
type: 'blob',
name: 'index.js',
},
]);
});
it('returns passed in folders path & tree when more than tree exists', () => {
const folder = {
name: 'app',
type: 'tree',
tree: [
{
name: 'spec',
type: 'blob',
tree: [],
},
],
};
const { path, treeAcc } = utils.getLowestSingleFolder(folder);
expect(path).toEqual('app');
expect(treeAcc).toBeNull();
});
});
describe('flattenTree', () => {
it('returns flattened directory structure', () => {
const tree = [
{
type: 'tree',
name: 'app',
tree: [
{
type: 'tree',
name: 'javascripts',
tree: [
{
type: 'blob',
name: 'index.js',
tree: [],
},
],
},
],
},
{
type: 'tree',
name: 'ee',
tree: [
{
type: 'tree',
name: 'lib',
tree: [
{
type: 'tree',
name: 'ee',
tree: [
{
type: 'tree',
name: 'gitlab',
tree: [
{
type: 'tree',
name: 'checks',
tree: [
{
type: 'tree',
name: 'longtreenametomakepath',
tree: [
{
type: 'blob',
name: 'diff_check.rb',
tree: [],
},
],
},
],
},
],
},
],
},
],
},
],
},
{
type: 'tree',
name: 'spec',
tree: [
{
type: 'tree',
name: 'javascripts',
tree: [],
},
{
type: 'blob',
name: 'index_spec.js',
tree: [],
},
],
},
];
const flattened = utils.flattenTree(tree);
expect(flattened).toEqual([
{
type: 'tree',
name: 'app/javascripts',
tree: [
{
type: 'blob',
name: 'index.js',
tree: [],
},
],
},
{
type: 'tree',
name: 'ee/lib/…/…/…/longtreenametomakepath',
tree: [
{
name: 'diff_check.rb',
tree: [],
type: 'blob',
},
],
},
{
type: 'tree',
name: 'spec',
tree: [
{
type: 'tree',
name: 'javascripts',
tree: [],
},
{
type: 'blob',
name: 'index_spec.js',
tree: [],
},
],
},
]);
});
});
describe('convertExpandLines', () => {
it('converts expanded lines to normal lines', () => {
const diffLines = [
......
import { generateTreeList, getLowestSingleFolder, flattenTree } from '~/diffs/utils/workers';
describe('~/diffs/utils/workers', () => {
describe('generateTreeList', () => {
let files;
beforeAll(() => {
files = [
{
new_path: 'app/index.js',
deleted_file: false,
new_file: false,
removed_lines: 10,
added_lines: 0,
file_hash: 'test',
},
{
new_path: 'app/test/index.js',
deleted_file: false,
new_file: true,
removed_lines: 0,
added_lines: 0,
file_hash: 'test',
},
{
new_path: 'app/test/filepathneedstruncating.js',
deleted_file: false,
new_file: true,
removed_lines: 0,
added_lines: 0,
file_hash: 'test',
},
{
new_path: 'package.json',
deleted_file: true,
new_file: false,
removed_lines: 0,
added_lines: 0,
file_hash: 'test',
},
];
});
it('creates a tree of files', () => {
const { tree } = generateTreeList(files);
expect(tree).toEqual([
{
key: 'app',
path: 'app',
name: 'app',
type: 'tree',
tree: [
{
addedLines: 0,
changed: true,
deleted: false,
fileHash: 'test',
key: 'app/index.js',
name: 'index.js',
parentPath: 'app/',
path: 'app/index.js',
removedLines: 10,
tempFile: false,
type: 'blob',
tree: [],
},
{
key: 'app/test',
path: 'app/test',
name: 'test',
type: 'tree',
opened: true,
tree: [
{
addedLines: 0,
changed: true,
deleted: false,
fileHash: 'test',
key: 'app/test/index.js',
name: 'index.js',
parentPath: 'app/test/',
path: 'app/test/index.js',
removedLines: 0,
tempFile: true,
type: 'blob',
tree: [],
},
{
addedLines: 0,
changed: true,
deleted: false,
fileHash: 'test',
key: 'app/test/filepathneedstruncating.js',
name: 'filepathneedstruncating.js',
parentPath: 'app/test/',
path: 'app/test/filepathneedstruncating.js',
removedLines: 0,
tempFile: true,
type: 'blob',
tree: [],
},
],
},
],
opened: true,
},
{
key: 'package.json',
parentPath: '/',
path: 'package.json',
name: 'package.json',
type: 'blob',
changed: true,
tempFile: false,
deleted: true,
fileHash: 'test',
addedLines: 0,
removedLines: 0,
tree: [],
},
]);
});
it('creates flat list of blobs & folders', () => {
const { treeEntries } = generateTreeList(files);
expect(Object.keys(treeEntries)).toEqual([
'app',
'app/index.js',
'app/test',
'app/test/index.js',
'app/test/filepathneedstruncating.js',
'package.json',
]);
});
});
describe('getLowestSingleFolder', () => {
it('returns path and tree of lowest single folder tree', () => {
const folder = {
name: 'app',
type: 'tree',
tree: [
{
name: 'javascripts',
type: 'tree',
tree: [
{
type: 'blob',
name: 'index.js',
},
],
},
],
};
const { path, treeAcc } = getLowestSingleFolder(folder);
expect(path).toEqual('app/javascripts');
expect(treeAcc).toEqual([
{
type: 'blob',
name: 'index.js',
},
]);
});
it('returns passed in folders path & tree when more than tree exists', () => {
const folder = {
name: 'app',
type: 'tree',
tree: [
{
name: 'spec',
type: 'blob',
tree: [],
},
],
};
const { path, treeAcc } = getLowestSingleFolder(folder);
expect(path).toEqual('app');
expect(treeAcc).toBeNull();
});
});
describe('flattenTree', () => {
it('returns flattened directory structure', () => {
const tree = [
{
type: 'tree',
name: 'app',
tree: [
{
type: 'tree',
name: 'javascripts',
tree: [
{
type: 'blob',
name: 'index.js',
tree: [],
},
],
},
],
},
{
type: 'tree',
name: 'ee',
tree: [
{
type: 'tree',
name: 'lib',
tree: [
{
type: 'tree',
name: 'ee',
tree: [
{
type: 'tree',
name: 'gitlab',
tree: [
{
type: 'tree',
name: 'checks',
tree: [
{
type: 'tree',
name: 'longtreenametomakepath',
tree: [
{
type: 'blob',
name: 'diff_check.rb',
tree: [],
},
],
},
],
},
],
},
],
},
],
},
],
},
{
type: 'tree',
name: 'spec',
tree: [
{
type: 'tree',
name: 'javascripts',
tree: [],
},
{
type: 'blob',
name: 'index_spec.js',
tree: [],
},
],
},
];
const flattened = flattenTree(tree);
expect(flattened).toEqual([
{
type: 'tree',
name: 'app/javascripts',
tree: [
{
type: 'blob',
name: 'index.js',
tree: [],
},
],
},
{
type: 'tree',
name: 'ee/lib/…/…/…/longtreenametomakepath',
tree: [
{
name: 'diff_check.rb',
tree: [],
type: 'blob',
},
],
},
{
type: 'tree',
name: 'spec',
tree: [
{
type: 'tree',
name: 'javascripts',
tree: [],
},
{
type: 'blob',
name: 'index_spec.js',
tree: [],
},
],
},
]);
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment