Skip to content
This repository has been archived by the owner on Sep 6, 2021. It is now read-only.

Expose code inspection providers for the extensions. #5125

Merged
merged 1 commit into from
Sep 27, 2013
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 116 additions & 49 deletions src/language/CodeInspection.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ define(function (require, exports, module) {
CommandManager = require("command/CommandManager"),
DocumentManager = require("document/DocumentManager"),
EditorManager = require("editor/EditorManager"),
FileUtils = require("file/FileUtils"),
LanguageManager = require("language/LanguageManager"),
PreferencesManager = require("preferences/PreferencesManager"),
PerfUtils = require("utils/PerfUtils"),
Expand Down Expand Up @@ -72,7 +73,6 @@ define(function (require, exports, module) {
META: "problem_type_meta"
};


/**
* @private
* @type {PreferenceStorage}
Expand Down Expand Up @@ -100,6 +100,12 @@ define(function (require, exports, module) {
*/
var $problemsPanel;

/**
* @private
* @type {$.Element}
*/
var $problemsPanelTable;

/**
* @private
* @type {boolean}
Expand Down Expand Up @@ -145,7 +151,69 @@ define(function (require, exports, module) {
}
_providers[languageId] = provider;
}


/**
* Returns a provider for given file path, if one is available.
* Decision is made depending on the file extension.
*
* @param {!string} filePath
* @return ?{{name:string, scanFile:function(string, string):?{!errors:Array, aborted:boolean}} provider
*/
function getProviderForPath(filePath) {
return _providers[LanguageManager.getLanguageForPath(filePath).getId()];
}

/**
* Runs a file inspection over passed file, specifying a provider is optional.
* This method doesn't update the Brackets UI, just provides inspection results.
* These results will reflect any unsaved changes present in the file that is currently opened.
*
* @param {!FileEntry} fileEntry File that will be inspected for errors.
* @param ?{{name:string, scanFile:function(string, string):?{!errors:Array, aborted:boolean}} provider
* @return {$.Promise} a jQuery promise that will be resolved with ?{!errors:Array, aborted:boolean}
*/
function inspectFile(fileEntry, provider) {
var response = new $.Deferred();
provider = provider || getProviderForPath(fileEntry.fullPath);

if (!provider) {
response.resolve(null);
return response.promise();
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

run() needs a way to distinguish between no provider vs. there is a provider, but it found no problems. I wonder if we should disallow providers from returning null? (Making them return { errors: [] } instead -- but we'd want to enforce that they can't return null, or else lots of them probably will...). Or we could return a special token for the no-provider case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added console.warn for this for now. Distinguish between no provider and provider is done via using the getProvider method so run() logic stays the same.


var doc = DocumentManager.getOpenDocumentForPath(fileEntry.fullPath),
fileTextPromise;

if (doc) {
fileTextPromise = new $.Deferred().resolve(doc.getText());
} else {
fileTextPromise = FileUtils.readAsText(fileEntry);
}

fileTextPromise
.done(function (fileText) {
var result,
perfTimerInspector = PerfUtils.markStart("CodeInspection '" + provider.name + "':\t" + fileEntry.fullPath);

try {
result = provider.scanFile(fileText, fileEntry.fullPath);
} catch (err) {
console.error("[CodeInspection] Provider " + provider.name + " threw an error: " + err);
response.reject(err);
return;
}

PerfUtils.addMeasurement(perfTimerInspector);
response.resolve(result);
})
.fail(function (err) {
console.error("[CodeInspection] Could not read file for inspection: " + fileEntry.fullPath);
response.reject(err);
});

return response.promise();
}

/**
* Run inspector applicable to current document. Updates status bar indicator and refreshes error list in
* bottom panel.
Expand All @@ -159,30 +227,31 @@ define(function (require, exports, module) {
return;
}

var currentDoc = DocumentManager.getCurrentDocument();

var perfTimerDOM,
perfTimerInspector;

var language = currentDoc ? LanguageManager.getLanguageForPath(currentDoc.file.fullPath) : "";
var languageId = language && language.getId();
var provider = language && _providers[languageId];
var currentDoc = DocumentManager.getCurrentDocument(),
provider = currentDoc && getProviderForPath(currentDoc.file.fullPath);

if (provider) {
perfTimerInspector = PerfUtils.markStart("CodeInspection '" + languageId + "':\t" + currentDoc.file.fullPath);

var result = provider.scanFile(currentDoc.getText(), currentDoc.file.fullPath);
_lastResult = result;

PerfUtils.addMeasurement(perfTimerInspector);
perfTimerDOM = PerfUtils.markStart("ProblemsPanel render:\t" + currentDoc.file.fullPath);

if (result && result.errors.length) {
inspectFile(currentDoc.file, provider).then(function (result) {
// check if current document wasn't changed while inspectFile was running
if (currentDoc !== DocumentManager.getCurrentDocument()) {
return;
}

_lastResult = result;

if (!result || !result.errors.length) {
Resizer.hide($problemsPanel);
StatusBar.updateIndicator(INDICATOR_ID, true, "inspection-valid", StringUtils.format(Strings.NO_ERRORS, provider.name));
setGotoEnabled(false);
return;
}

var perfTimerDOM = PerfUtils.markStart("ProblemsPanel render:\t" + currentDoc.file.fullPath);

// Augment error objects with additional fields needed by Mustache template
var numProblems = 0;
result.errors.forEach(function (error) {
error.friendlyLine = error.pos.line + 1;

error.codeSnippet = currentDoc.getLine(error.pos.line);
error.codeSnippet = error.codeSnippet.substr(0, Math.min(175, error.codeSnippet.length)); // limit snippet width

Expand All @@ -193,27 +262,11 @@ define(function (require, exports, module) {

// Update results table
var html = Mustache.render(ResultsTemplate, {reportList: result.errors});
var $selectedRow;

$problemsPanel.find(".table-container")
$problemsPanelTable
.empty()
.append(html)
.scrollTop(0) // otherwise scroll pos from previous contents is remembered
.on("click", "tr", function (e) {
if ($selectedRow) {
$selectedRow.removeClass("selected");
}

$selectedRow = $(e.currentTarget);
$selectedRow.addClass("selected");
var lineTd = $selectedRow.find(".line-number");
var line = parseInt(lineTd.text(), 10) - 1; // convert friendlyLine back to pos.line
var character = lineTd.data("character");

var editor = EditorManager.getCurrentFullEditor();
editor.setCursorPos(line, character, true);
EditorManager.focusEditor();
});
.scrollTop(0); // otherwise scroll pos from previous contents is remembered

$problemsPanel.find(".title").text(StringUtils.format(Strings.ERRORS_PANEL_TITLE, provider.name));
if (!_collapsed) {
Expand All @@ -231,19 +284,14 @@ define(function (require, exports, module) {
StringUtils.format(Strings.MULTIPLE_ERRORS, provider.name, numProblems));
}
setGotoEnabled(true);

} else {
Resizer.hide($problemsPanel);
StatusBar.updateIndicator(INDICATOR_ID, true, "inspection-valid", StringUtils.format(Strings.NO_ERRORS, provider.name));
setGotoEnabled(false);
}

PerfUtils.addMeasurement(perfTimerDOM);

PerfUtils.addMeasurement(perfTimerDOM);
});
} else {
// No provider for current file
_lastResult = null;
Resizer.hide($problemsPanel);
var language = currentDoc && LanguageManager.getLanguageForPath(currentDoc.file.fullPath);
if (language) {
StatusBar.updateIndicator(INDICATOR_ID, true, "inspection-disabled", StringUtils.format(Strings.NO_LINT_AVAILABLE, language.getName()));
} else {
Expand Down Expand Up @@ -339,6 +387,24 @@ define(function (require, exports, module) {
var resultsPanel = PanelManager.createBottomPanel("errors", $(panelHtml), 100);
$problemsPanel = $("#problems-panel");

var $selectedRow;
$problemsPanelTable = $problemsPanel.find(".table-container")
.on("click", "tr", function (e) {
if ($selectedRow) {
$selectedRow.removeClass("selected");
}

$selectedRow = $(e.currentTarget);
$selectedRow.addClass("selected");
var lineTd = $selectedRow.find(".line-number");
var line = parseInt(lineTd.text(), 10) - 1; // convert friendlyLine back to pos.line
var character = lineTd.data("character");

var editor = EditorManager.getCurrentFullEditor();
editor.setCursorPos(line, character, true);
EditorManager.focusEditor();
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch with this btw!


$("#problems-panel .close").click(function () {
toggleCollapsed(true);
});
Expand All @@ -363,7 +429,8 @@ define(function (require, exports, module) {


// Public API
exports.register = register;
exports.Type = Type;
exports.toggleEnabled = toggleEnabled;
exports.register = register;
exports.Type = Type;
exports.toggleEnabled = toggleEnabled;
exports.inspectFile = inspectFile;
});