diff --git a/src/chrome/komodo/content/pref/pref-syntax-checking.js b/src/chrome/komodo/content/pref/pref-syntax-checking.js
index 0a2365ca5c..916929718d 100644
--- a/src/chrome/komodo/content/pref/pref-syntax-checking.js
+++ b/src/chrome/komodo/content/pref/pref-syntax-checking.js
@@ -448,6 +448,23 @@ function javaScriptInfo(languageName) {
};
}
+languageInfo.Ruby = {
+ browseForRubocopBinary: () => {
+ let rubocop_binary = document.getElementById('rubocop_binary');
+ let currentPath = rubocop_binary.value;
+ let path = ko.filepicker.browseForExeFile(null, currentPath || "");
+ if (path)
+ rubocop_binary.value = path;
+ },
+ browseForRubocopConfig: () => {
+ let rubocop_config = document.getElementById('rubocop_config');
+ let currentPath = rubocop_config.value;
+ let path = ko.filepicker.browseForExeFile(null, currentPath || "");
+ if (path)
+ rubocop_config.value = path;
+ }
+};
+
function typescript_setup() {
if (!('TypeScript' in dialog)) {
dialog.TypeScript = {};
diff --git a/src/chrome/komodo/content/pref/pref-syntax-checking.xul b/src/chrome/komodo/content/pref/pref-syntax-checking.xul
index db636094c2..2ccb9b931f 100644
--- a/src/chrome/komodo/content/pref/pref-syntax-checking.xul
+++ b/src/chrome/komodo/content/pref/pref-syntax-checking.xul
@@ -609,6 +609,46 @@
+
+
+
+ &rubocopChecking.description;
+
+
+
+
+
+
+ &rubocopChecking.binary;
+
+
+
+
+
+
+ &rubocopChecking.config;
+
+
+
+
+
diff --git a/src/chrome/komodo/locale/en-US/pref/pref.dtd b/src/chrome/komodo/locale/en-US/pref/pref.dtd
index 57a6f9c2d6..62403a884b 100644
--- a/src/chrome/komodo/locale/en-US/pref/pref.dtd
+++ b/src/chrome/komodo/locale/en-US/pref/pref.dtd
@@ -851,6 +851,13 @@ adding an additional caret at the clicked position.">
+
+
+
+
+
+
+
diff --git a/src/lint/Conscript b/src/lint/Conscript
index 0264274036..4660a27345 100644
--- a/src/lint/Conscript
+++ b/src/lint/Conscript
@@ -66,6 +66,7 @@ if ($havePy2to3) {
$cons->InstallXpcomComponent('koCSSExLinter.py');
$cons->InstallXpcomComponent('koHTMLLinter.py');
$cons->InstallXpcomComponent('koPHPLinter.py');
+$cons->InstallXpcomComponent('koRubocopLinter.py');
$cons->InstallXpcomComponent('koEsLintLinter.py');
$cons->InstallXpcomComponent('koJavaScriptLinter.py');
$cons->InstallXpcomComponent('koLintService.py');
diff --git a/src/lint/koRubocopLinter.py b/src/lint/koRubocopLinter.py
new file mode 100644
index 0000000000..66b6e2311c
--- /dev/null
+++ b/src/lint/koRubocopLinter.py
@@ -0,0 +1,110 @@
+from xpcom import components
+from koLintResult import KoLintResult, SEV_ERROR, SEV_WARNING, SEV_INFO
+from koLintResults import koLintResults
+import os
+import logging
+import tempfile
+import process
+import koprocessutils
+import which
+import json
+
+Cc = components.classes
+Ci = components.interfaces
+
+log = logging.getLogger("koRubocopLinter")
+#log.setLevel(logging.DEBUG)
+
+
+class KoRubocopLinter(object):
+ _com_interfaces_ = [Ci.koILinter]
+ _reg_clsid_ = "{0BED01B1-6BAD-4B55-A407-3C6273C0032D}"
+ _reg_contractid_ = "@addons.defman.me/koRubocopLinter;1"
+ _reg_categories_ = [
+ ("category-komodo-linter", 'Ruby'),
+ ]
+
+ def __init__(self,):
+ self.ignore_cops = ["Style/FileName"]
+ self.file_ext = '.rb'
+ self.project = Cc["@activestate.com/koPartService;1"].getService(Ci.koIPartService)
+ self.ruby_linter = Cc["@activestate.com/koLinter?language=Ruby;1"].getService(Ci.koILinter)
+
+ def lint(self, request):
+ text = request.content.encode(request.encoding.python_encoding_name)
+ return self.lint_with_text(request, text)
+
+ def lint_with_text(self, request, text):
+ if not request.prefset.getBoolean("lint_rubocop_enabled", False):
+ log.debug("Rubocop: not enabled")
+ return self.ruby_linter.lint(request)
+ try:
+ rubocop = request.prefset.getString('rubocop_binary' ,'')
+ if rubocop == '':
+ log.debug('Rubocop: looking for the rubocop executable on $PATH')
+ rubocop = which.which('rubocop')
+ except which.WhichError:
+ log.debug("Rubocop: rubocop is not found")
+ return self.ruby_linter.lint(request)
+
+ cwd = None
+ cmd = []
+ rbfile = None
+
+ if self.project.currentProject is not None:
+ cwd = self.project.currentProject.liveDirectory
+ log.debug("Rubocop: using current project directory")
+ else:
+ cwd = request.cwd
+ log.debug("Rubocop: cwd = %s" % (cwd))
+
+ if cwd is not None:
+ for ext in ['yaml', 'yml']:
+ config = os.path.join(cwd, '.rubocop.{}'.format(ext))
+ if os.path.exists(config):
+ log.debug("Config file: {}".format(config))
+ break
+ else:
+ config = request.prefset.getString('rubocop_config', '')
+ log.debug("Config file (set by user): {}".format(config))
+ if config and os.path.isfile(config):
+ cmd = [rubocop, '--format', 'json', '--config', config]
+ else:
+ cmd = [rubocop, '--format', 'json']
+ log.debug("Rubocop: .rubocop.yml or .rubocop.yaml are not found")
+
+ cmd += ['--stdin', request.koDoc.file.encodedPath]
+
+ log.debug("Rubocop: command = %s" % (" ".join(cmd)))
+
+ env = koprocessutils.getUserEnv()
+ cwd = cwd or None
+ p = process.ProcessOpen(cmd, cwd=cwd, env=env, stdin=process.PIPE)
+ stdout, stderr = p.communicate(input=text)
+
+ results = koLintResults()
+ data = json.loads(stdout)['files'][0]
+
+ for offense in data['offenses']:
+ if offense['cop_name'] in self.ignore_cops:
+ log.debug("Rubocop: cop '%s' ignored" % (offense['cop_name']))
+ continue
+ line = offense['location']['line']
+ column = offense['location']['column']
+ column_end = column + offense['location']['length']
+ if offense['severity'] in ['refactor', 'convention']:
+ severity = SEV_INFO
+ elif offense['severity'] == "warning":
+ severity = SEV_WARNING
+ else:
+ severity = SEV_ERROR
+ description = "%s: %s" % (offense['cop_name'], offense['message'])
+ result = KoLintResult(description=description,
+ severity=severity,
+ lineStart=line,
+ lineEnd=line,
+ columnStart=column,
+ columnEnd=column_end)
+ results.addResult(result)
+ log.debug("Rubocop: lint results: %d" % (len(data['offenses'])))
+ return results