Skip to content

Commit

Permalink
improvements to list command and other tidbits (#46)
Browse files Browse the repository at this point in the history
  • Loading branch information
Opeyem1a authored Oct 19, 2024
1 parent aa4f4a5 commit b0a235c
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 61 deletions.
18 changes: 13 additions & 5 deletions src/commands/branch/track.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,13 @@ import { useTree } from '../../hooks/use-tree.js';

function BranchTrack(_: CommandProps) {

Check warning on line 10 in src/commands/branch/track.tsx

View workflow job for this annotation

GitHub Actions / lint_test

'_' is defined but never used
const { allBranches, currentBranch } = useGitHelpers();
const { rootBranchName, isCurrentBranchTracked, attachTo, isLoading } =
useTree();
const {
rootBranchName,
isCurrentBranchTracked,
attachTo,
isLoading,
currentTree,
} = useTree();

// either false or the name of the parent branch
const [complete, setComplete] = useState<false | string>(false);
Expand All @@ -26,9 +31,12 @@ function BranchTrack(_: CommandProps) {

const branchItems = useMemo(() => {
if (allBranches.isLoading) return [];

return allBranches.value.map((b) => ({ label: b, value: b }));
}, [allBranches.value, allBranches.isLoading]);
// only branches in the tree already can be selected as the parent in this case
const branchesInTree = allBranches.value.filter((b) => {
return Boolean(currentTree.find((node) => node.key === b));
});
return branchesInTree.map((b) => ({ label: b, value: b }));
}, [allBranches.value, allBranches.isLoading, currentTree]);

if (isLoading || currentBranch.isLoading || allBranches.isLoading) {
return <Loading />;
Expand Down
7 changes: 6 additions & 1 deletion src/commands/changes/commit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,21 @@ import {
import { Loading } from '../../components/loading.js';
import { SelectRootBranch } from '../../components/select-root-branch.js';
import { Text } from 'ink';
import { UntrackedBranch } from '../../components/untracked-branch.js';
import { useGit } from '../../hooks/use-git.js';
import { useTree } from '../../hooks/use-tree.js';

const ChangesCommit = (props: CommandProps) => {
const { rootBranchName } = useTree();
const { rootBranchName, isCurrentBranchTracked } = useTree();

if (!rootBranchName) {
return <SelectRootBranch />;
}

if (!isCurrentBranchTracked) {
return <UntrackedBranch />;
}

return <DoChangesCommit {...props} />;
};

Expand Down
129 changes: 76 additions & 53 deletions src/commands/list.tsx
Original file line number Diff line number Diff line change
@@ -1,88 +1,111 @@
import React, { useMemo } from 'react';
import { Box, Text } from 'ink';
import { CommandConfig } from '../types.js';
import { Loading } from '../components/loading.js';
import { SelectRootBranch } from '../components/select-root-branch.js';
import { Tree } from '../services/tree.js';
import { useGitHelpers } from '../hooks/use-git-helpers.js';
import { useTree } from '../hooks/use-tree.js';

const colorMap = ['blue', 'red', 'green', 'yellow', 'magenta', 'cyan', 'white'];

/**
* Function to convert the parent-referential Tree structure to a child-referral structure.
* @param tree the current tree
* @returns a map containing nodes and their children
* @returns a record mapping every branch name to the names of its child branches in the tree
*/
export const getChildMap = (tree: Tree): Map<string, string[]> => {
const childMap = new Map<string, string[]>();
export const treeToParentChildRecord = (
tree: Tree
): Record<string, string[]> => {
const record: Record<string, string[]> = {};

tree.forEach(({ key: node, parent }) => {
if (!childMap.has(node)) {
childMap.set(node, []);
tree.forEach((node) => {
if (!(node.key in record)) {
record[node.key] = [];
}
if (parent !== null) {
if (!childMap.has(parent)) {
childMap.set(parent, []);
}
childMap.get(parent)!.push(node);

if (node.parent === null) return;

if (node.parent in record) {
const existingChildren = record[node.parent] as string[];
record[node.parent] = [...existingChildren, node.key];
}
if (!(node.parent in record)) {
record[node.parent] = [];
}
});
return childMap;

return record;
};

const getChildMappedTreeString = (
childMap: Map<string, string[]>,
currentNode: string,
prefix: string = '',
const BranchTreeDisplay = ({
treeParentChildRecord,
displayedBranchName,
currentBranch,
prefix = '',
isLast = true,
depth = 0
) => {
depth = 0,
}: {
treeParentChildRecord: Record<string, string[]>;
displayedBranchName: string;
currentBranch: string;
prefix?: string;
isLast?: boolean;
depth?: number;
}) => {
// Misc UNICODE for reference: └ ─ ┘ ┌ ┐ ├ | ● ○

const connector = depth === 0 ? ' ' : isLast ? '└──' : '├──';
const color = colorMap[depth % colorMap.length];

const currentElement = (
<Text key={currentNode}>
{`${prefix}${connector}○ `}
<Text color={color}>{`${currentNode}\n`}</Text>
</Text>
);

const updatedPrefix = `${prefix}${isLast ? ' ' : '| '}`;

const childElements: JSX.Element[] = [];
const children = childMap.get(currentNode) ?? [];
const numChildren = children.length;
children.forEach((child) => {
const isLastChild = child === children[numChildren - 1];
const childElement = getChildMappedTreeString(
childMap,
child,
updatedPrefix,
isLastChild,
depth + 1
);
childElements.push(
<React.Fragment key={child}>{childElement}</React.Fragment>
);
});
const children = treeParentChildRecord?.[displayedBranchName] ?? [];

return (
<>
{currentElement}
{childElements}
<Text key={displayedBranchName}>
{`${prefix}${connector}${currentBranch === displayedBranchName ? '◉' : '○'} `}
<Text color={color}>{`${displayedBranchName}\n`}</Text>
</Text>
{children.map((child) => {
return (
<React.Fragment key={child}>
<BranchTreeDisplay
treeParentChildRecord={treeParentChildRecord}
displayedBranchName={child}
currentBranch={currentBranch}
prefix={updatedPrefix}
isLast={child === children[children.length - 1]}
depth={depth + 1}
/>
</React.Fragment>
);
})}
</>
);
};

export const List = () => {
const { get, getRoot } = useTree();
const tree = useMemo(() => get(), []);
const { key: root } = useMemo(() => getRoot(), []);
const childMap = getChildMap(tree);
const { currentBranch } = useGitHelpers();
const { get, rootBranchName } = useTree();
const treeParentChildRecord = useMemo(
() => treeToParentChildRecord(get()),
[]
);

if (!rootBranchName) {
return <SelectRootBranch />;
}

if (currentBranch.isLoading) {
return <Loading />;
}

return (
<Box flexDirection="column">
<Text>{getChildMappedTreeString(childMap, root)}</Text>
<Text>
<BranchTreeDisplay
treeParentChildRecord={treeParentChildRecord}
displayedBranchName={rootBranchName}
currentBranch={currentBranch.value}
/>
</Text>
</Box>
);
};
Expand Down
8 changes: 6 additions & 2 deletions src/services/tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ export interface TreeService {
moveOnto: (args: { branch: string; parent: string }) => void;
removeBranch: (branch: string) => void;
get: () => Tree;
getRoot: () => BranchNode;
getRoot: () => BranchNode | undefined;
ROOT: symbol;
}

Expand Down Expand Up @@ -231,7 +231,11 @@ export const createTreeService = (config?: TreeServiceConfig): TreeService => {
},
getRoot: () => {
const tree = _readTree({ storeService, setCurrentTree });
return _getRoot({ tree });
try {
return _getRoot({ tree });
} catch (e) {
return undefined;
}
},
ROOT,
} as Omit<TreeService, 'currentTree'>;
Expand Down

0 comments on commit b0a235c

Please sign in to comment.