def _buildResult(self, resultDict, leadingWS): """Convert a pycompile.py output resultDict to a KoILintResult. A pycompile.py dict looks like this: {'description': 'SyntaxError: invalid syntax', 'filename': 'foo.py', 'lineno': 1, 'offset': 8, # may be None 'severity': 'ERROR', 'text': 'asdf = \n'} """ r = KoLintResult() r.description = resultDict["description"] if leadingWS: resultDict['lineno'] -= 1 if resultDict['text'].startswith(leadingWS): resultDict['text'] = resultDict['text'][len(leadingWS):] if resultDict["offset"] is not None: if leadingWS: actualLineOffset = len(leadingWS) if resultDict['offset'] >= actualLineOffset: resultDict['offset'] -= actualLineOffset r.description += " (at column %d)" % resultDict["offset"] r.lineStart = resultDict['lineno'] r.lineEnd = resultDict['lineno'] # Would be nice to actually underline from teh given offset, but # then have to be smart abouve how far after that to underline. r.columnStart = 1 r.columnEnd = len(resultDict['text']) if resultDict['severity'] == "ERROR": r.severity = r.SEV_ERROR elif resultDict['severity'] == "WARNING": r.severity = r.SEV_WARNING return r
def lint_with_text(self, request, text): if not text.strip(): return None cwd = request.cwd env = koprocessutils.getUserEnv() settingsDir = env.get("DJANGO_SETTINGS_MODULE", None) if not settingsDir: settingsDir = self._getSettingsDir(cwd) if settingsDir: # Save the current buffer to a temporary file. tmpFileName = tempfile.mktemp() fout = open(tmpFileName, 'wb') try: fout.write(text) fout.close() #XXX: How to tell whether we're using Python or Python3? prefName = "pythonExtraPaths" pythonPath = request.prefset.getString(prefName, "") pythonPathEnv = env.get("PYTHONPATH", "") if pythonPathEnv: if pythonPath: pythonPath += os.pathsep + pythonPathEnv else: pythonPath = pythonPathEnv if pythonPath: if sys.platform.startswith("win"): pythonPath = pythonPath.replace('\\', '/') env["PYTHONPATH"] = pythonPath elif env.has_key("PYTHONPATH"): del env["PYTHONPATH"] results = koLintResults() pythonExe = self._pythonInfo.getExecutableFromDocument(request.koDoc) argv = [pythonExe, self._djangoLinterPath, tmpFileName, settingsDir] p = process.ProcessOpen(argv, cwd=cwd, env=env, stdin=None) output, error = p.communicate() #log.debug("Django output: output:[%s], error:[%s]", output, error) retval = p.returncode finally: os.unlink(tmpFileName) if retval == 1: if error: results.addResult(self._buildResult(text, error)) else: results.addResult(self._buildResult(text, "Unexpected error")) else: result = KoLintResult() result.lineStart = 1 result.lineEnd = 1 result.columnStart = 1 result.columnEnd = 1 + len(text.splitlines(1)[0]) result.description = "Can't find settings.py for this Django file" result.encodedDescription = result.description result.severity = result.SEV_ERROR results = koLintResults() results.addResult(result) return results
def _buildResult(self, resultDict, leadingWS): """Convert a pycompile.py output resultDict to a KoILintResult. A pycompile.py dict looks like this: {'description': 'SyntaxError: invalid syntax', 'filename': 'foo.py', 'lineno': 1, 'offset': 8, # may be None 'severity': 'ERROR', 'text': 'asdf = \n'} """ r = KoLintResult() r.description = resultDict["description"] if leadingWS: resultDict['lineno'] -= 1 if resultDict['text'].startswith(leadingWS): resultDict['text'] = resultDict['text'][len(leadingWS):] if resultDict["offset"] is not None: if leadingWS: actualLineOffset = len(leadingWS) if resultDict['offset'] >= actualLineOffset: resultDict['offset'] -= actualLineOffset r.description += " (at column %d)" % resultDict["offset"] r.lineStart = resultDict['lineno'] r.lineEnd = resultDict['lineno'] # Would be nice to actually underline from teh given offset, but # then have to be smart abouve how far after that to underline. r.columnStart = 1 r.columnEnd = len(resultDict['text']) if resultDict['severity'] == "ERROR": r.severity = r.SEV_ERROR elif resultDict['severity'] == "WARNING": r.severity = r.SEV_WARNING return r
def lint_with_text(self, request, text): if not text.strip(): return None cwd = request.cwd env = koprocessutils.getUserEnv() settingsDir = env.get("DJANGO_SETTINGS_MODULE", None) if not settingsDir: settingsDir = self._getSettingsDir(cwd) if settingsDir: # Save the current buffer to a temporary file. tmpFileName = tempfile.mktemp() fout = open(tmpFileName, 'wb') try: fout.write(text) fout.close() #XXX: How to tell whether we're using Python or Python3? prefName = "pythonExtraPaths" pythonPath = request.prefset.getString(prefName, "") pythonPathEnv = env.get("PYTHONPATH", "") if pythonPathEnv: if pythonPath: pythonPath += os.pathsep + pythonPathEnv else: pythonPath = pythonPathEnv if pythonPath: if sys.platform.startswith("win"): pythonPath = pythonPath.replace('\\', '/') env["PYTHONPATH"] = pythonPath elif env.has_key("PYTHONPATH"): del env["PYTHONPATH"] results = koLintResults() pythonExe = self._pythonInfo.getExecutableFromDocument(request.koDoc) argv = [pythonExe, self._djangoLinterPath, tmpFileName, settingsDir] p = process.ProcessOpen(argv, cwd=cwd, env=env, stdin=None) output, error = p.communicate() #log.debug("Django output: output:[%s], error:[%s]", output, error) retval = p.returncode finally: os.unlink(tmpFileName) if retval == 1: if error: results.addResult(self._buildResult(text, error)) else: results.addResult(self._buildResult(text, "Unexpected error")) else: result = KoLintResult() result.lineStart = 1 result.lineEnd = 1 result.columnStart = 1 result.columnEnd = 1 + len(text.splitlines(1)[0]) result.description = "Can't find settings.py for this Django file" result.encodedDescription = result.description result.severity = result.SEV_ERROR results = koLintResults() results.addResult(result) return results
def parse(self, filepath, cwd=None): results = koLintResults() entries = [] cmd = [self.xpcshell_exe, self.csslint_filepath, filepath] stdout = None # We only need the stdout result. try: p = process.ProcessOpen(cmd, cwd=cwd, env=self._setLDLibraryPath(), stdin=None) stdout, stderr = p.communicate() entries = json.loads(stdout or "[]") except: log.exception("Problem running xcshell: %r\n,output:%r\n", cmd, stdout) return results for entry in entries: # Convert to Komodo lint result object. #print 'entry: %r' % (entry, ) results.addResult( KoLintResult(description=entry.get('description', ''), severity=entry.get('severity', 1), lineStart=entry.get('lineStart', 0), lineEnd=entry.get('lineEnd', -1), columnStart=entry.get('columnStart', 0), columnEnd=entry.get('columnEnd', 0))) return results
def _parseWarnings(self, warntext, text, leadingWS): """Parse out warnings from the text like the following and return a list of KoLintResult's. Example output: t.py:3: SyntaxWarning: import * only allowed at module level def foo(): t.py:1: DeprecationWarning: the regex module is deprecated; please use the re module import regex Also sometimes get lines like this: t.py: Token Error: ('EOF in multi-line string', (3, 6)) Note that this is picked up in the SyntaxError processing so we can skip that here for now. """ textlines = text.splitlines(1) if leadingWS: del textlines[0] warningRe = re.compile("^(?P<fname>.*?):(?P<line>\d+): (?P<desc>.*)$") results = koLintResults() for line in warntext.splitlines(): match = warningRe.match(line) if match: # Ignore lines that don't match this, e.g. " def foo():" r = KoLintResult() lineNo = int(match.group('line')) if leadingWS: lineNo -= 1 koLintResult.createAddResult(results, textlines, r.SEV_WARNING, lineNo, match.group('desc'), leadingWS) return results
def lint_with_text(self, request, text): if self._fmt_cmd_start is None: return cwd = request.cwd or None results = koLintResults() tmpfilename = tempfile.mktemp() + ".go" fout = open(tmpfilename, 'wb') fout.write(text) fout.close() cmd = self._fmt_cmd_start + [tmpfilename] env = koprocessutils.getUserEnv() bad_keys = [] for k, v in env.items(): if not isinstance(v, (str, unicode)): bad_keys.append(k) for k in bad_keys: del env[k] try: p = process.ProcessOpen(cmd, cwd=cwd, env=env, stdin=None) stdout, stderr = p.communicate() if not stderr: return results except: log.exception("Failed to run %s, cwd %r", cmd, cwd) return results finally: os.unlink(tmpfilename) errLines = stderr.splitlines(0) # Don't need the newlines. textLines = text.splitlines(0) for line in errLines: m = self._ptn_err.match(line) if m: fname = m.group(1) if tmpfilename not in fname: continue lineNo = int(m.group(2)) columnStart = int(m.group(3)) desc = m.group(4) m1 = self._problem_token.search(desc) if m1: columnEnd = columnStart + len(m1.group(1)) else: columnEnd = columnStart + 1 result = KoLintResult(description=desc, severity=SEV_ERROR, lineStart=lineNo, lineEnd=lineNo, columnStart=columnStart, columnEnd=columnEnd) results.addResult(result) return results
def _buildResult(self, text, message): inputLines = text.splitlines() r = KoLintResult() r.severity = r.SEV_ERROR r.description = message m = re.compile(r'TemplateSyntaxError:\s*(.*)\n').match(message) if m: message = m.group(1) for errorType in self._simple_matchers: if errorType in message: if self._do_simple_matcher( self._simple_matchers[errorType], text, r): return r for raw_ptn in self._contextual_matchers: if not self._compiled_ptns.has_key(raw_ptn): self._compiled_ptns[raw_ptn] = re.compile(raw_ptn) ptn = self._compiled_ptns[raw_ptn] m = ptn.search(message) if m: if self._do_contextual_matcher( self._contextual_matchers[raw_ptn], m.group(1), text, r): return r if self._test_for_duplicate_extends(message, text, r): return r # Special-case contextual pattern has two parts m = re.compile( r"Could not parse the remainder: '(.*?)' from '(.*?)'").search( message) if m: part2 = m.group(1) part1 = m.group(2)[:-1 * len(part2)] ptn = r'%s(%%s)' % (re.escape(part1), ) res = self._do_contextual_matcher(ptn, part2, text, r) if res: return r # Let the Unclosed Block Tag message fall through, and highlight # last line, since we don't know which open-tag it's referring to # Underline the last line for all other unrecognized syntax errors. r.columnStart = 1 problemLineIdx = len(inputLines) - 1 while problemLineIdx >= 0 and not inputLines[problemLineIdx].strip(): problemLineIdx -= 1 if problemLineIdx == -1: r.lineStart = r.lineEnd = 1 r.columnEnd = 1 else: r.lineStart = r.lineEnd = problemLineIdx + 1 r.columnEnd = len(inputLines[problemLineIdx]) return r
def _addMixedEOLWarnings(self, results, content, expectedEOL): """Add lint results (at the WARNING level) for each line that has an unexpected EOL. "results" in a koILintResults to which to add mixed EOL results. "content" is the content to analyze "expectedEOL" is the currently configured EOL for the document, this must be on of the EOL_LF, EOL_CR, EOL_CRLF constants. """ import eollib mixedEOLs = eollib.getMixedEOLLineNumbers(content, expectedEOL) if not mixedEOLs: return def collapseContinuousLineNumbers(lineNos): """Return a collapsed group of continuous line numbers.""" results = [] start = -10 last = -10 for lineNo in lineNos: if lineNo == last + 1: pass else: if start >= 0: results.append((start, last)) start = lineNo last = lineNo if start >= 0: results.append((start, last)) return results # Add a warning lint result for each such line. expectedEOLStr = eollib.eol2eolPref[expectedEOL] lines = content.splitlines(1) # For performance reasons, we collapse groups of continuous line # numbers into the one line result - bug 92733. for lineStart, lineEnd in collapseContinuousLineNumbers(mixedEOLs): r = KoLintResult() r.description = "This line does not end with the expected "\ "EOL: '%s' (select View | View EOL Markers)"\ % expectedEOLStr r.lineStart = lineStart + 1 r.lineEnd = lineEnd + 1 r.columnStart = 1 r.columnEnd = len(lines[lineEnd]) + 1 r.severity = r.SEV_WARNING results.addResult(r)
def _createAddResult(self, results, textlines, errorLines, severity): for line in errorLines: m = self.warnLinePtn.match(line) if m: lineNo = int(m.group(2)) desc = "pyflakes: %s" % (m.group(3), ) targetLine = textlines[lineNo - 1] columnEnd = len(targetLine) + 1 result = KoLintResult(description=desc, severity=severity, lineStart=lineNo, lineEnd=lineNo, columnStart=1, columnEnd=columnEnd) results.addResult(result)
def lint_with_text(self, request, text): #pylint: disable=R0201 textlines = text.splitlines() results = CSSLinter().lint(text, request.koDoc.language) lintResults = koLintResults() for r in results: if r.line_start is None: createAddResult(lintResults, textlines, r.status + 1, len(textlines) - 1, r.message) else: result = KoLintResult(description=r.message, severity=r.status + 1, lineStart=r.line_start, lineEnd=r.line_end, columnStart=r.col_start + 1, columnEnd=r.col_end + 1) lintResults.addResult(result) return lintResults
def _buildResult(self, text, message, filename=None): r = KoLintResult() r.severity = r.SEV_ERROR r.description = message m = re.match('./(.+\.go):(\d+): (.*)', message) if m: lint_message_file, line_no, error_message = m.groups() if lint_message_file != filename: r.description = message return r line_no = int(line_no) line_contents = text.splitlines()[int(line_no)-1].rstrip() r.description = error_message r.lineStart = r.lineEnd = line_no r.columnStart = len(line_contents) - len(line_contents.strip()) + 1 r.columnEnd = len(line_contents) + 1 return r
def _addMixedEOLWarnings(self, results, content, expectedEOL): """Add lint results (at the WARNING level) for each line that has an unexpected EOL. "results" in a koILintResults to which to add mixed EOL results. "content" is the content to analyze "expectedEOL" is the currently configured EOL for the document, this must be on of the EOL_LF, EOL_CR, EOL_CRLF constants. """ import eollib mixedEOLs = eollib.getMixedEOLLineNumbers(content, expectedEOL) if not mixedEOLs: return def collapseContinuousLineNumbers(lineNos): """Return a collapsed group of continuous line numbers.""" results = [] start = -10 last = -10 for lineNo in lineNos: if lineNo == last+1: pass else: if start >= 0: results.append((start, last)) start = lineNo last = lineNo if start >= 0: results.append((start, last)) return results # Add a warning lint result for each such line. expectedEOLStr = eollib.eol2eolPref[expectedEOL] lines = content.splitlines(1) # For performance reasons, we collapse groups of continuous line # numbers into the one line result - bug 92733. for lineStart, lineEnd in collapseContinuousLineNumbers(mixedEOLs): r = KoLintResult() r.description = "This line does not end with the expected "\ "EOL: '%s' (select View | View EOL Markers)"\ % expectedEOLStr r.lineStart = lineStart+1 r.lineEnd = lineEnd+1 r.columnStart = 1 r.columnEnd = len(lines[lineEnd]) + 1 r.severity = r.SEV_WARNING results.addResult(r)
def _buildResult(self, text, message): inputLines = text.splitlines() r = KoLintResult() r.severity = r.SEV_ERROR r.description = message m = re.compile(r'TemplateSyntaxError:\s*(.*)\n').match(message) if m: message = m.group(1) for errorType in self._simple_matchers: if errorType in message: if self._do_simple_matcher( self._simple_matchers[errorType], text, r): return r for raw_ptn in self._contextual_matchers: if raw_ptn not in self._compiled_ptns: self._compiled_ptns[raw_ptn] = re.compile(raw_ptn) ptn = self._compiled_ptns[raw_ptn] m = ptn.search(message) if m: if self._do_contextual_matcher(self._contextual_matchers[raw_ptn], m.group(1), text, r): return r if self._test_for_duplicate_extends(message, text, r): return r # Special-case contextual pattern has two parts m = re.compile( r"Could not parse the remainder: '(.*?)' from '(.*?)'").search(message) if m: part2 = m.group(1) part1 = m.group(2)[:-1 * len(part2)] ptn = r'%s(%%s)' % (re.escape(part1),) res = self._do_contextual_matcher(ptn, part2, text, r) if res: return r # Let the Unclosed Block Tag message fall through, and highlight # last line, since we don't know which open-tag it's referring to # Underline the last line for all other unrecognized syntax errors. r.columnStart = 1 problemLineIdx = len(inputLines) - 1 while problemLineIdx >= 0 and not inputLines[problemLineIdx].strip(): problemLineIdx -= 1 if problemLineIdx == -1: r.lineStart = r.lineEnd = 1 r.columnEnd = 1 else: r.lineStart = r.lineEnd = problemLineIdx + 1 r.columnEnd = len(inputLines[problemLineIdx]) return r
def parse(self, filepath, cwd=None, text=None): results = koLintResults() entries = [] cmd = [self.xpcshell_exe, self.csslint_filepath, filepath] stdout = None # We only need the stdout result. try: p = process.ProcessOpen(cmd, cwd=cwd, env=self._setEnv(), stdin=None) stdout, stderr = p.communicate() entries = json.loads(stdout or "[]") except: log.exception("Problem running xcshell: %r\n,output:%r\n", cmd, stdout) return results lines = text.split('\n') for entry in entries: # Convert to Komodo lint result object. #print 'entry: %r' % (entry, ) line_start = entry.get('lineStart', 0) column_start = entry.get('columnStart', 0) column_end = entry.get('columnEnd', 0) if column_start + 1 == column_end: word = re.search('\\w+$', lines[line_start - 1][0:column_end - 1]) if word: column_start = word.start() + 1 results.addResult( KoLintResult(description=entry.get('description', ''), severity=entry.get('severity', 1), lineStart=line_start, lineEnd=entry.get('lineEnd', -1), columnStart=column_start, columnEnd=column_end)) return results
def _TclPro_lint(self, lInput, lOutput): # TclPro output has the following header info: ################### #TclPro -- Version 1.4.1 #Copyright (C) Ajuba Solutions 1998-2001. All rights reserved. #This product is registered to: <user name> # #scanning: <filename> #checking: <filename> #<filename>:<lineNo> (<errorName>) <errorText> #<commandWithError> #<caret at col in command where error is> #[repeat above three until no more errors] ################### # The 'checking' line exists when the -onepass option isn't used i = 1 lastCol = -1 lastLine = -1 results = koLintResults() numLines = len(lOutput) while i < numLines: match = re.search(self._tclProRe, lOutput[i]) if not match: # We expect the first few lines to not match, while we # skip the header lines i += 1 # But if too many lines aren't matching, then we must have # unrecognizable input if i > 8: log.warn("couldn't recognize Tcl linter output format") break continue if i+2 >= numLines: log.error("unexpected return data format: short data") return cmd = lOutput[i+1].rstrip() col = lOutput[i+2].find('^') if col == -1: if i+3 == numLines: log.error("unexpected return data format: short data") return col = lOutput[i+3].find('^') if col == -1: log.warn("No column indicator following line %d:%r", i, lOutput[i]) i += 1 continue cmd = lOutput[i+2].rstrip() parseLineOffset = 1 else: parseLineOffset = 0 lineNo = match.group(1) # lInput is zero-based, whereas text display lines start at one, # so we adjust lineNo down one here. lineNo = int(lineNo) - 1 if lineNo != lastLine: lastLine = lineNo lastCol = 0 if col == -1: col = len(lInput[lineNo].rstrip()) startCol = lInput[lineNo].find(cmd, lastCol) if startCol == -1: # It's possible that we are throwing multiple warnings on # the same command. Let's check again, starting at col 0. # If something was found, ignore multiple warnings in the # same line, otherwise log a problem if lastCol == 0 or \ lInput[lineNo].find(cmd, 0) == -1: # Looks like we can't figure something out... log.debug('undecipherable lint output: %s,%s "%s" in "%s" last %s' \ % (lineNo+1, col, cmd, lInput[lineNo], lastCol)) i += parseLineOffset + 3 continue #print 'TclLint: %s,%s "%s" in "%s"' \ # % (lineNo+1, startCol, cmd, lInput[lineNo]) result = KoLintResult() msg = match.group(2) result.description = match.group(3).strip() result.lineStart = lineNo+1 # All our warnings stay on one line result.lineEnd = result.lineStart cmdLen = len(cmd) if msg.startswith("warn") or msg.startswith("nonPort"): result.severity = result.SEV_WARNING # In the warning case, go to the next char that we think # ends the word (not precise), starting at the caret. endCol = col while endCol < cmdLen and cmd[endCol] not in endWordSet: # might want to handle \ escapes endCol += 1 # readjust for where the command started endCol += startCol # and bump up the startCol to where the caret indicated startCol += col else: result.severity = result.SEV_ERROR # In the error case, highlight the entire command # The linter should only give one error per command endCol = startCol + cmdLen # Adjust by 1 as columns start at 1 for the text display result.columnStart = startCol + 1 result.columnEnd = endCol + 1 results.addResult(result) # Set lastCol to properly handle multiple similar code-blocks # with errors on the same line. lastCol = startCol+1 i += 3 # end while return results
def _errorResult(description): r = KoLintResult() r.severity = r.SEV_ERROR r.description = description return r
while 1: index = encodedString.find('?', offset) if index == -1: break indeces.append(index) offset = index + 1 log.debug("encoding errors at indeces %s", indeces) results = koLintResults() lines = content.splitlines(1) # keep line terminators offset = 0 # the current offset in the document for i in range(len(lines)): line = lines[i] while indeces and indeces[0] < offset + len(line): index = indeces.pop(0) # this index is on this line r = KoLintResult() r.description = "This character cannot be represented with "\ "the current encoding: '%s'"\ % encoding.python_encoding_name r.lineStart = i + 1 r.lineEnd = i + 1 r.columnStart = index - offset + 1 r.columnEnd = r.columnStart + 1 log.debug("encoding error: index=%d: %d,%d-%d,%d", index, r.lineStart, r.columnStart, r.lineEnd, r.columnEnd) r.severity = r.SEV_ERROR results.addResult(r) if not indeces: break offset += len(line) else:
def _TclPro_lint(self, lInput, lOutput): # TclPro output has the following header info: ################### #TclPro -- Version 1.4.1 #Copyright (C) Ajuba Solutions 1998-2001. All rights reserved. #This product is registered to: <user name> # #scanning: <filename> #checking: <filename> #<filename>:<lineNo> (<errorName>) <errorText> #<commandWithError> #<caret at col in command where error is> #[repeat above three until no more errors] ################### # The 'checking' line exists when the -onepass option isn't used i = 1 lastCol = -1 lastLine = -1 results = koLintResults() numLines = len(lOutput) while i < numLines: match = re.search(self._tclProRe, lOutput[i]) if not match: # We expect the first few lines to not match, while we # skip the header lines i += 1 # But if too many lines aren't matching, then we must have # unrecognizable input if i > 8: log.warn("couldn't recognize Tcl linter output format") break continue if i+2 >= numLines: log.error("unexpected return data format: short data") return cmd = lOutput[i+1].rstrip() col = lOutput[i+2].find('^') if col == -1: if i+3 == numLines: log.error("unexpected return data format: short data") return col = lOutput[i+3].find('^') if col == -1: log.warn("No column indicator following line %d:%r", i, lOutput[i]) i += 1 continue cmd = lOutput[i+2].rstrip() parseLineOffset = 1 else: parseLineOffset = 0 lineNo = match.group(1) # lInput is zero-based, whereas text display lines start at one, # so we adjust lineNo down one here. lineNo = int(lineNo) - 1 if lineNo != lastLine: lastLine = lineNo lastCol = 0 if col == -1: col = len(lInput[lineNo].rstrip()) startCol = lInput[lineNo].find(cmd, lastCol) if startCol == -1: # It's possible that we are throwing multiple warnings on # the same command. Let's check again, starting at col 0. # If something was found, ignore multiple warnings in the # same line, otherwise log a problem if lastCol == 0 or \ lInput[lineNo].find(cmd, 0) == -1: # Looks like we can't figure something out... log.debug('undecipherable lint output: %s,%s "%s" in "%s" last %s' \ % (lineNo+1, col, cmd, lInput[lineNo], lastCol)) i += parseLineOffset + 3 continue #print 'TclLint: %s,%s "%s" in "%s"' \ # % (lineNo+1, startCol, cmd, lInput[lineNo]) result = KoLintResult() msg = match.group(2) result.description = match.group(3).strip() result.lineStart = lineNo+1 # All our warnings stay on one line result.lineEnd = result.lineStart cmdLen = len(cmd) if msg.startswith("warn") or msg.startswith("nonPort"): result.severity = result.SEV_WARNING # In the warning case, go to the next char that we think # ends the word (not precise), starting at the caret. endCol = col while endCol < cmdLen and cmd[endCol] not in endWordSet: # might want to handle \ escapes endCol += 1 # readjust for where the command started endCol += startCol # and bump up the startCol to where the caret indicated startCol += col else: result.severity = result.SEV_ERROR # In the error case, highlight the entire command # The linter should only give one error per command endCol = startCol + cmdLen # Adjust by 1 as columns start at 1 for the text display result.columnStart = startCol + 1 result.columnEnd = endCol + 1 results.addResult(result) # Set lastCol to properly handle multiple similar code-blocks # with errors on the same line. lastCol = startCol+1 i += 3 # end while return results
def _fixPerlPart(self, text): parts = self._masonMatcher.findall(text) if not parts: return "", [] i = 0 lim = len(parts) perlTextParts = [] masonLintResults = [] eols = ("\n", "\r\n") # states currTags = [] perlTags = ('init', 'perl', 'once') # drop code for other tags. lineNo = i while i < lim: part = parts[i] if part in eols: perlTextParts.append(part) elif part.startswith("%") and (i == 0 or parts[i - 1].endswith("\n")): m = self._perlLineRE.match(part) if not m: perlTextParts.append(self._spaceOutNonNewlines(part)) else: perlTextParts.append(self._spaceOutNonNewlines(m.group(1))) perlTextParts.append(m.group(2)) elif part.startswith("<"): m = self._blockTagRE.match(part) if m: payload = m.group(2) if m.group(1): unexpectedEndTag = None if currTags: currTag = currTags[-1] if currTag == payload: currTags.pop() else: # Recover by removing everything up to and including the tag unexpectedEndTag = currTags[-1] for idx in range(len(currTags) - 1, -1, -1): if currTags[idx] == payload: del currTags[idx:-1] break else: unexpectedEndTag = "not in a tag block" if unexpectedEndTag is not None: lr = KoLintResult() lr.lineStart = lr.lineEnd = lineNo + 1 lr.columnStart = 1 lr.columnEnd = 1 + len(text.splitlines()[lineNo]) if unexpectedEndTag == "not in a tag block": lr.description = "Got end tag %s, not in a tag" % part else: lr.description = "Expected </%%%s>, got %s" % ( unexpectedEndTag, part) lr.encodedDescription = lr.description lr.severity = lr.SEV_WARNING masonLintResults.append(lr) else: currTags.append(payload) perlTextParts.append(self._spaceOutNonNewlines(part)) else: m = self._exprRE.match(part) if not m: perlTextParts.append(self._spaceOutNonNewlines(part)) else: perlTextParts.append(self._spaceOutNonNewlines(m.group(1))) payload = m.group(2) if payload.startswith("#"): perlTextParts.append(payload) # One big comment elif "|" not in payload: perlTextParts.append("print " + payload + ";"); else: # Filters aren't perl syntax, so punt perlTextParts.append(self._spaceOutNonNewlines(m.group(2))) perlTextParts.append(self._spaceOutNonNewlines(m.group(3))) else: # We only copy things out under certain circumstances if not currTags or currTags[-1] not in perlTags: perlTextParts.append(self._spaceOutNonNewlines(part)) else: perlTextParts.append(part) i += 1 lineNo += part.count("\n") return "".join(perlTextParts), masonLintResults
while 1: index = encodedString.find('?', offset) if index == -1: break indeces.append(index) offset = index + 1 log.debug("encoding errors at indeces %s", indeces) results = koLintResults() lines = content.splitlines(1) # keep line terminators offset = 0 # the current offset in the document for i in range(len(lines)): line = lines[i] while indeces and indeces[0] < offset + len(line): index = indeces.pop(0) # this index is on this line r = KoLintResult() r.description = "This character cannot be represented with "\ "the current encoding: '%s'"\ % encoding.python_encoding_name r.lineStart = i+1 r.lineEnd = i+1 r.columnStart = index - offset + 1 r.columnEnd = r.columnStart + 1 log.debug("encoding error: index=%d: %d,%d-%d,%d", index, r.lineStart, r.columnStart, r.lineEnd, r.columnEnd) r.severity = r.SEV_ERROR results.addResult(r) if not indeces: break offset += len(line) else:
class KoEsLintLinter(object): _com_interfaces_ = [Ci.koILinter] _reg_clsid_ = "{00CEDD2B-BE9E-4FD6-A9DE-5405B5B4503D}" _reg_contractid_ = "@addons.defman.me/koEsLintLinter;1" _reg_categories_ = [("category-komodo-linter", 'JavaScript&type=eslint'), ("category-komodo-linter", 'Node.js&type=eslint'), ("category-komodo-linter", 'JSX&type=eslint')] def __init__(self, ): self.project = Cc["@activestate.com/koPartService;1"].getService( Ci.koIPartService) 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_eslint_enabled", False): log.debug("EsLint: not enabled") return is_project = False cwd = None cmd = [] config = None if self.project.currentProject is not None: is_project = True cwd = self.project.currentProject.liveDirectory log.debug("EsLint: using current project directory") else: cwd = request.cwd log.debug("EsLint: cwd = %s" % (cwd)) # priority: # 1 - node_modules's eslint # 2 - eslint binary set by user [fallback] # 3 - eslint binary found on $PATH [fallback] if "win" in sys.platform: eslint = os.path.join(cwd, 'node_modules/.bin/eslint.cmd') else: eslint = os.path.join(cwd, 'node_modules/.bin/eslint') if not os.path.exists(eslint): log.debug("EsLint: {} does not exist".format(eslint)) eslint = request.prefset.getString('eslint_binary', '') if not os.path.exists(eslint): log.debug("EsLint: eslint executable is not set/not found") try: eslint = which.which('eslint') log.debug("EsLint: eslint executable found on $PATH") except which.WhichError: log.debug( "EsLint: eslint executable is not found on $PATH") return else: log.debug("EsLint: eslint executable is set by the user") if cwd is not None: # priority: # 1 - user config in the cwd # 2 - user config set by user [fallback] for file_format in ['js', 'yml', 'json']: config = os.path.join(cwd, '.eslintrc.{}'.format(file_format)) log.debug(config) if os.path.exists(config): break if not os.path.exists(config): config = request.prefset.getString('eslint_config', '') else: log.debug("EsLint: cwd is empty") return if config and os.path.isfile(config): cmd = [ eslint, '--no-color', '--format', 'json', '--config', config ] else: log.info("EsLint: .eslintrc is not found") return cmd += ['--stdin', '--stdin-filename', request.koDoc.file.encodedPath] log.debug("EsLint: command = %s" % (" ".join(cmd))) log.debug("EsLint: text = %s" % text) 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() try: log.debug("EsLint: stdout = %s" % stdout) log.debug("EsLint: stderr = %s" % stderr) data = json.loads(stdout)[0]['messages'] except Exception, e: log.warn('Failed to parse the eslint output!') log.warn('The output was: {}'.format(stdout)) return for message in data: line = message['line'] column = message['column'] try: column_end = message['endColumn'] except: if column > 1: column -= 1 column_end = column + 1 try: line_end = message['endLine'] except: line_end = line if message['severity'] == 2: severity = SEV_ERROR elif message['severity'] == 1: severity = SEV_WARNING else: severity = SEV_INFO if message['ruleId'] != None: description = "%s: %s" % (message['ruleId'], message['message']) else: description = message['message'] result = KoLintResult(description=description, severity=severity, lineStart=line, lineEnd=line_end, columnStart=column, columnEnd=column_end) results.addResult(result) return results
def lint_with_text(self, request, text): if not text.strip(): return None cwd = request.cwd env = koprocessutils.getUserEnv() settingsDir = env.get("DJANGO_SETTINGS_MODULE", None) if not settingsDir: # Django wants to do something like "import project.settings", which # means "project/settings.py" needs to exist. First, try to find it. settingsDir = self._getSettingsDir(cwd) # Ultimately, Komodo's Django linter (djangoLinter.py) sets the # DJANGO_SETTINGS_MODULE env variable to be the basename of # "settingsDir", which needs to be a module in the PYTHONPATH (which # the linter will append the dirname of "settingsDir" to). Append # ".settings" so when Django tries to do something like # "import project.settings", it will behave as expected. settingsDir += ".settings" if settingsDir: # Save the current buffer to a temporary file. tmpFileName = tempfile.mktemp() fout = open(tmpFileName, 'wb') try: fout.write(text) fout.close() #XXX: How to tell whether we're using Python or Python3? prefName = "pythonExtraPaths" pythonPath = request.prefset.getString(prefName, "") pythonPathEnv = env.get("PYTHONPATH", "") if pythonPathEnv: if pythonPath: pythonPath += os.pathsep + pythonPathEnv else: pythonPath = pythonPathEnv if pythonPath: if sys.platform.startswith("win"): pythonPath = pythonPath.replace('\\', '/') env["PYTHONPATH"] = pythonPath elif env.has_key("PYTHONPATH"): del env["PYTHONPATH"] # First try to use Python2 to run the django linter. If django # is not found, use Python3 instead. results = koLintResults() pythonExe = self._pythonInfo.getExecutableFromDocument( request.koDoc) djangoLinterPath = self._djangoLinterPath if pythonExe: p = process.ProcessOpen([pythonExe, "-c", "import django"], env=env, stdin=None) output, error = p.communicate() #log.debug("Django output: output:[%s], error:[%s]", output, error) if error.find('ImportError:') >= 0 and \ self._python3Info.getExecutableFromDocument(request.koDoc): pythonExe = self._python3Info.getExecutableFromDocument( request.koDoc) djangoLinterPath = self._djangoLinter3Path else: pythonExe = self._python3Info.getExecutableFromDocument( request.koDoc) djangoLinterPath = self._djangoLinter3Path #log.debug("pythonExe = " + pythonExe) #log.debug("djangoLinterPath = " + djangoLinterPath) argv = [pythonExe, djangoLinterPath, tmpFileName, settingsDir] p = process.ProcessOpen(argv, cwd=cwd, env=env, stdin=None) output, error = p.communicate() retval = p.returncode #log.debug("Django output: output:[%s], error:[%s], retval:%d", output, error) finally: os.unlink(tmpFileName) if error: results.addResult(self._buildResult(text, error)) elif retval != 0: results.addResult(self._buildResult(text, "Unexpected error")) else: result = KoLintResult() result.lineStart = 1 result.lineEnd = 1 result.columnStart = 1 result.columnEnd = 1 + len(text.splitlines(1)[0]) result.description = "Can't find settings.py for this Django file" result.encodedDescription = result.description result.severity = result.SEV_ERROR results = koLintResults() results.addResult(result) return results
def PerlWarnsToLintResults(warns, perlfilename, actualFileName, perlcode): perllines = perlcode.split('\n') # preprocess the perl warnings # - copy up '(Missing semicolon on previous line?)' to previous line # - remove 'perlfilename' #XXX it would be nice to find all hints and copy them up newwarns = [] for line in warns: # get rid if eols line = line[:-1] # handle multi-line string literals if newwarns and newwarns[-1] and newwarns[-1][-1] == "\r" : #XXX we should print an EOL symbol newwarns[-1] = newwarns[-1][:-1] + '\r' + line ## this code is not handling hints correctly. In any case, hints make ## the error messages too long for the status bar #elif line.find('(Missing semicolon on previous line?)') != -1: # newwarns[-1] = newwarns[-1] + " " + line[1:] else: newline = line newwarns.append(newline) warns = newwarns results = [] if type(perlfilename) == unicode: perlfilename = perlfilename.encode('utf-8') escPerlName = re.escape(perlfilename) # Fix bug 96303 (again): Replace any runs of non-ascii characters with unescaped '.*' # The other parts of the filename do get escaped. perlfilename_ascii_parts = re.split(r'([^\x00-\x7f]+)', perlfilename) escaped_even_parts = [re.escape(part) for part in perlfilename_ascii_parts[0::2]] escPerlName = ".*?".join(escaped_even_parts) warnRe = re.compile(r'(?P<description>.*) at %s line (?P<lineNum>\d+)(?P<hint>.*)' % escPerlName) successRe = re.compile(r'%s syntax OK' % escPerlName) compilationFailedRe = re.compile(r'Compilation failed in require at %s' % escPerlName) compilationErrorsRe = re.compile(r'%s had compilation errors\.' % escPerlName) beginFailedRe = re.compile(r'BEGIN failed--compilation aborted') pendingLines = [] sawSyntaxOK = False sawSyntaxBad = False for warn in warns: if successRe.match(warn): sawSyntaxOK = True continue match = warnRe.search(warn) if match: if compilationFailedRe.match(warn) or beginFailedRe.match(warn) or compilationErrorsRe.match(warn): sawSyntaxBad = True if results: # Don't bother displaying this continue lineNum = int(match.group('lineNum')) varName = match.groupdict().get('varName', None) lr = KoLintResult() lr.description = _combineTextParts(pendingLines, match.group('description') + match.group('hint')).replace(perlfilename, actualFileName) pendingLines = [] while lr.description.endswith("\n"): lr.description = lr.description[:-1] lr.lineStart = lineNum lr.lineEnd = lineNum if varName: lr.columnStart = perllines[lr.lineStart-1].find(varName) + 1 if lr.columnStart == -1: lr.columnEnd = -1 else: lr.columnEnd = lr.columnStart + len(varName) + 1 else: lr.columnStart = 1 lr.columnEnd = len(perllines[lr.lineStart-1]) + 1 results.append(lr) else: pendingLines.append(warn) if pendingLines and results: results[-1].description = _combineTextParts(pendingLines, results[-1].description).replace(perlfilename, actualFileName) # XXX # # Whatever is caught by the next else block is (according to Aaron) additional information # re: the last error. It should be added to the lint warning, but isn't because the # status bar display isn't smart enough and it looks ugly. # Todo: 1) add the extra info to the lint description (possibly add a new attribute to LintResults # with this 'metadata' # 2) Update the status bar display and tooltip display to display the right info. # I'm commenting this block out because log.warn's impact speed. --david # # else: # if not (successRe.search(warn) or failureRe.search(warn)): # log.warn("Skipping Perl warning: %r", warn) # under some conditions, Perl will spit out messages after the # "syntax Ok", so check the entire output. if results: if sawSyntaxOK and not sawSyntaxBad: severity = KoLintResult.SEV_WARNING elif sawSyntaxBad and not sawSyntaxOK: severity = KoLintResult.SEV_ERROR elif successRe.search("\n".join(warns)): severity = KoLintResult.SEV_WARNING else: severity = KoLintResult.SEV_ERROR lintResults = koLintResults() for lr in results: lr.severity = severity lintResults.addResult(lr) return lintResults
def _fixPerlPart(self, text): parts = self._masonMatcher.findall(text) if not parts: return "", [] i = 0 lim = len(parts) perlTextParts = [] masonLintResults = [] eols = ("\n", "\r\n") # states currTags = [] perlTags = ('init', 'perl', 'once') # drop code for other tags. lineNo = i while i < lim: part = parts[i] if part in eols: perlTextParts.append(part) elif part.startswith("%") and (i == 0 or parts[i - 1].endswith("\n")): m = self._perlLineRE.match(part) if not m: perlTextParts.append(self._spaceOutNonNewlines(part)) else: perlTextParts.append(self._spaceOutNonNewlines(m.group(1))) perlTextParts.append(m.group(2)) elif part.startswith("<"): m = self._blockTagRE.match(part) if m: payload = m.group(2) if m.group(1): unexpectedEndTag = None if currTags: currTag = currTags[-1] if currTag == payload: currTags.pop() else: # Recover by removing everything up to and including the tag unexpectedEndTag = currTags[-1] for idx in range(len(currTags) - 1, -1, -1): if currTags[idx] == payload: del currTags[idx:-1] break else: unexpectedEndTag = "not in a tag block" if unexpectedEndTag is not None: lr = KoLintResult() lr.lineStart = lr.lineEnd = lineNo + 1 lr.columnStart = 1 lr.columnEnd = 1 + len(text.splitlines()[lineNo]) if unexpectedEndTag == "not in a tag block": lr.description = "Got end tag %s, not in a tag" % part else: lr.description = "Expected </%%%s>, got %s" % ( unexpectedEndTag, part) lr.encodedDescription = lr.description lr.severity = lr.SEV_WARNING masonLintResults.append(lr) else: currTags.append(payload) perlTextParts.append(self._spaceOutNonNewlines(part)) else: m = self._exprRE.match(part) if not m: perlTextParts.append(self._spaceOutNonNewlines(part)) else: perlTextParts.append( self._spaceOutNonNewlines(m.group(1))) payload = m.group(2) if payload.startswith("#"): perlTextParts.append(payload) # One big comment elif "|" not in payload: perlTextParts.append("print " + payload + ";") else: # Filters aren't perl syntax, so punt perlTextParts.append( self._spaceOutNonNewlines(m.group(2))) perlTextParts.append( self._spaceOutNonNewlines(m.group(3))) else: # We only copy things out under certain circumstances if not currTags or currTags[-1] not in perlTags: perlTextParts.append(self._spaceOutNonNewlines(part)) else: perlTextParts.append(part) i += 1 lineNo += part.count("\n") return "".join(perlTextParts), masonLintResults
def PerlWarnsToLintResults(warns, perlfilename, actualFileName, perlcode): perllines = perlcode.split('\n') # preprocess the perl warnings # - copy up '(Missing semicolon on previous line?)' to previous line # - remove 'perlfilename' #XXX it would be nice to find all hints and copy them up newwarns = [] for line in warns: # get rid if eols line = line[:-1] # handle multi-line string literals if newwarns and newwarns[-1] and newwarns[-1][-1] == "\r": #XXX we should print an EOL symbol newwarns[-1] = newwarns[-1][:-1] + '\r' + line ## this code is not handling hints correctly. In any case, hints make ## the error messages too long for the status bar #elif line.find('(Missing semicolon on previous line?)') != -1: # newwarns[-1] = newwarns[-1] + " " + line[1:] else: newline = line newwarns.append(newline) warns = newwarns results = [] if type(perlfilename) == unicode: perlfilename = perlfilename.encode('utf-8') escPerlName = re.escape(perlfilename) # Fix bug 96303 (again): Replace any runs of non-ascii characters with unescaped '.*' # The other parts of the filename do get escaped. perlfilename_ascii_parts = re.split(r'([^\x00-\x7f]+)', perlfilename) escaped_even_parts = [ re.escape(part) for part in perlfilename_ascii_parts[0::2] ] escPerlName = ".*?".join(escaped_even_parts) warnRe = re.compile( r'(?P<description>.*) at %s line (?P<lineNum>\d+)(?P<hint>.*)' % escPerlName) successRe = re.compile(r'%s syntax OK' % escPerlName) compilationFailedRe = re.compile(r'Compilation failed in require at %s' % escPerlName) compilationErrorsRe = re.compile(r'%s had compilation errors\.' % escPerlName) beginFailedRe = re.compile(r'BEGIN failed--compilation aborted') pendingLines = [] sawSyntaxOK = False sawSyntaxBad = False for warn in warns: if successRe.match(warn): sawSyntaxOK = True continue match = warnRe.search(warn) if match: if compilationFailedRe.match(warn) or beginFailedRe.match( warn) or compilationErrorsRe.match(warn): sawSyntaxBad = True if results: # Don't bother displaying this continue lineNum = int(match.group('lineNum')) varName = match.groupdict().get('varName', None) lr = KoLintResult() lr.description = _combineTextParts( pendingLines, match.group('description') + match.group('hint')).replace( perlfilename, actualFileName) pendingLines = [] while lr.description.endswith("\n"): lr.description = lr.description[:-1] lr.lineStart = lineNum lr.lineEnd = lineNum if varName: lr.columnStart = perllines[lr.lineStart - 1].find(varName) + 1 if lr.columnStart == -1: lr.columnEnd = -1 else: lr.columnEnd = lr.columnStart + len(varName) + 1 else: lr.columnStart = 1 lr.columnEnd = len(perllines[lr.lineStart - 1]) + 1 results.append(lr) else: pendingLines.append(warn) if pendingLines and results: results[-1].description = _combineTextParts( pendingLines, results[-1].description).replace(perlfilename, actualFileName) # XXX # # Whatever is caught by the next else block is (according to Aaron) additional information # re: the last error. It should be added to the lint warning, but isn't because the # status bar display isn't smart enough and it looks ugly. # Todo: 1) add the extra info to the lint description (possibly add a new attribute to LintResults # with this 'metadata' # 2) Update the status bar display and tooltip display to display the right info. # I'm commenting this block out because log.warn's impact speed. --david # # else: # if not (successRe.search(warn) or failureRe.search(warn)): # log.warn("Skipping Perl warning: %r", warn) # under some conditions, Perl will spit out messages after the # "syntax Ok", so check the entire output. if results: if sawSyntaxOK and not sawSyntaxBad: severity = KoLintResult.SEV_WARNING elif sawSyntaxBad and not sawSyntaxOK: severity = KoLintResult.SEV_ERROR elif successRe.search("\n".join(warns)): severity = KoLintResult.SEV_WARNING else: severity = KoLintResult.SEV_ERROR lintResults = koLintResults() for lr in results: lr.severity = severity lintResults.addResult(lr) return lintResults
def _jslint_with_text(self, request, text, prefSwitchName, prefOptionsName): if not text: #log.debug("<< no text") return prefset = request.prefset if not prefset.getBooleanPref(prefSwitchName): return jsfilename, isMacro, datalines = self._make_tempfile_from_text(request, text) jsInterp = self._get_js_interp_path() jsLintDir = os.path.join(self.koDirs.supportDir, "lint", "javascript") jsLintApp = os.path.join(jsLintDir, "lintWrapper.js") jsLintBasename = None if prefSwitchName == "lintWithJSLint": appName = "jslint" else: appName = "jshint" try: customJSLint = prefset.getStringPref(appName + "_linter_chooser") if customJSLint == "specific": p = prefset.getStringPref(appName + "_linter_specific") if p and os.path.exists(p): jsLintDir = os.path.dirname(p) + "/" jsLintBasename = os.path.basename(p) except: log.exception("Problem finding the custom lintjs file") options = prefset.getStringPref(prefOptionsName).strip() # Lint the temp file, the jsInterp options are described here: # https://developer.mozilla.org/en/Introduction_to_the_JavaScript_shell cmd = [jsInterp, jsLintApp, "--include=" + jsLintDir] if jsLintBasename: if prefSwitchName == "lintWithJSLint": cmd.append("--jslint-basename=" + jsLintBasename) else: cmd.append("--jshint-basename=" + jsLintBasename) elif prefSwitchName == "lintWithJSHint": cmd.append("--jshint") if options: # Drop empty parts. otherParts = [s for s in re.compile(r'\s+').split(options)] cmd += [s for s in re.compile(r'\s+').split(options)] if request.koDoc.language == "Node.js": if not "node=" in options: cmd.append("node=1") if (prefSwitchName == "lintWithJSHint" and not self.strict_option_re.match(options) and 'globalstrict=' not in options): # jshint tests options.strict !== false, otherwise strict is on # Other options are tested as simply !options.strict cmd.append('strict=false') fd = open(jsfilename) cwd = request.cwd or None # We only need the stderr result. try: p = process.ProcessOpen(cmd, cwd=cwd, env=self._setLDLibraryPath(), stdin=fd) stdout, stderr = p.communicate() if stderr: log.warn("Error in jslint/jshint: stderr: %s, command was: %s", stderr, cmd) #log.debug("jslint(%s): stdout: %s, stderr: %s", prefSwitchName, stdout, stderr) warnLines = stdout.splitlines() # Don't need the newlines. i = 0 outputStart = "++++JSLINT OUTPUT:" while i < len(warnLines): if outputStart in warnLines[i]: warnLines = warnLines[i + 1:] break i += 1 except: log.exception("Problem running GenericJSLinter") finally: try: fd.close() except: log.error("Problem closing file des(%s)", jsfilename) try: os.unlink(jsfilename) except: log.error("Problem deleting file des(%s)", jsfilename) # 'jslint' error reports come in this form: # jslint error: at line \d+ column \d+: explanation results = koLintResults() msgRe = re.compile("^jslint error: at line (?P<lineNo>\d+) column (?P<columnNo>\d+):\s*(?P<desc>.*?)$") numDataLines = len(datalines) if len(warnLines) % 2 == 1: warnLines.append("") for i in range(0, len(warnLines), 2): msgLine = warnLines[i] evidenceLine = warnLines[i + 1] m = msgRe.match(msgLine) if m: lineNo = int(m.group("lineNo")) #columnNo = int(m.group("columnNo")) # build lint result object result = KoLintResult() evidenceLineNo = lineNo if evidenceLineNo >= numDataLines: evidenceLineNo = numDataLines - 1 if evidenceLine in datalines[evidenceLineNo]: lineNo = evidenceLineNo pass elif evidenceLineNo > 0 and evidenceLine in datalines[evidenceLineNo - 1]: lineNo = evidenceLineNo - 1 elif lineNo >= numDataLines: lineNo = numDataLines - 1 # if the error is on the last line, work back to the last # character of the first nonblank line so we can display # the error somewhere if len(datalines[lineNo]) == 0: while lineNo > 0 and len(datalines[lineNo - 1]) == 0: lineNo -= 1 result.columnStart = 1 result.columnEnd = len(datalines[lineNo]) + 1 result.lineStart = result.lineEnd = lineNo + 1 result.severity = result.SEV_WARNING result.description = m.group("desc") results.addResult(result) return results
def _createAddResult(self, results, datalines, errorType, lineNo, desc, numDots): # build lint result object result = KoLintResult() if lineNo >= len(datalines): lineNo = len(datalines) - 1 # if the error is on the last line, work back to the last # character of the first nonblank line so we can display # the error somewhere while lineNo >= 0 and not datalines[lineNo]: lineNo -= 1 if lineNo < 0: return result.columnEnd = len(datalines[lineNo - 1]) + 1 result.columnStart = 1 lineNo += 1 else: if numDots is None: result.columnStart = 1 else: result.columnStart = numDots + 1 result.columnEnd = result.columnStart + 1 result.lineStart = lineNo result.lineEnd = lineNo if (errorType.lower().find('warning') >= 0): result.severity = result.SEV_WARNING else: result.severity = result.SEV_ERROR # This always results in a lint result spanning a single # character, which, given the squiggly reporting scheme is # almost invisible. Workaround: set the result to be the # whole line and append the column number to the description. result.description = "%s: %s (on column %d)" % (errorType,desc,result.columnStart) result.columnStart = 1 result.columnEnd = len(datalines[lineNo-1])+1 results.addResult(result)
def _createAddResult(self, results, datalines, errorType, lineNo, desc, numDots): # build lint result object result = KoLintResult() if lineNo >= len(datalines): lineNo = len(datalines) - 1 # if the error is on the last line, work back to the last # character of the first nonblank line so we can display # the error somewhere while lineNo >= 0 and not datalines[lineNo]: lineNo -= 1 if lineNo < 0: return result.columnEnd = len(datalines[lineNo - 1]) + 1 result.columnStart = 1 lineNo += 1 else: if numDots is None: result.columnStart = 1 else: result.columnStart = numDots + 1 result.columnEnd = result.columnStart + 1 result.lineStart = lineNo result.lineEnd = lineNo if (errorType.lower().find('warning') >= 0): result.severity = result.SEV_WARNING else: result.severity = result.SEV_ERROR # This always results in a lint result spanning a single # character, which, given the squiggly reporting scheme is # almost invisible. Workaround: set the result to be the # whole line and append the column number to the description. result.description = "%s: %s (on column %d)" % (errorType, desc, result.columnStart) result.columnStart = 1 result.columnEnd = len(datalines[lineNo - 1]) + 1 results.addResult(result)
def _jslint_with_text(self, request, text, prefSwitchName, prefOptionsName): if not text: #log.debug("<< no text") return prefset = request.prefset if not prefset.getBooleanPref(prefSwitchName): return jsfilename, isMacro, datalines = self._make_tempfile_from_text( request, text) jsInterp = self._get_js_interp_path() jsLintDir = os.path.join(self.koDirs.supportDir, "lint", "javascript") jsLintApp = os.path.join(jsLintDir, "lintWrapper.js") jsLintBasename = None if prefSwitchName == "lintWithJSLint": appName = "jslint" else: appName = "jshint" try: customJSLint = prefset.getStringPref(appName + "_linter_chooser") if customJSLint == "specific": p = prefset.getStringPref(appName + "_linter_specific") if p and os.path.exists(p): jsLintDir = os.path.dirname(p) + "/" jsLintBasename = os.path.basename(p) except: log.exception("Problem finding the custom lintjs file") options = prefset.getStringPref(prefOptionsName).strip() # Lint the temp file, the jsInterp options are described here: # https://developer.mozilla.org/en/Introduction_to_the_JavaScript_shell cmd = [jsInterp, jsLintApp, "--include=" + jsLintDir] if jsLintBasename: if prefSwitchName == "lintWithJSLint": cmd.append("--jslint-basename=" + jsLintBasename) else: cmd.append("--jshint-basename=" + jsLintBasename) elif prefSwitchName == "lintWithJSHint": cmd.append("--jshint") if options: # Drop empty parts. otherParts = [s for s in re.compile(r'\s+').split(options)] cmd += [s for s in re.compile(r'\s+').split(options)] if request.koDoc.language == "Node.js": if not "node=" in options: cmd.append("node=1") if (prefSwitchName == "lintWithJSHint" and not self.strict_option_re.match(options) and 'globalstrict=' not in options): # jshint tests options.strict !== false, otherwise strict is on # Other options are tested as simply !options.strict cmd.append('strict=false') fd = open(jsfilename) cwd = request.cwd or None # We only need the stderr result. try: p = process.ProcessOpen(cmd, cwd=cwd, env=self._setLDLibraryPath(), stdin=fd) stdout, stderr = p.communicate() if stderr: log.warn("Error in jslint/jshint: stderr: %s, command was: %s", stderr, cmd) #log.debug("jslint(%s): stdout: %s, stderr: %s", prefSwitchName, stdout, stderr) warnLines = stdout.splitlines() # Don't need the newlines. i = 0 outputStart = "++++JSLINT OUTPUT:" while i < len(warnLines): if outputStart in warnLines[i]: warnLines = warnLines[i + 1:] break i += 1 except: log.exception("Problem running GenericJSLinter") finally: try: fd.close() except: log.error("Problem closing file des(%s)", jsfilename) try: os.unlink(jsfilename) except: log.error("Problem deleting file des(%s)", jsfilename) # 'jslint' error reports come in this form: # jslint error: at line \d+ column \d+: explanation results = koLintResults() msgRe = re.compile( "^jslint error: at line (?P<lineNo>\d+) column (?P<columnNo>\d+):\s*(?P<desc>.*?)$" ) numDataLines = len(datalines) if len(warnLines) % 2 == 1: warnLines.append("") for i in range(0, len(warnLines), 2): msgLine = warnLines[i] evidenceLine = warnLines[i + 1] m = msgRe.match(msgLine) if m: lineNo = int(m.group("lineNo")) #columnNo = int(m.group("columnNo")) # build lint result object result = KoLintResult() evidenceLineNo = lineNo if evidenceLineNo >= numDataLines: evidenceLineNo = numDataLines - 1 if evidenceLine in datalines[evidenceLineNo]: lineNo = evidenceLineNo pass elif evidenceLineNo > 0 and evidenceLine in datalines[ evidenceLineNo - 1]: lineNo = evidenceLineNo - 1 elif lineNo >= numDataLines: lineNo = numDataLines - 1 # if the error is on the last line, work back to the last # character of the first nonblank line so we can display # the error somewhere if len(datalines[lineNo]) == 0: while lineNo > 0 and len(datalines[lineNo - 1]) == 0: lineNo -= 1 result.columnStart = 1 result.columnEnd = len(datalines[lineNo]) + 1 result.lineStart = result.lineEnd = lineNo + 1 result.severity = result.SEV_WARNING result.description = m.group("desc") results.addResult(result) return results
def lint_with_text(self, request, text): if not text.strip(): return None cwd = request.cwd env = koprocessutils.getUserEnv() settingsDir = env.get("DJANGO_SETTINGS_MODULE", None) if not settingsDir: # Django wants to do something like "import project.settings", which # means "project/settings.py" needs to exist. First, try to find it. settingsDir = self._getSettingsDir(cwd) # Ultimately, Komodo's Django linter (djangoLinter.py) sets the # DJANGO_SETTINGS_MODULE env variable to be the basename of # "settingsDir", which needs to be a module in the PYTHONPATH (which # the linter will append the dirname of "settingsDir" to). Append # ".settings" so when Django tries to do something like # "import project.settings", it will behave as expected. settingsDir += ".settings" if settingsDir: # Save the current buffer to a temporary file. tmpFileName = tempfile.mktemp() fout = open(tmpFileName, 'wb') try: fout.write(text) fout.close() #XXX: How to tell whether we're using Python or Python3? prefName = "pythonExtraPaths" pythonPath = request.prefset.getString(prefName, "") pythonPathEnv = env.get("PYTHONPATH", "") if pythonPathEnv: if pythonPath: pythonPath += os.pathsep + pythonPathEnv else: pythonPath = pythonPathEnv if pythonPath: if sys.platform.startswith("win"): pythonPath = pythonPath.replace('\\', '/') env["PYTHONPATH"] = pythonPath elif env.has_key("PYTHONPATH"): del env["PYTHONPATH"] # First try to use Python2 to run the django linter. If django # is not found, use Python3 instead. results = koLintResults() pythonExe = self._pythonInfo.getExecutableFromDocument(request.koDoc) djangoLinterPath = self._djangoLinterPath if pythonExe: p = process.ProcessOpen([pythonExe, "-c", "import django"], env=env, stdin=None) output, error = p.communicate() #log.debug("Django output: output:[%s], error:[%s]", output, error) if error.find('ImportError:') >= 0 and \ self._python3Info.getExecutableFromDocument(request.koDoc): pythonExe = self._python3Info.getExecutableFromDocument(request.koDoc) djangoLinterPath = self._djangoLinter3Path else: pythonExe = self._python3Info.getExecutableFromDocument(request.koDoc) djangoLinterPath = self._djangoLinter3Path #log.debug("pythonExe = " + pythonExe) #log.debug("djangoLinterPath = " + djangoLinterPath) argv = [pythonExe, djangoLinterPath, tmpFileName, settingsDir] p = process.ProcessOpen(argv, cwd=cwd, env=env, stdin=None) output, error = p.communicate() retval = p.returncode #log.debug("Django output: output:[%s], error:[%s], retval:%d", output, error) finally: os.unlink(tmpFileName) if error: results.addResult(self._buildResult(text, error)) elif retval != 0: results.addResult(self._buildResult(text, "Unexpected error")) else: result = KoLintResult() result.lineStart = 1 result.lineEnd = 1 result.columnStart = 1 result.columnEnd = 1 + len(text.splitlines(1)[0]) result.description = "Can't find settings.py for this Django file" result.encodedDescription = result.description result.severity = result.SEV_ERROR results = koLintResults() results.addResult(result) return results