Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ In addition to being able to format pre-formatted tree strings, you may also gen

The walking process through files is performed asynchronously. Therefore, selecting heavily-nested folders (e.g. `node_modules`) will directly affect performance speed.

By default, `node_modules` and `.git` are ignored while generating tree string for directories. However, this can be customized by setting `asciiTreeGenerator.directoryIgnore` in configurations. Also, setting `asciiTreeGenerator.directoryMaxDepth` can limit the depth of directory walking-through.
At first,`asciiTreeGenerator.directoryIgnore` will get `.gitignore` as default. However, this can be customized by setting `asciiTreeGenerator.directoryIgnore` in configurations. if no `.gitignore` file and `asciiTreeGenerator.directoryIgnore` setting `node_modules` and `.git` are ignored while generating tree string for directories. Also, setting `asciiTreeGenerator.directoryMaxDepth` can limit the depth of directory walking-through.

## Configuration

Expand Down
11 changes: 9 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@
{
"title": "Ascii Tree Generator Configuration",
"properties": {
"asciiTreeGenerator.enableCommentInFile": {
"type": "boolean",
"default": false,
"description": "Enable comment in file by @file"
},
"asciiTreeGenerator.rootElement": {
"type": "integer",
"default": 46,
Expand Down Expand Up @@ -69,7 +74,7 @@
"items": {
"type": "string"
},
"default": ["node_modules", ".git"],
"default": [],
"description": "The glob patterns of ignored path while generate tree string for directory"
},
"asciiTreeGenerator.directoryMaxDepth": {
Expand Down Expand Up @@ -136,6 +141,7 @@
"@types/glob": "^7.1.1",
"@types/mocha": "8",
"@types/node": "12",
"@types/parse-gitignore": "^1.0.2",
"@types/vscode": "^1.50.0",
"@typescript-eslint/eslint-plugin": "^4.16.0",
"@typescript-eslint/parser": "^4.16.0",
Expand All @@ -146,6 +152,7 @@
"vscode-test": "^1.5.2"
},
"dependencies": {
"glob": "^7.1.3"
"glob": "^7.1.3",
"parse-gitignore": "^2.0.0"
}
}
1 change: 1 addition & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { IVsCodeConfig } from './lib/interface';
export function getConfig(): IVsCodeConfig {
const config: IVsCodeConfig = {
directoryIgnore: vscode.workspace.getConfiguration().get<string[]>('asciiTreeGenerator.directoryIgnore'),
enableCommentInFile: vscode.workspace.getConfiguration().get<boolean>('asciiTreeGenerator.enableCommentInFile'),
directoryMaxDepth: vscode.workspace.getConfiguration().get<number>('asciiTreeGenerator.directoryMaxDepth'),
rootCharCode: vscode.workspace
.getConfiguration()
Expand Down
7 changes: 4 additions & 3 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as path from 'path';

import { formatFileTreeItemsFromDirectory } from './lib/directory';
import { generate } from './lib/generator';
import { getUserEOL, createWebview, revertTreeString, getCharCodesFromConfig, getDirectoryIgnoreFromConfig, getDirectoryMaxDepthFromConfig } from './utils';
import { getUserEOL, createWebview, revertTreeString, getCharCodesFromConfig, getDirectoryIgnoreFromConfig, getDirectoryMaxDepthFromConfig, getEnableCommentInFileFromConfig } from './utils';
import { formatFileTreeItemsFromText } from './lib/text';

export function activate(context: vscode.ExtensionContext) {
Expand All @@ -28,10 +28,11 @@ export function activate(context: vscode.ExtensionContext) {
// if no selected resource found, then try to get workspace root path
const target: vscode.Uri = resource || rootWorkspace.uri;
const root = path.relative(rootWorkspace.uri.fsPath, target.fsPath) || '.';

const enableCommentInFile = getEnableCommentInFileFromConfig();
// Todo: read plugin configuration
const items = await formatFileTreeItemsFromDirectory(target!.fsPath, {
maxDepth: Number.MAX_VALUE,
enableCommentInFile,
sort: true,
ignore: [],
});
Expand Down Expand Up @@ -61,10 +62,10 @@ export function activate(context: vscode.ExtensionContext) {
const target: vscode.Uri = resource || rootWorkspace.uri;
const root =
path.relative(rootWorkspace.uri.fsPath, target.fsPath) || '.';

const items = await formatFileTreeItemsFromDirectory(target!.fsPath, {
ignore: getDirectoryIgnoreFromConfig(),
maxDepth: getDirectoryMaxDepthFromConfig(),
enableCommentInFile: getEnableCommentInFileFromConfig(),
sort: true,
});
const text = generate(items, {
Expand Down
12 changes: 10 additions & 2 deletions src/lib/directory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as rawGlob from 'glob';
import * as fs from 'fs';
import * as path from 'path';
import { promisify } from 'util';
import { getCommentInFile } from '../utils';
import { IFileStat, IFileTreeItem, IListDirectoryConfig } from './interface';

const glob = promisify(rawGlob);
Expand All @@ -14,19 +15,25 @@ const readStat = promisify(fs.stat);
*/
async function getFileStat(
name: string,
dir?: string
dir?: string,
enableCommentInFile?: boolean
): Promise<IFileStat | null> {
const absolutePath = path.join(dir || process.cwd(), name);
let stat: fs.Stats;
let comment: string | undefined;
try {
stat = await readStat(absolutePath);
} catch (e) {
return null;
}
if (enableCommentInFile) {
comment = getCommentInFile(name, dir);
}
return {
name,
absolutePath,
stat,
comment,
isDirectory: stat.isDirectory(),
children: [],
};
Expand Down Expand Up @@ -63,7 +70,7 @@ export async function listDirectory(
ignore,
});
let files: IFileStat[] = (await Promise.all(
fileNames.map((item) => getFileStat(item, dir))
fileNames.map((item) => getFileStat(item, dir, config?.enableCommentInFile))
)) as IFileStat[];
files = files.filter((item) => item !== null);
// sort
Expand Down Expand Up @@ -101,6 +108,7 @@ export async function formatFileTreeItemsFromDirectory(
const item = {
name: f.name,
isLast: index === list.length - 1,
comment: f.comment,
depth: parent ? parent.depth + 1 : 0,
parent,
};
Expand Down
17 changes: 14 additions & 3 deletions src/lib/generator.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { EOL } from 'os';
import { IFileTreeItem, IFormatOptions } from './interface';
import { defaultCharset } from '../utils';

import { defaultCharset, getTreeStringItemCharacterLength } from '../utils';
const DEFAULT_COMMENT_PRE_SPACE = ' ';
const DEFAULT_COMMENT_PRE_TEXT = '// ';
function createTreeString(start: string, fill: string, size = 3) {
let result = '';
for (let i = 0; i < size; i++) {
Expand All @@ -14,6 +15,13 @@ function createTreeString(start: string, fill: string, size = 3) {
return result + ' ';
}

function getItemCommentText(maxRight: number, item: IFileTreeItem) {
if (!item.comment) return '';
const itemRight = getTreeStringItemCharacterLength(item.depth, item.name);
const itemRightSpace = maxRight - itemRight;
return Array(itemRightSpace).fill(' ').join('') + DEFAULT_COMMENT_PRE_SPACE + DEFAULT_COMMENT_PRE_TEXT + item.comment;
}

/** generate tree string */
export function generate(items: IFileTreeItem[], options: IFormatOptions = {}) {
const {
Expand All @@ -28,6 +36,9 @@ export function generate(items: IFileTreeItem[], options: IFormatOptions = {}) {
: 0;
leftSpace = Array(minLeft).fill(' ').join('');
}
const maxRight = items.length
? Math.max(...items.map((item) => getTreeStringItemCharacterLength(item.depth, item.name) || 0))
: 0;
const lines = items.map((item) => {
const texts: string[] = [];
texts.push(
Expand All @@ -43,7 +54,7 @@ export function generate(items: IFileTreeItem[], options: IFormatOptions = {}) {
);
parent = parent.parent;
}
return leftSpace + texts.join('') + item.name;
return leftSpace + texts.join('') + item.name + getItemCommentText(maxRight, item);
});
if (charset.root !== '') {
lines.unshift(leftSpace + charset.root);
Expand Down
4 changes: 4 additions & 0 deletions src/lib/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ export interface IListDirectoryConfig {
ignore?: string[];
sort?: boolean;
maxDepth?: number;
enableCommentInFile?: boolean;
}

export interface IFileStat {
name: string;
absolutePath: string;
stat: Stats;
isDirectory: boolean;
comment?: string,
children: IFileStat[];
parent?: IFileStat;
}
Expand All @@ -21,6 +23,7 @@ export interface IFileTreeItem {
depth: number;
// left space width
left?: number;
comment?: string;
parent?: IFileTreeItem;
}

Expand All @@ -44,6 +47,7 @@ export interface IVsCodeConfig {
parentCharCode: number | undefined;
dashCharCode: number | undefined;
blankCharCode: number | undefined;
enableCommentInFile: boolean | undefined;
}

// format tree string options
Expand Down
121 changes: 119 additions & 2 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import * as fs from 'fs';
import * as path from 'path';
import { ICharset, IVsCodeConfig } from './lib/interface';
import { getConfig } from './config';

import * as parseGitignore from 'parse-gitignore';
const ONE_DEEP_CHARACTER_LENGTH = 4;
let isInTestMode = false;

export const defaultCharset: ICharset = {
Expand Down Expand Up @@ -34,6 +35,88 @@ export function getUserEOL() {
}
return eol;
}
/**
* get comment in file
* @param name file name
* @param dir file dir path
* @returns common string
*/
export function getCommentInFile(
name: string,
dir?: string
): string | undefined {
const absolutePath = path.join(dir || process.cwd(), name);
try {
const fileString = getStringFromFile(absolutePath);
const commonString = getCommentInFileString(fileString);
return commonString;
}
catch (e) {
return undefined;
}
}
/**
* get tree string item character length
* @param deep tree string item deep
* @param name file name
* @returns character length
*/
export function getTreeStringItemCharacterLength(deep: number, name: string): number {
return deep * ONE_DEEP_CHARACTER_LENGTH + calculateStringLength(name);
}

export function checkCharacterLength(char: string) {
const regexChinese = /[\u4e00-\u9fa5]/;
if (regexChinese.test(char)) {
return 2;
}
else {
return 1;
}
}

export function calculateStringLength(str: string) {
let length = 0;
for (let i = 0; i < str.length; i++) {
length += checkCharacterLength(str[i]);
}
return length;
}

/**
* get comment in file string
* @param fileString file string
* @returns comment
*/
export function getCommentInFileString(fileString: string): string | undefined {
// get comment from tags @fileoverview @file @overview
const fileRegex = /@(file|fileoverview|overview)\s+(.+)/;
const match = fileString.match(fileRegex);
if (match) {
return match[2].trim();
} else {
return undefined;
}
}

export function isPathExists(p: string): boolean {
try {
fs.accessSync(p);
} catch (err) {
return false;
}
return true;
}

export function getStringFromFile(filePath: string): string {
if (!isPathExists(filePath)) return '';
try {
const fileString = fs.readFileSync(filePath, "utf8");
return fileString;
} catch (e) {
return '';
}
}

/**
* create webview, with the ability to copy to clipboard, ect
Expand Down Expand Up @@ -127,11 +210,45 @@ export function getCharCodesFromConfig(): ICharset {
return charset;
}

export function getEnableCommentInFileFromConfig(): boolean {
const { enableCommentInFile } = getConfig();
return enableCommentInFile || false;
}

export function getDirectoryIgnoreFromGitignore(): string[] | undefined {
const workspaces = vscode.workspace.workspaceFolders;
const rootWorkspace: vscode.WorkspaceFolder | undefined = workspaces
? workspaces[0]
: undefined;
if (!rootWorkspace) {
return undefined;
}
const gitignorePath = path.join(
rootWorkspace.uri.fsPath,
'.gitignore'
);
if (!isPathExists(gitignorePath)) {
return undefined;
}
try {
const gitignoreContent = getStringFromFile(gitignorePath);
const gitignore = parseGitignore.parse(gitignoreContent).patterns;
return gitignore;
}
catch (e) {
return undefined;
}
}

export function getDirectoryIgnoreFromConfig(): string[] {
const { directoryIgnore }: IVsCodeConfig = getConfig();
if (Array.isArray(directoryIgnore)) {
const directoryIgnoreInGitignore = getDirectoryIgnoreFromGitignore();
if (Array.isArray(directoryIgnore) && directoryIgnore.length > 0) {
return directoryIgnore.filter(item => typeof item === 'string');
}
if (Array.isArray(directoryIgnoreInGitignore) && directoryIgnoreInGitignore.length > 0) {
return directoryIgnoreInGitignore.filter(item => typeof item === 'string');
}
return defaultDirectoryIgnore;
}

Expand Down
12 changes: 12 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,13 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.15.tgz#10ee6a6a3f971966fddfa3f6e89ef7a73ec622df"
integrity sha512-F6S4Chv4JicJmyrwlDkxUdGNSplsQdGwp1A0AJloEVDirWdZOAiRHhovDlsFkKUrquUXhz1imJhXHsf59auyAg==

"@types/parse-gitignore@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@types/parse-gitignore/-/parse-gitignore-1.0.2.tgz#ee82f98465166090d44fb1ff12b642f3c361b32c"
integrity sha512-AQwj+lNTWI7y1kkMe8qLByiToXoXs/du70qGFIHJZaJUVrF5jB8QzvWmLyR1VWYqRagpY8ABrqAjs7uHsJnVBQ==
dependencies:
"@types/node" "*"

"@types/vscode@^1.50.0":
version "1.57.0"
resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.57.0.tgz#cc648e0573b92f725cd1baf2621f8da9f8bc689f"
Expand Down Expand Up @@ -1125,6 +1132,11 @@ parent-module@^1.0.0:
dependencies:
callsites "^3.0.0"

parse-gitignore@^2.0.0:
version "2.0.0"
resolved "https://registry.npm.qianxin-inc.cn/parse-gitignore/download/parse-gitignore-2.0.0.tgz#81156b265115c507129f3faea067b8476da3b642"
integrity sha1-gRVrJlEVxQcSnz+uoGe4R22jtkI=

path-exists@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
Expand Down