UNPKG

alm

Version:

The best IDE for TypeScript

172 lines (171 loc) 6.84 kB
"use strict"; /** * 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 = {}));