Skip to content

Commit

Permalink
mhutchie#337 Enhanced the Commit Details View File Tree renderer to b…
Browse files Browse the repository at this point in the history
…etter display deeply nested directory structures, such that folders with a single child folder are compressed into a single combined folder element.
  • Loading branch information
mhutchie committed Jul 4, 2020
1 parent d65a3e7 commit 079e61c
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 13 deletions.
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@
"default": "date",
"markdownDescription": "Specifies the order of commits on the Git Graph view. See [git log](https://git-scm.com/docs/git-log#_commit_ordering) for more information on each order option."
},
"git-graph.commitDetailsViewFileTreeCompactFolders": {
"type": "boolean",
"default": true,
"description": "Render the File Tree in the Commit Details / Comparison View in a compacted form, such that folders with a single child folder are compressed into a single combined folder element."
},
"git-graph.commitDetailsViewLocation": {
"type": "string",
"enum": [
Expand Down
7 changes: 7 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,13 @@ class Config {
return this.config.get<string>('fileEncoding', 'utf8');
}

/**
* Get the value of the `git-graph.commitDetailsViewFileTreeCompactFolders` Extension Setting.
*/
get fileTreeCompactFolders() {
return !!this.config.get('commitDetailsViewFileTreeCompactFolders', true);
}

/**
* Get the value of the `git-graph.graphColours` Extension Setting.
*/
Expand Down
1 change: 1 addition & 0 deletions src/gitGraphView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,7 @@ export class GitGraphView implements vscode.Disposable {
enhancedAccessibility: config.enhancedAccessibility,
fetchAndPrune: config.fetchAndPrune,
fetchAvatars: config.fetchAvatars && this.extensionState.isAvatarStorageAvailable(),
fileTreeCompactFolders: config.fileTreeCompactFolders,
graphColours: config.graphColours,
graphStyle: config.graphStyle,
grid: { x: 16, y: 24, offsetX: 16, offsetY: 12, expandY: 250 },
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ export interface GitGraphViewConfig {
readonly enhancedAccessibility: boolean;
readonly fetchAndPrune: boolean;
readonly fetchAvatars: boolean;
readonly fileTreeCompactFolders: boolean;
readonly graphColours: ReadonlyArray<string>;
readonly graphStyle: GraphStyle;
readonly grid: { x: number, y: number, offsetX: number, offsetY: number, expandY: number };
Expand Down
62 changes: 62 additions & 0 deletions tests/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1618,6 +1618,68 @@ describe('Config', () => {
});
});

describe('fileTreeCompactFolders', () => {
it('Should return TRUE when the configuration value is TRUE', () => {
// Setup
workspaceConfiguration.get.mockReturnValueOnce(true);

// Run
const value = config.fileTreeCompactFolders;

// Assert
expect(workspaceConfiguration.get).toBeCalledWith('commitDetailsViewFileTreeCompactFolders', true);
expect(value).toBe(true);
});

it('Should return FALSE when the configuration value is FALSE', () => {
// Setup
workspaceConfiguration.get.mockReturnValueOnce(false);

// Run
const value = config.fileTreeCompactFolders;

// Assert
expect(workspaceConfiguration.get).toBeCalledWith('commitDetailsViewFileTreeCompactFolders', true);
expect(value).toBe(false);
});

it('Should return TRUE when the configuration value is truthy', () => {
// Setup
workspaceConfiguration.get.mockReturnValueOnce(5);

// Run
const value = config.fileTreeCompactFolders;

// Assert
expect(workspaceConfiguration.get).toBeCalledWith('commitDetailsViewFileTreeCompactFolders', true);
expect(value).toBe(true);
});

it('Should return FALSE when the configuration value is falsy', () => {
// Setup
workspaceConfiguration.get.mockReturnValueOnce(0);

// Run
const value = config.fileTreeCompactFolders;

// Assert
expect(workspaceConfiguration.get).toBeCalledWith('commitDetailsViewFileTreeCompactFolders', true);
expect(value).toBe(false);
});

it('Should return the default value (TRUE) when the configuration value is not set', () => {
// Setup
workspaceConfiguration.get.mockImplementationOnce((_, defaultValue) => defaultValue);

// Run
const value = config.fileTreeCompactFolders;

// Assert
expect(workspaceConfiguration.get).toBeCalledWith('commitDetailsViewFileTreeCompactFolders', true);
expect(value).toBe(true);
});
});

describe('graphColours', () => {
it('Should return a filtered array of colours based on the configuration value', () => {
// Setup
Expand Down
37 changes: 24 additions & 13 deletions web/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2891,21 +2891,32 @@ window.addEventListener('load', () => {
function generateFileViewHtml(folder: FileTreeFolder, gitFiles: ReadonlyArray<GG.GitFileChange>, lastViewedFile: string | null, type: GG.FileViewType, isUncommitted: boolean) {
return type === GG.FileViewType.List
? generateFileListHtml(folder, gitFiles, lastViewedFile, isUncommitted)
: generateFileTreeHtml(folder, gitFiles, lastViewedFile, isUncommitted);
: generateFileTreeHtml(folder, gitFiles, lastViewedFile, isUncommitted, true);
}

function generateFileTreeHtml(folder: FileTreeFolder, gitFiles: ReadonlyArray<GG.GitFileChange>, lastViewedFile: string | null, isUncommitted: boolean) {
let html = (folder.name !== '' ? '<span class="fileTreeFolder' + (folder.reviewed ? '' : ' pendingReview') + '" data-folderpath="' + encodeURIComponent(folder.folderPath) + '"><span class="fileTreeFolderIcon">' + (folder.open ? SVG_ICONS.openFolder : SVG_ICONS.closedFolder) + '</span><span class="gitFolderName">' + escapeHtml(folder.name) + '</span></span>' : '') + '<ul class="fileTreeFolderContents' + (!folder.open ? ' hidden' : '') + '">';
let keys = sortFolderKeys(folder);
for (let i = 0; i < keys.length; i++) {
let cur = folder.contents[keys[i]];
if (cur.type === 'folder') {
html += '<li' + (cur.open ? '' : ' class="closed"') + ' data-pathseg="' + encodeURIComponent(cur.name) + '">' + generateFileTreeHtml(cur, gitFiles, lastViewedFile, isUncommitted) + '</li>';
} else {
html += generateFileTreeLeafHtml(cur.name, cur, gitFiles, lastViewedFile, isUncommitted);
}
}
return html + '</ul>';
function generateFileTreeHtml(folder: FileTreeFolder, gitFiles: ReadonlyArray<GG.GitFileChange>, lastViewedFile: string | null, isUncommitted: boolean, topLevelFolder: boolean): string {
const curFolderInfo = topLevelFolder || !initialState.config.fileTreeCompactFolders
? { folder: folder, name: folder.name, pathSeg: folder.name }
: getCurrentFolderInfo(folder, folder.name, folder.name);

const children = sortFolderKeys(curFolderInfo.folder).map((key) => {
const cur = curFolderInfo.folder.contents[key];
return cur.type === 'folder'
? generateFileTreeHtml(cur, gitFiles, lastViewedFile, isUncommitted, false)
: generateFileTreeLeafHtml(cur.name, cur, gitFiles, lastViewedFile, isUncommitted);
});

return (topLevelFolder ? '' : '<li' + (curFolderInfo.folder.open ? '' : ' class="closed"') + ' data-pathseg="' + encodeURIComponent(curFolderInfo.pathSeg) + '"><span class="fileTreeFolder' + (curFolderInfo.folder.reviewed ? '' : ' pendingReview') + '" title="./' + escapeHtml(curFolderInfo.folder.folderPath) + '" data-folderpath="' + encodeURIComponent(curFolderInfo.folder.folderPath) + '"><span class="fileTreeFolderIcon">' + (curFolderInfo.folder.open ? SVG_ICONS.openFolder : SVG_ICONS.closedFolder) + '</span><span class="gitFolderName">' + escapeHtml(curFolderInfo.name) + '</span></span>') +
'<ul class="fileTreeFolderContents' + (curFolderInfo.folder.open ? '' : ' hidden') + '">' + children.join('') + '</ul>' +
(topLevelFolder ? '' : '</li>');
}

function getCurrentFolderInfo(folder: FileTreeFolder, name: string, pathSeg: string): { folder: FileTreeFolder, name: string, pathSeg: string } {
const keys = Object.keys(folder.contents);
let child: FileTreeNode;
return keys.length === 1 && (child = folder.contents[keys[0]]).type === 'folder'
? getCurrentFolderInfo(<FileTreeFolder>child, name + ' / ' + child.name, pathSeg + '/' + child.name)
: { folder: folder, name: name, pathSeg: pathSeg };
}

function generateFileListHtml(folder: FileTreeFolder, gitFiles: ReadonlyArray<GG.GitFileChange>, lastViewedFile: string | null, isUncommitted: boolean) {
Expand Down

0 comments on commit 079e61c

Please sign in to comment.