Skip to content
| Marketplace
Sign in
Visual Studio Code>Other>version482New to Visual Studio Code? Get it now.
version482

version482

pmchen

|
412 installs
| (0) | Free
Keeps versions of files in EECS 482 github repos
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info
//
// Keep versions of files in EECS 482 github repos
//
const vscode = require('vscode');
const os = require('os');
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const child_process = require('child_process');
const version = 'vscode-20250913';
const minTimeInterval = 1;      // minimum time between versions
const username = os.userInfo().username.replace(/[^a-zA-Z0-9]/g, "");

let hash = {};          // last hash value for each version file
let size = {};          // last size for each version file
let prior = {};         // prior contents for each file
let priorTime = {};     // last time each file was written

let sessionStart = time();
let hashInitial = crypto.createHash('sha256').update(username + ' ' + sessionStart.toString());

let versionDir = {};    // .version482 directory name for each source file directory
let timer = {};

exports.activate = () => {
    vscode.workspace.textDocuments.forEach(doc => initFile(doc));
    vscode.workspace.onDidOpenTextDocument(doc => {
        initFile(doc);
    });
    vscode.workspace.onDidSaveTextDocument(doc => {
        textChanged(doc);
    });
    vscode.workspace.onDidChangeTextDocument(e => {
        textChanged(e.document);
    });
}

// return current time in seconds
function time() {
    return(Math.trunc(Date.now()/1000));
}

// See if directory is in an EECS 482 git repo.
// Store .version482 directory name in versionDir, creating the .version482
// directory if needed.  If the directory isn't in a git repo, then set
// the versionDir entry to the empty string.
function initVersionDir(dir) {
    if (! (dir in versionDir)) {
        let dirFound = "";
        if (fs.existsSync(dir)) {
            dirFound = dir;
        } else {
            // Windows VS Code may prepend extra directory components when WSL
            // files are opened as local files).  Fix this by removing directory
            // components from the beginning of the directory name until I find a
            // directory that exists.

            // Parse the directory name into components.
            let dirRoot = dir;
            let dirRootLast;
            let basename = "";
            let components = [];
            do {
                dirRootLast = dirRoot;
                basename = path.basename(dirRoot);
                if (basename != "") {
                    components.unshift(basename);
                    dirRoot = path.dirname(dirRoot);
                }
            } while (basename != "" && dirRoot != dirRootLast);

            // Look for the directory, removing prefixes if needed.
            for (let i=0; i<components.length && dirFound == ""; i++) {
                const dir1 = dirRoot + components.slice(i).join(path.sep);
                if (fs.existsSync(dir1)) {
                    dirFound = dir1;
                }
            }
        }

        if (dirFound == "") {
            versionDir[dir] = '';
        } else {
            try {
                const stdout = child_process.execSync('cd "' + dirFound + '"; git remote -v 2> /dev/null');
                if (stdout.indexOf('eecs482') < 0) {
                    // not in an EECS 482 repo
                    versionDir[dir] = '';
                } else {
                    // this will throw an exception if git rev-parse fails,
                    // e.g., if the directory is not in a work tree
                    versionDir[dir] = child_process.execSync('cd "' + dirFound + '"; git rev-parse --show-toplevel').toString().trimEnd() + path.sep + '.version482';

                    // create the .version482 directory if needed
                    try {
                        if (! fs.statSync(versionDir[dir]).isDirectory) {
                            versionDir[dir] = '';
                        }
                    } catch (err) {
                        fs.mkdirSync(versionDir[dir]);
                    }
                }
            } catch (err) {
                versionDir[dir] = '';
            }
        }
    }
}

function initFile(doc) {
    initVersionDir(path.dirname(doc.fileName));
}

function textChanged(doc) {
    let now = time();

    // Limit the rate of versioning events.  Also save events where time has
    // gone backward by more than minTimeInterval.
    if (doc.fileName in priorTime
            && Math.abs(now - priorTime[doc.fileName]) < minTimeInterval) {
        // replace any pending timer event for this file, so events don't pile up
        if (doc.fileName in timer) {
            clearTimeout(timer[doc.fileName]);
        }
        // make sure this version is eventually saved
        timer[doc.fileName] = setTimeout(textChanged, minTimeInterval*1000, doc);
        return;
    }

    initVersionDir(path.dirname(doc.fileName));

    // make sure file is in an EECS 482 git repo
    if (versionDir[path.dirname(doc.fileName)] == '') {
        return;
    }

    // make sure file is a program source file, i.e., has extension {cpp,cc,h,hpp,py}
    const ext = path.extname(doc.fileName);
    if (ext != '.cpp' && ext != '.cc' && ext != '.h' && ext != '.hpp' && ext != '.py') {
        return;
    }

    // make sure file isn't too big
    if (doc.getText().length > 10 * 1024 * 1024) {
        return;
    }

    const versionDirname = versionDir[path.dirname(doc.fileName)];

    // check if version file has grown too old or too big
    if (now - sessionStart > 60*60 || (versionDirname in size && size[versionDirname] > 50 * 1024 * 1024)) {
        // start new version file by mimicking restarting vscode
        hash = {};
        size = {};
        prior = {};
        priorTime = {};
        sessionStart = now;
        hashInitial = crypto.createHash('sha256').update(username + ' ' + sessionStart.toString());
    }

    if (! (versionDirname in hash)) {
        hash[versionDirname] = hashInitial.copy();
    }
    if (! (versionDirname in size)) {
        size[versionDirname] = 0;
    }

    const priorName = versionDirname + path.sep + sessionStart.toString() + '.' + username + '.prior';
    const currentName = versionDirname + path.sep + sessionStart.toString() + '.' + username + '.current';

    if (! (doc.fileName in prior)) {
        prior[doc.fileName] = '';
    }

    fs.writeFileSync(priorName, prior[doc.fileName]);

    const current = doc.getText();
    fs.writeFileSync(currentName, current);
    const versionfile = versionDirname + path.sep + sessionStart.toString() + '.' + username;
    const diff = child_process.execSync('diff "' + priorName + '" "' + currentName + '"; rm "' + priorName + '" "' + currentName + '"').toString();
    let dict = {};
    dict['file'] = doc.fileName;
    dict['diff'] = diff;
    const line = version + ' ' + now + ' '
		    + size[versionDirname] + ' '
		    + hash[versionDirname].copy().digest('hex') + ' '
		    + JSON.stringify(dict);
    try {
	fs.appendFileSync(versionfile, line + "\n", () => {});
    } catch (err) {
	return;
    }
    prior[doc.fileName] = current;
    priorTime[doc.fileName] = now;

    hash[versionDirname] = crypto.createHash('sha256').update(line);
    size[versionDirname] += line.length + 1;
}
  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
© 2025 Microsoft