<commit_before>import * as Path from 'path'
import * as fs from 'fs'
/*
DirectoryTree load synccode file or walk through the directory to initialize a tree in memory

*/

export default class DirectoryTree {
    constructor(workDir) {
        this._workDir = workDir
        this._dirTree = null
        this.loadSynccodeFile()
        this.initTree()
    }
    
    loadSynccodeFile() {
        let syncCodeFile = Path.resolve(this._workDir, './.synccode')
        if(!fs.existsSync(syncCodeFile)) {
            return
        }
        try {
            this._dirTree = JSON.parse(fs.readFileSync(syncCodeFile).toString())
        } catch(e) {
            return
        }
    }
    
    initTree() {
        if(this_dirTree === null) {
            this._dirTree = walkDir(this._workDir, file => false)
        }
    }
    
    walkDir(path, shouldIgnore) {
        let tree = {path}
        let stat
        try {
            stat = fs.statSync(path)
        } catch(e) {
            return null
        }
        if(stat.isFile()) {
            let name = Path.basename(path)
            if(shouldIgnore(name)) {
                return null
            }
            tree.lastModify = stat.mtime
            return tree
        }
        tree.children = fs
                        .readdirSync(path)
                        .map(fileName => {
                            return walkDir(Path.join(path, fileName), shouldIgnore)
                        })
                        .filter(e => !!e) //!!null === false
        if(tree.children.length === 0) {
            return null
        }
        return tree
    }

    createNewFile(path, stat) {

    }
    
    changeFile(path, stat) {

    }
    
    removeFile(path, stat) {

    }
    
    shouldSync(path) {

    }

    RestoreTree() {
        if(this_._dirTree === null) {
            return
        }
        JSON.stringify(this._dirTree)
    }
}<commit_msg>finish basic operations of directory tree<commit_after>import * as Path from 'path'
import * as fs from 'fs'
/*
DirectoryTree load synccode file or walk through the directory to initialize a tree in memory

*/

export default class DirectoryTree {
    constructor(workDir) {
        this._workDir = workDir
        this._dirTree = null
        this.loadSynccodeFile()
        this.initTree()
    }
    
    loadSynccodeFile() {
        let syncCodeFile = Path.resolve(this._workDir, './.synccode')
        if(!fs.existsSync(syncCodeFile)) {
            return
        }
        try {
            this._dirTree = JSON.parse(fs.readFileSync(syncCodeFile).toString())
        } catch(e) {
            return
        }
    }
    
    initTree() {
        if(this_dirTree === null) {
            this._dirTree = convertDirIntoTree(this._workDir, file => false)
        }
    }
    
    convertDirIntoTree(path, shouldIgnore) {
        let name = Path.basename(path)
        if(shouldIgnore(name)) {
            return null
        }
        let tree = { name }
        let stat
        try {
            stat = fs.statSync(path)
        } catch(e) {
            return null
        }
        if(stat.isFile()) {
            tree.lastModify = stat.mtime
            return tree
        }
        tree.children = {}
        fs
        .readdirSync(path)
        .forEach(fileName => {
            let subtree = convertDirIntoTree(Path.join(path, fileName), shouldIgnore)
            if(subtree !== null) {
                tree.children[fileName] = subtree
            }
        })
        if(this.isEmptyObject(tree.children)) {
            return null
        }
        return tree
    }

    getRelativePath(path) {
        if(path.length <= this._workDir || path.indexOf(this._workDir) === -1) {
            return ""
        }
        return path.substr(this._workDir.length).trim("/")
    }

    resolvePathToArray(path) {
        return this.getRelativePath(path).split("/")
    }

    resolveObjectInTree(path) {
        let pathArr = this.resolvePathToArray(path)
        let tree = this._dirTree
        if(pathArr.length === 0) {
            return [tree, false, 0]
        }
        for(let i = 0; i<pathArr.length; i++) {
            let fileName = pathArr[i]
            if(!tree.children[fileName]) {
                return [tree, false, i]
            }
            tree = tree.children[fileName]
        }
        return [tree, true, i]
    }

    isEmptyObject(obj) {
        for(let key in obj) {
            return false
        }
        return true
    }

    buildFullPathInTree(path, resolvedObject = null) {
        let [tree, ok, depth] = resolvedObject instanceof Array ? resolvedObject : this.resolveObjectInTree(path)
        if(ok) {
            return tree
        }
        let pathArr = this.resolvePathToArray(path)
        //the last param of pathArr must be a file rather than a directory
        for(let i = depth; i < pathArr.length-1; i++) {
            tree.children[pathArr[i]] = { children: {}}
            tree = tree.children[pathArr[i]]
        }
        return tree
    }

    CreateNewFile(path, stat) {
        let tree = this.buildFullPathInTree(path)
        let fileName = Path.basename(path)
        tree.children[fileName] = {
            lastModify: stat.mtime
        }
    }
    
    ChangeFile(path, stat) {
        let [tree, ok, depth] = this.resolveObjectInTree(path)
        if(ok) {
            tree.lastModify = stat.mtime
            return
        }
        this.CreateNewFile(path, stat)
    }
    
    RemoveFile(path, stat) {
        let parentPath = path.substring(0, path.trim('/').lastIndexOf('/'))
        let fileName = Path.basename(path)
        let [tree, ok, depth] = this.resolveObjectInTree(parentPath)
        //In fact the ok must equal true or it is a bug
        if(ok) {
            delete tree.children[fileName]
            if(tree !== this._dirTree && this.isEmptyObject(tree.children)) {
                this.RemoveFile(parentPath)
            }
        }
    }
    
    shouldSync(path) {

    }

    RestoreTree() {
        if(this_._dirTree === null) {
            return
        }
        const content = JSON.stringify(this._dirTree)
        fs.writeFileSync(Path.resolve(this._workDir, './.synccode'), content)
    }
}
{'fraction_non_alphanumeric': 0.10873755220044973, 'fraction_numerical': 0.0011243173787343398, 'mean_word_length': 1.5678350515463917, 'pattern_counts': {'":': 0, '<': 6, '<?xml version=': 0, '>': 8, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4186467', 'n_tokens_mistral': 1815, 'n_tokens_neox': 1792, 'n_words': 374}