зеркало из https://github.com/mozilla/brackets.git
Adding only FileIndexerManager. Separating out QuickFileOpen
This commit is contained in:
Родитель
915c3a663e
Коммит
bdf4d7b008
|
@ -0,0 +1,377 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012 Adobe Systems Incorporated. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*jslint vars: true, plusplus: true, devel: true, browser: true, nomen: true, indent: 4, maxerr: 50 */
|
||||||
|
/*global define: false, $: false, brackets, PathUtils */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Manages a collection of FileIndexes where each index maintains a list of information about
|
||||||
|
* files that meet the criteria specified by the index. The indexes are created lazily when
|
||||||
|
* they are queried and marked dirty when Brackets becomes active.
|
||||||
|
*
|
||||||
|
* TODO (issue 325 ) - FileIndexer doesn't currently add a file to the index when the user createa
|
||||||
|
* a new file within brackets.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
define(function (require, exports, module) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var NativeFileSystem = require("NativeFileSystem").NativeFileSystem,
|
||||||
|
PerfUtils = require("PerfUtils"),
|
||||||
|
ProjectManager = require("ProjectManager"),
|
||||||
|
Strings = require("strings");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All the indexes are stored in this object. The key is the name of the index
|
||||||
|
* and the value is a FileIndex.
|
||||||
|
*/
|
||||||
|
var _indexList = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tracks whether _indexList should be considered dirty and invalid. Calls that access
|
||||||
|
* any data in _indexList should call syncFileIndex prior to accessing the data.
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
var _indexListDirty = true;
|
||||||
|
|
||||||
|
/** class FileIndex
|
||||||
|
*
|
||||||
|
* A FileIndex contains an array of fileInfos that meet the criteria specified by
|
||||||
|
* the filterFunction. FileInfo's in the fileInfo array should unique map to one file.
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @param {!string} indexname
|
||||||
|
* @param {function({!entry})} filterFunction returns true to indicate the entry
|
||||||
|
* should be included in the index
|
||||||
|
*/
|
||||||
|
function FileIndex(indexName, filterFunction) {
|
||||||
|
this.name = indexName;
|
||||||
|
this.fileInfos = [];
|
||||||
|
this.filterFunction = filterFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** class FileInfo
|
||||||
|
*
|
||||||
|
* Class to hold info about a file that a FileIndex wishes to retain.
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @param {!string}
|
||||||
|
*/
|
||||||
|
function FileInfo(entry) {
|
||||||
|
this.name = entry.name;
|
||||||
|
this.fullPath = entry.fullPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new index to _indexList and marks the list dirty
|
||||||
|
*
|
||||||
|
* A future performance optimization is to only build the new index rather than
|
||||||
|
* marking them all dirty
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {!string} indexName must be unque
|
||||||
|
* @param {!function({entry} filterFunction should return true to include an
|
||||||
|
* entry in the index
|
||||||
|
|
||||||
|
*/
|
||||||
|
function _addIndex(indexName, filterFunction) {
|
||||||
|
if (_indexList.hasOwnProperty(indexName)) {
|
||||||
|
throw new Error("Duplicate index name");
|
||||||
|
}
|
||||||
|
if (typeof filterFunction !== "function") {
|
||||||
|
throw new Error("Invalid arguments");
|
||||||
|
}
|
||||||
|
|
||||||
|
_indexList[indexName] = new FileIndex(indexName, filterFunction);
|
||||||
|
|
||||||
|
_indexListDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the entry against the filterFunction for each index and adds
|
||||||
|
* a fileInfo to the index if the entry meets the criteria. FileInfo's are
|
||||||
|
* shared between indexes.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {!entry} entry to be added to the indexes
|
||||||
|
*/
|
||||||
|
// future use when files are incrementally added
|
||||||
|
//
|
||||||
|
function _addFileToIndexes(entry) {
|
||||||
|
|
||||||
|
// skip invisible files on mac
|
||||||
|
if (brackets.platform === "mac" && entry.name.charAt(0) === ".") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileInfo = new FileInfo(entry);
|
||||||
|
//console.log(entry.name);
|
||||||
|
|
||||||
|
$.each(_indexList, function (indexName, index) {
|
||||||
|
if (index.filterFunction(entry)) {
|
||||||
|
index.fileInfos.push(fileInfo);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error dialog when max files in index is hit
|
||||||
|
*/
|
||||||
|
function _showMaxFilesDialog() {
|
||||||
|
return brackets.showModalDialog(
|
||||||
|
brackets.DIALOG_ID_ERROR,
|
||||||
|
Strings.ERROR_MAX_FILES_TITLE,
|
||||||
|
Strings.ERROR_MAX_FILES
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Recursively visits all files that are descendent of dirEntry and adds
|
||||||
|
* files files to each index when the file matches the filter critera
|
||||||
|
* @private
|
||||||
|
* @param {!DirectoryEntry} dirEntry
|
||||||
|
* @returns {$.Promise}
|
||||||
|
*/
|
||||||
|
function _scanDirectorySubTree(dirEntry) {
|
||||||
|
if (!dirEntry) {
|
||||||
|
throw new Error("Bad dirEntry passed to _scanDirectorySubTree");
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep track of directories as they are asynchronously read. We know we are done
|
||||||
|
// when dirInProgress becomes empty again.
|
||||||
|
var state = { fileCount: 0,
|
||||||
|
dirInProgress: {}, // directory names that are in progress of being read
|
||||||
|
dirError: {}, // directory names with read errors. key=dir path, value=error
|
||||||
|
maxFilesHit: false // used to show warning dialog only once
|
||||||
|
};
|
||||||
|
|
||||||
|
var deferred = new $.Deferred();
|
||||||
|
|
||||||
|
// inner helper function
|
||||||
|
function _dirScanDone() {
|
||||||
|
var key;
|
||||||
|
for (key in state.dirInProgress) {
|
||||||
|
if (state.dirInProgress.hasOwnProperty(key)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _finishDirScan(dirEntry) {
|
||||||
|
//console.log("finished: " + dirEntry.fullPath);
|
||||||
|
delete state.dirInProgress[dirEntry.fullPath];
|
||||||
|
|
||||||
|
if (_dirScanDone()) {
|
||||||
|
//console.log("dir scan completly done");
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// inner helper function
|
||||||
|
function _scanDirectoryRecurse(dirEntry) {
|
||||||
|
state.dirInProgress[dirEntry.fullPath] = true;
|
||||||
|
//console.log("started dir: " + dirEntry.fullPath);
|
||||||
|
|
||||||
|
dirEntry.createReader().readEntries(
|
||||||
|
// success callback
|
||||||
|
function (entries) {
|
||||||
|
// inspect all children of dirEntry
|
||||||
|
entries.forEach(function (entry) {
|
||||||
|
if (entry.isFile) {
|
||||||
|
_addFileToIndexes(entry);
|
||||||
|
state.fileCount++;
|
||||||
|
|
||||||
|
// For now limit the number of files that are indexed. This limit could be increased
|
||||||
|
// if files were indexed in a worker thread so scanning didn't block the UI
|
||||||
|
if (state.fileCount === 10000) {
|
||||||
|
if (!state.maxFilesHit) {
|
||||||
|
state.maxFilesHit = true;
|
||||||
|
_showMaxFilesDialog();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (entry.isDirectory) {
|
||||||
|
_scanDirectoryRecurse(entry);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_finishDirScan(dirEntry);
|
||||||
|
},
|
||||||
|
// error callback
|
||||||
|
function (error) {
|
||||||
|
state.dirError[dirEntry.fullPath] = error;
|
||||||
|
_finishDirScan(dirEntry);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_scanDirectoryRecurse(dirEntry);
|
||||||
|
|
||||||
|
return deferred.promise();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// debug
|
||||||
|
function _logFileList(list) {
|
||||||
|
list.forEach(function (fileInfo) {
|
||||||
|
console.log(fileInfo.name);
|
||||||
|
});
|
||||||
|
console.log("length: " + list.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the fileInfo array for all the indexes in _indexList
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function _clearIndexes() {
|
||||||
|
$.each(_indexList, function (indexName, index) {
|
||||||
|
index.fileInfos = [];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Markes all file indexes dirty
|
||||||
|
*/
|
||||||
|
function markDirty() {
|
||||||
|
_indexListDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by syncFileIndex function to prevent reentrancy
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
var _syncFileIndexReentracyGuard = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears and rebuilds all of the fileIndexes and sets _indexListDirty to false
|
||||||
|
* @return {$.Promise} resolved when index has been updated
|
||||||
|
*/
|
||||||
|
function syncFileIndex() {
|
||||||
|
if (_syncFileIndexReentracyGuard) {
|
||||||
|
throw new Error("syncFileIndex cannot be called Recursively");
|
||||||
|
}
|
||||||
|
|
||||||
|
_syncFileIndexReentracyGuard = true;
|
||||||
|
|
||||||
|
var rootDir = ProjectManager.getProjectRoot();
|
||||||
|
if (_indexListDirty) {
|
||||||
|
PerfUtils.markStart("FileIndexManager.syncFileIndex(): " + rootDir.fullPath);
|
||||||
|
|
||||||
|
_clearIndexes();
|
||||||
|
|
||||||
|
return _scanDirectorySubTree(rootDir)
|
||||||
|
.done(function () {
|
||||||
|
PerfUtils.addMeasurement("FileIndexManager.syncFileIndex(): " + rootDir.fullPath);
|
||||||
|
_indexListDirty = false;
|
||||||
|
_syncFileIndexReentracyGuard = false;
|
||||||
|
|
||||||
|
//_logFileList(_indexList["all"].fileInfos);
|
||||||
|
//_logFileList(_indexList["css"].fileInfos);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
_syncFileIndexReentracyGuard = false;
|
||||||
|
return $.Deferred().resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the FileInfo array for the specified index
|
||||||
|
* @param {!string} indexname
|
||||||
|
* @return {$.Promise} a promise that is resolved with an Array of FileInfo's
|
||||||
|
*/
|
||||||
|
function getFileInfoList(indexName) {
|
||||||
|
var result = new $.Deferred();
|
||||||
|
|
||||||
|
if (!_indexList.hasOwnProperty(indexName)) {
|
||||||
|
throw new Error("indexName not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
syncFileIndex()
|
||||||
|
.done(function () {
|
||||||
|
result.resolve(_indexList[indexName].fileInfos);
|
||||||
|
});
|
||||||
|
|
||||||
|
return result.promise();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls the filterFunction on every in the index specified by indexName
|
||||||
|
* and return a a new list of FileInfo's
|
||||||
|
* @param {!string}
|
||||||
|
* @param {function({string})} filterFunction
|
||||||
|
* @return {$.Promise} a promise that is resolved with an Array of FileInfo's
|
||||||
|
*/
|
||||||
|
function getFilteredList(indexName, filterFunction) {
|
||||||
|
var result = new $.Deferred();
|
||||||
|
|
||||||
|
if (!_indexList.hasOwnProperty(indexName)) {
|
||||||
|
throw new Error("indexName not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
syncFileIndex()
|
||||||
|
.done(function () {
|
||||||
|
var resultList = [];
|
||||||
|
getFileInfoList(indexName)
|
||||||
|
.done(function (fileList) {
|
||||||
|
resultList = fileList.filter(function (fileInfo) {
|
||||||
|
return filterFunction(fileInfo.name);
|
||||||
|
});
|
||||||
|
|
||||||
|
result.resolve(resultList);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return result.promise();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns an array of fileInfo's that match the filename parameter
|
||||||
|
* @param {!string} indexName
|
||||||
|
* @param {!filename}
|
||||||
|
* @return {$.Promise} a promise that is resolved with an Array of FileInfo's
|
||||||
|
*/
|
||||||
|
function getFilenameMatches(indexName, filename) {
|
||||||
|
return getFilteredList(indexName, function (item) {
|
||||||
|
return item === filename;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the indexes
|
||||||
|
*/
|
||||||
|
|
||||||
|
_addIndex(
|
||||||
|
"all",
|
||||||
|
function (entry) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
_addIndex(
|
||||||
|
"css",
|
||||||
|
function (entry) {
|
||||||
|
var filename = entry.name;
|
||||||
|
return PathUtils.filenameExtension(filename) === ".css";
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$(ProjectManager).on("projectRootChanged", function (event, projectRoot) {
|
||||||
|
markDirty();
|
||||||
|
});
|
||||||
|
|
||||||
|
exports.markDirty = markDirty;
|
||||||
|
exports.getFileInfoList = getFileInfoList;
|
||||||
|
exports.getFilenameMatches = getFilenameMatches;
|
||||||
|
|
||||||
|
|
||||||
|
});
|
|
@ -10,9 +10,10 @@
|
||||||
* creating and updating the project tree when projects are opened and when changes occur to
|
* creating and updating the project tree when projects are opened and when changes occur to
|
||||||
* the file tree.
|
* the file tree.
|
||||||
*
|
*
|
||||||
* This module dispatches 1 event:
|
* This module dispatches these events:
|
||||||
* - initializeComplete -- When the ProjectManager initializes the first
|
* - initializeComplete -- When the ProjectManager initializes the first
|
||||||
* project at application start-up.
|
* project at application start-up.
|
||||||
|
* - projectRootChanged -- when _projectRoot changes
|
||||||
*
|
*
|
||||||
* These are jQuery events, so to listen for them you do something like this:
|
* These are jQuery events, so to listen for them you do something like this:
|
||||||
* $(ProjectManager).on("eventname", handler);
|
* $(ProjectManager).on("eventname", handler);
|
||||||
|
@ -409,6 +410,9 @@ define(function (require, exports, module) {
|
||||||
// Point at a real folder structure on local disk
|
// Point at a real folder structure on local disk
|
||||||
NativeFileSystem.requestNativeFileSystem(rootPath,
|
NativeFileSystem.requestNativeFileSystem(rootPath,
|
||||||
function (rootEntry) {
|
function (rootEntry) {
|
||||||
|
var projectRootChanged = (!_projectRoot || !rootEntry)
|
||||||
|
|| _projectRoot.fullPath !== rootEntry.fullPath;
|
||||||
|
|
||||||
// Success!
|
// Success!
|
||||||
_projectRoot = rootEntry;
|
_projectRoot = rootEntry;
|
||||||
|
|
||||||
|
@ -426,6 +430,10 @@ define(function (require, exports, module) {
|
||||||
if (isFirstProjectOpen) {
|
if (isFirstProjectOpen) {
|
||||||
$(exports).triggerHandler("initializeComplete", _projectRoot);
|
$(exports).triggerHandler("initializeComplete", _projectRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (projectRootChanged) {
|
||||||
|
$(exports).triggerHandler("projectRootChanged", _projectRoot);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
resultRenderTree.fail(function () {
|
resultRenderTree.fail(function () {
|
||||||
result.reject();
|
result.reject();
|
||||||
|
|
|
@ -57,6 +57,10 @@ define(function (require, exports, module) {
|
||||||
exports.ERROR_CREATING_FILE_TITLE = "Error creating file";
|
exports.ERROR_CREATING_FILE_TITLE = "Error creating file";
|
||||||
exports.ERROR_CREATING_FILE = "An error occurred when trying to create the file \"{0}\". {1}";
|
exports.ERROR_CREATING_FILE = "An error occurred when trying to create the file \"{0}\". {1}";
|
||||||
|
|
||||||
|
// FileIndexManager error string
|
||||||
|
exports.ERROR_MAX_FILES_TITLE = "Error Indexing Files";
|
||||||
|
exports.ERROR_MAX_FILES = "The maximum number of files have been indexed. Actions that look up files in the index may function incorrectly.";
|
||||||
|
|
||||||
exports.SAVE_CLOSE_TITLE = "Save Changes";
|
exports.SAVE_CLOSE_TITLE = "Save Changes";
|
||||||
exports.SAVE_CLOSE_MESSAGE = "Do you want to save the changes you made in the document \"{0}\"?";
|
exports.SAVE_CLOSE_MESSAGE = "Do you want to save the changes you made in the document \"{0}\"?";
|
||||||
exports.SAVE_CLOSE_MULTI_MESSAGE = "Do you want to save your changes to the following files?";
|
exports.SAVE_CLOSE_MULTI_MESSAGE = "Do you want to save your changes to the following files?";
|
||||||
|
|
|
@ -24,6 +24,7 @@ define(function (require, exports, module) {
|
||||||
require("spec/ProjectManager-test.js");
|
require("spec/ProjectManager-test.js");
|
||||||
require("spec/WorkingSetView-test.js");
|
require("spec/WorkingSetView-test.js");
|
||||||
require("spec/KeyMap-test.js");
|
require("spec/KeyMap-test.js");
|
||||||
|
require("spec/FileIndexManager-test.js");
|
||||||
require("spec/CodeHintUtils-test.js");
|
require("spec/CodeHintUtils-test.js");
|
||||||
require("spec/CSSManager-test.js");
|
require("spec/CSSManager-test.js");
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче