alm
Version:
The best IDE for TypeScript
172 lines (171 loc) • 6.84 kB
JavaScript
;
/**
* Manages active project.
* Also pushes errors out to clients + keeps the language service in sync
*/
Object.defineProperty(exports, "__esModule", { value: true });
var utils = require("../../../common/utils");
var project = require("./core/project");
var types = require("../../../common/types");
var tsErrorsCache_1 = require("./cache/tsErrorsCache");
var setErrorsByFilePaths = tsErrorsCache_1.errorsCache.setErrorsByFilePaths, clearErrors = tsErrorsCache_1.errorsCache.clearErrors, clearErrorsForFilePath = tsErrorsCache_1.errorsCache.clearErrorsForFilePath;
var building_1 = require("./modules/building");
var events_1 = require("../../../common/events");
var chalk = require("chalk");
var master;
function setMaster(m) {
master = m;
}
exports.setMaster = setMaster;
/** On working */
exports.working = new events_1.TypedEvent();
/** The active project name */
var activeProjectConfigDetails = null;
/** The currently active project */
var currentProject = null;
/**
* Changes the active project.
* Clear any previously reported errors and recalculate the errors
* This is what the user should call if they want to manually sync as well
*/
function setActiveProjectConfigDetails(projectData) {
initialSync = true;
activeProjectConfigDetails = projectData;
currentProject = new project.Project(projectData);
/** Refresh them errors */
clearErrors();
cancelAnyPendingAnalysisAndMarkforRefreshingAllProjectDiagnostics();
/** Return active project for any chaining */
return currentProject;
}
exports.setActiveProjectConfigDetails = setActiveProjectConfigDetails;
function sync() {
// We need to request new data load from master
master.sync({});
}
/**
* File changing on disk
*/
function fileEdited(evt) {
var proj = GetProject.ifCurrent(evt.filePath);
if (proj) {
evt.edits.forEach(function (edit) {
proj.languageServiceHost.applyCodeEdit(evt.filePath, edit.from, edit.to, edit.newText);
// For debugging
// console.log(proj.languageService.getSourceFile(evt.filePath).text);
// update errors for this file if its *heuristically* small
if (edit.from.line < 1000) {
refreshFileDiagnostics(evt.filePath);
}
// After a while update all project diagnostics as well
cancelAnyPendingAnalysisAndMarkforRefreshingAllProjectDiagnostics();
});
}
}
exports.fileEdited = fileEdited;
function fileChangedOnDisk(evt) {
// Check if its a part of the current project .... if not ignore :)
var proj = GetProject.ifCurrent(evt.filePath);
if (proj) {
proj.languageServiceHost.setContents(evt.filePath, evt.contents);
cancelAnyPendingAnalysisAndMarkforRefreshingAllProjectDiagnostics();
}
}
exports.fileChangedOnDisk = fileChangedOnDisk;
/**
* If there hasn't been a request for a while then we refresh
* As its a bit slow to get *all* the errors
*/
var initialSync = false;
var cancellationToken = null;
var refreshAllProjectDiagnostics = function () {
if (currentProject) {
var timer_1 = utils.timer();
var projectFilePath = currentProject.configFile.projectFilePath;
if (initialSync) {
console.error("[TSC] Started Initial Error Analysis: " + projectFilePath);
}
else {
console.log("[TSC] Incremental Error Analysis " + projectFilePath);
}
// Get all the errors from the project files:
cancellationToken = utils.cancellationToken();
currentProject.getDiagnostics(cancellationToken).then(function (diagnostics) {
console.error('[TSC] Error Analysis Duration:', timer_1.seconds);
var errors = diagnostics.map(building_1.diagnosticToCodeError);
var filePaths = currentProject.getFilePaths();
setErrorsByFilePaths(filePaths, errors);
console.log("[TSC] FileCount: " + filePaths.length + " ", errors.length ? chalk.red("Errors: " + errors.length) : chalk.green("Errors: " + errors.length));
initialSync = false;
exports.working.emit({ working: false });
})
.catch(function (res) {
console.log('[TSC] Cancelled error analysis');
});
}
};
var cancelAnyPendingAnalysisAndMarkforRefreshingAllProjectDiagnostics = function () {
exports.working.emit({ working: true });
if (cancellationToken) {
cancellationToken.cancel();
cancellationToken = null;
}
refreshAllProjectDiagnosticsDebounced();
};
var refreshAllProjectDiagnosticsDebounced = utils.debounce(refreshAllProjectDiagnostics, 3000);
/**
* Constantly streaming this is slow for large files so this is debounced as well
*/
var refreshFileDiagnostics = utils.debounce(function (filePath) {
var proj = GetProject.ifCurrent(filePath);
if (proj) {
var diagnostics = proj.getDiagnosticsForFile(filePath);
var errors = diagnostics.map(building_1.diagnosticToCodeError);
setErrorsByFilePaths([filePath], errors);
}
}, 1000);
/** Get project functions */
var GetProject;
(function (GetProject) {
/**
* Utility function used all the time
*/
function ifCurrent(filePath) {
if (currentProject && currentProject.includesSourceFile(filePath)) {
return currentProject;
}
}
GetProject.ifCurrent = ifCurrent;
/**
* Sometimes (e.g in the projectService) you want to error out
* because these functions should not be called if there is no active project
*/
function ifCurrentOrErrorOut(filePath) {
var proj = ifCurrent(filePath);
if (!proj) {
/** This is happening for invalid cases so added some debug assistance */
var isThereAnActiveProject = !!currentProject;
var filePathsCount = isThereAnActiveProject ? currentProject.getProjectSourceFiles().length : 0;
console.log({
error: types.errors.CALLED_WHEN_NO_ACTIVE_PROJECT_FOR_FILE_PATH,
filePath: filePath,
isThereAnActiveProject: isThereAnActiveProject,
filePathsCount: filePathsCount
});
throw new Error(types.errors.CALLED_WHEN_NO_ACTIVE_PROJECT_FOR_FILE_PATH);
}
return proj;
}
GetProject.ifCurrentOrErrorOut = ifCurrentOrErrorOut;
/**
* Get current if any OR throw
*/
function getCurrentIfAny() {
if (!currentProject) {
console.error(types.errors.CALLED_WHEN_NO_ACTIVE_PROJECT_GLOBAL);
throw new Error(types.errors.CALLED_WHEN_NO_ACTIVE_PROJECT_GLOBAL);
}
return currentProject;
}
GetProject.getCurrentIfAny = getCurrentIfAny;
})(GetProject = exports.GetProject || (exports.GetProject = {}));