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

Use .jshintrc relative to file being hinted. #39

Merged
merged 8 commits into from
Jun 26, 2014
Merged
Show file tree
Hide file tree
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
3 changes: 2 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ module.exports = {
configDefaults: {
hintOnSave: true,
hintOnModify: true,
showErrorPanel: true
showErrorPanel: true,
watchConfig: true
},
activate: function(){
return this.atomJsHint = new AtomJsHint();
Expand Down
220 changes: 163 additions & 57 deletions lib/atom-jshint.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,53 +14,91 @@ module.exports = AtomJshint = (function(){
});
var $ = require('atom').$;
function AtomJsHint(){
atom.workspace.eachEditor((function(self) {
return function(editor) {
return self.handleEvents(editor);
};
})(this));
this.config = {globals:{},options:{}};
this.loadConfig();
this.configs = {};
this.currentConfig = null;
this.npmConfig = this.loadNpmConfig();
this.subscribe(atom.workspaceView, 'pane-container:active-pane-item-changed',
function(event,editor){
this.unsubscribeEditor(this.editor);
this.currentConfig = null;
if (editor.constructor.name === 'Editor'){
this.editor = editor;
this.subscribeEditor(editor);
this.run(editor);
} else {
this.resetState();
}
}.bind(this)
);
this.editor = atom.workspace.getActiveEditor();
if(this.editor){
setTimeout(function(){
this.subscribeEditor(this.editor);
this.run(this.editor);
}.bind(this), 1000);
}
}
Subscriber.includeInto(AtomJsHint);

AtomJsHint.prototype.destroy = function(){
this.configs.forEach(function(config){
if (config.fileWatcher){
config.fileWatcher.close();
delete config.fileWatcher;
}
});
if (this.editor) {
this.unsubscribeEditor(this.editor);
}
return this.unsubscribe();
};

AtomJsHint.prototype.handleEvents = function(editor) {
AtomJsHint.prototype.subscribeEditor = function(editor) {
var buffer = editor.getBuffer();
var listenFor = [];
if( atom.config.get('atom-jshint.hintOnModify') ){ listenFor.push('contents-modified'); }
if( atom.config.get('atom-jshint.hintOnSave') ) { listenFor.push('saved'); }

this.subscribe(atom.workspaceView, 'pane-container:active-pane-item-changed', function(){
this.subscribe(editor, 'grammar-changed', function(){
this.unsubscribeEditor(editor);
this.subscribeEditor(editor);
this.run(editor);
}.bind(this));
this.subscribe(buffer, listenFor.join(' '), (function(self) {
return _.debounce(function() {
self.run(editor);
},50);
})(this));

if(this.isHintable(editor)) {
var listenFor = [];
if( atom.config.get('atom-jshint.hintOnModify') ){ listenFor.push('contents-modified'); }
if( atom.config.get('atom-jshint.hintOnSave') ) { listenFor.push('saved'); }

this.subscribe(buffer, listenFor.join(' '), (function(self) {
return _.debounce(function() {
self.run(editor);
},50);
})(this));
}

this.subscribe(buffer, 'destroyed', (function(self) {
return function() {
return self.unsubscribe(buffer);
return self.unsubscribeEditor(editor);
};
})(this));
};

AtomJsHint.prototype.unsubscribeEditor = function(editor) {
if (editor){
delete this.editor;
return this.unsubscribe(editor) && this.unsubscribe(editor.getBuffer());
}
};

AtomJsHint.prototype.run = function(editor){
var self = this;
var text = this.getContents();
var text = this.getContents(editor);
if( !text ) {
this.resetState(editor);
this.resetState();
return;
}

var cb = function (jsHintErrors) {
JSHINT_Worker.removeListener('message', cb);
if(jsHintErrors.length === 0) {
self.resetState(editor);
self.resetState();
}
if( editor.cursors[0] ){
self.updateStatus(jsHintErrors, editor.cursors[0].getBufferRow());
Expand All @@ -78,20 +116,27 @@ module.exports = AtomJshint = (function(){
};
JSHINT_Worker.on('message', cb);

JSHINT_Worker.send({
method: 'run',
text:text,
options: this.config.options,
config: this.config.globals
});
var config = this.getConfig(editor.getPath());
if (config.message) {
JSHINT_Worker.send({
method: 'run',
text:text,
options: config.message.options,
config: config.message.globals
});
} else if (config.error) {
this.resetState();
this.updateStatus(config.error);
} else {
this.resetState();
}
};

AtomJsHint.prototype.resetState = function(editor){
AtomJsHint.prototype.resetState = function(){
this.updateStatus(false);
this.updateGutter([]);
this.updatePane([]);
atom.workspaceView.off('cursor:moved');
this.unsubscribe(editor);
};

AtomJsHint.prototype.updatePane = function(errors){
Expand Down Expand Up @@ -127,56 +172,117 @@ module.exports = AtomJshint = (function(){
return error.line === row + 1;
});
if (lineErrors.length > 0) {
msg = 'Error: ' + lineErrors[0].line + ':' + lineErrors[0].character + ' ' + lineErrors[0].reason;
msg = 'Error: ' + lineErrors[0].line +
':' + lineErrors[0].character +
' ' + lineErrors[0].reason;
} else {
msg = errors.length > 0 ? errors.length + ' JSHint error' + (errors.length>1?'s':'') : '';
msg = errors.length > 0 ? errors.length +
' JSHint error' + (errors.length>1?'s':'') : '';
}
atom.workspaceView.statusBar.appendLeft('<span id="jshint-status" class="inline-block">' + msg + '</span>');
} else if (typeof errors === 'string') {
msg = errors;
}
if (msg) {
atom.workspaceView.statusBar.appendLeft(
'<span id="jshint-status" class="inline-block">' + msg + '</span>');
}
};


AtomJsHint.prototype.getContents = function(){
if( !atom.workspace.activePaneItem ) return false;
var filename = atom.workspace.activePaneItem.getUri();
if( !filename ) return false;
if( filename.slice(-3) !== '.js' ) return false;
var text = atom.workspace.activePaneItem.getText();
AtomJsHint.prototype.isHintable = function(editor){
if( !editor ) return false;
var grammar = editor.getGrammar();
if (!grammar || grammar.name !== 'JavaScript') return false;
return true;
};
AtomJsHint.prototype.getContents = function(editor){
if(!this.isHintable(editor)) return false;
var text = editor.getText();
if( !text ) return false;
return text;
};

AtomJsHint.prototype.loadConfig = function(){
AtomJsHint.prototype.loadNpmConfig = function(){
if( fs.existsSync(atom.project.path + '/package.json') ){
var packageJson = require(atom.project.path + '/package.json');
if( packageJson.jshintConfig ) {
return this.setConfig(packageJson.jshintConfig);
return this.getConfigMessage(packageJson.jshintConfig);
}
}
var workingPath = atom.project.path || '';
var dirs = workingPath.split(path.sep);
while ( dirs.length >= 1 ) {
var configPath = path.join(dirs.join(path.sep), '/.jshintrc');
if( fs.existsSync(configPath) ){
var configFile = fs.readFileSync(configPath, 'UTF8');
var conf = {};
try {
conf = JSON.parse(configFile);
} catch(e){
console.error('error parsing config file');
};

AtomJsHint.prototype.getConfig = function(filePath){
function loadConfig(filePath) {
var workingPath = filePath || '';
var dirs = workingPath.split(path.sep);
dirs.pop();
while ( dirs.length >= 1 ) {
var dir = dirs.join(path.sep);
var config = this.configs[dir];
if (config && !config.stale){
return config;
}
return this.setConfig(conf);
var configPath = path.join(dir, '/.jshintrc');
if( fs.existsSync(configPath) ){
if (config) {
config.stale = false;
} else {
config = {stale: false, fileWatcher: this.watchConfig(configPath)};
}
try {
var configFile = fs.readFileSync(configPath, 'UTF8');
var conf = JSON.parse(configFile);
config.message = this.getConfigMessage(conf);
} catch(e){
console.error('Error parsing config file', e.message);
config.error = 'Could not load: ' + configPath + ': ' + e.message;
}
this.configs[dir] = config;
return config;
}
dirs.pop();
}
dirs.pop();
return {};
}

if (this.npmConfig) {
return {message: this.npmConfig};
} else if(this.currentConfig && !this.currentConfig.stale){
return this.currentConfig;
} else {
var config = loadConfig.call(this, filePath);
this.currentConfig = config;
return config;
}
};

AtomJsHint.prototype.setConfig = function(conf){
AtomJsHint.prototype.getConfigMessage = function(conf){
if(!conf) return null;
var config = {globals:{},options:{}};
config.globals = conf.globals || {};
if( conf.global ) { delete conf.globals; }
if( conf.globals ) { delete conf.globals; }
config.options = conf;
this.config = config;
return config;
};

AtomJsHint.prototype.watchConfig = function(configFile){
return fs.watch(configFile, { persistent: false },
function (event) {
var dir = path.dirname(configFile);
var config = this.configs[dir];
if (event === 'rename') {
config.fileWatcher.close();
if(config === this.currentConfig){
this.currentConfig = null;
}
delete this.configs[dir];
} else {
config.stale=true;
config.message=null;
config.error=null;
}
}.bind(this)
);
};

return AtomJsHint;
Expand Down