Skip to content

Instantly share code, notes, and snippets.

@ferenczy
Last active May 29, 2024 21:09
Show Gist options
  • Save ferenczy/040cd74c22f44a46e093575b5e1e2862 to your computer and use it in GitHub Desktop.
Save ferenczy/040cd74c22f44a46e093575b5e1e2862 to your computer and use it in GitHub Desktop.
Get lost unsaved buffers in Atom editor: Load text content of all unsaved buffers stored by Atom for any project and print it to Developers tools console
/**
* Get lost unsaved buffers in Atom editor
*
* Load text content of all unsaved buffers stored by Atom for any project in IndexedDB and print it to Developers tools console
*
* Author: David Ferenczy Rogožan (https://ferenczy.cz)
*
* Instructions:
*
* 1. Start Atom
* 2. Open Developer tools (Ctrl + Shift + I)
* 3. Open the tab _Sources_ and in the left panel, open the tab _Snippets_
* 4. Click _New snippet_
* 5. Give it whatever name and paste this file into the snippet
* 6. Press Ctrl + Enter to run it
* 7. The console should contain all stored unsaved buffer content, you can copy&paste it directly from there or open the context menu of the console content and use _Save as..._ to store it in a file
*
* Tested with Atom 1.40.1 x64 on Windows 7
*
* Resources:
* - https://developers.google.com/web/tools/chrome-devtools/storage/indexeddb#edit
* - https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB#Using_a_cursor
*
**/
// false to log only stored buffer content
var DEBUG_OUTPUT = false;
var OBJECT_STORE_NAME = 'states';
var CONTENT_STYLE = 'border: 1px solid red; background-color: #FEE; padding: 1em';
var connection = indexedDB.open('AtomEnvironments', 1);
connection.onerror = function(e) {
console.log('Error opening IndexedDB: ' + e.target.errorCode);
console.log(e);
};
connection.onsuccess = (e) => {
debugLog('Connected to the IndexedDB');
// get handle to the Atom's obbject store `states`
var db = e.target.result;
var objectStore = db.transaction([OBJECT_STORE_NAME]).objectStore(OBJECT_STORE_NAME);
// get cursor to iterate over all items in the `states` object
objectStore.openCursor().onsuccess = (e) => {
var cursor = e.target.result;
if (cursor) {
debugLog('Key: ' + cursor.key);
// get actuall value of the item
var value = cursor.value;
debugLog('Strored at: ' + value.storedAt, 1);
// we're looking for `buffers` which are stored under the field `value.project`
var project =
value.value &&
value.value.project || false;
if (project) {
var paths = project.paths || [];
var buffers = project.buffers || [];
debugLog('Paths: ' + paths.join(', '), 1);
debugLog(buffers.length ? 'Buffers: ' + buffers.length : '[No buffers]', 1);
// iterate through `project`'s buffers
buffers.forEach((entry, i) => {
debugLog('Buffer #' + (i + 1), 1);
// unsaved buffers obviously don't have any `filePath` set
// saved buffers have `filePath` filled but don't have any buffer content since it's stored in the actual file
// files with unsaved changes will probably have both `filePath` and buffer content filled
var filePath = entry.filePath || '[unsaved]';
debugLog('File path: ' + filePath, 2);
var text = entry.text || false;
if (text) {
debugLog('Content:', 2);
// log stored buffer text content
if (DEBUG_OUTPUT) {
// log buffer content synchronized with other logging
console.log('%c' + text, CONTENT_STYLE);
}
else {
// use timer so filename and current line is not logged to console
setTimeout(console.log.bind(console, '\n%c' + text, CONTENT_STYLE));
}
}
else {
debugLog('[no content stored]', 2);
}
});
}
// iterate to the next item
cursor.continue();
}
}
};
function debugLog(text, indent = 0) {
if (DEBUG_OUTPUT) {
text = ' '.repeat(indent * 4) + text;
console.log(text);
}
}
debugLog('Done.');
@ferenczy
Copy link
Author

I'm glad it helped you guys! It took me quite long time to investigate where and how it's being stored, so I didn't want to keep it for myself. :) Then, it was quite easy to write a simple script.

@SnoringFrog
Copy link

First off thank you for this, definitely wouldn't have gotten my data back without it.

Not sure if this is something that changed after you wrote this, or just got missed because the focus here was on totally unsaved buffers, but previously saved files with unsaved changes don't use the "text" field, they use "outstandingChanges", which is an array of ascii codes for the changes.

I didn't come up with the cleanest method for getting these, but I replaced lines 78-95 with:

if (filePath.includes("part of file name you are looking for")) {
                        output="";
                        entry.outstandingChanges.forEach((e,ii) => {
                            if (e == "0") {
                                output = output + " ";
                            } else {
                                output = output + String.fromCharCode(e);
                            }
                        });
                        console.log(output);
                    }

Replacing "part of file name you are looking for" with part of the path to the file with unsaved changes. It won't show deletions well, and some of the changes to my file seemed listed out of order, but it got me the bulk of what I was missing, so hopefully this helps someone else.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment