Merge branch 'master' of github.com:Microsoft/DevSkim-Sublime-Plugin
This commit is contained in:
Коммит
32512fb222
120
DevSkim.py
120
DevSkim.py
|
@ -12,6 +12,8 @@ import logging
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
import os
|
import os
|
||||||
|
import shlex
|
||||||
|
import subprocess
|
||||||
import traceback
|
import traceback
|
||||||
import webbrowser
|
import webbrowser
|
||||||
|
|
||||||
|
@ -213,6 +215,7 @@ class DevSkimEventListener(sublime_plugin.EventListener):
|
||||||
# Open a regular URL in the user's web browser
|
# Open a regular URL in the user's web browser
|
||||||
if re.match("^https?://", command, re.IGNORECASE):
|
if re.match("^https?://", command, re.IGNORECASE):
|
||||||
webbrowser.open_new(command)
|
webbrowser.open_new(command)
|
||||||
|
return
|
||||||
|
|
||||||
# Special commands, intercept and perform the fix
|
# Special commands, intercept and perform the fix
|
||||||
if command.startswith('#fixit'):
|
if command.startswith('#fixit'):
|
||||||
|
@ -341,6 +344,7 @@ class DevSkimEventListener(sublime_plugin.EventListener):
|
||||||
self.analyze_current_view(view, show_popup=False, single_line=True)
|
self.analyze_current_view(view, show_popup=False, single_line=True)
|
||||||
except Exception as msg:
|
except Exception as msg:
|
||||||
logger.warning("Error analyzing current view: %s", msg)
|
logger.warning("Error analyzing current view: %s", msg)
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
def on_load_async(self, view):
|
def on_load_async(self, view):
|
||||||
"""Handle asynchronous loading event."""
|
"""Handle asynchronous loading event."""
|
||||||
|
@ -352,6 +356,7 @@ class DevSkimEventListener(sublime_plugin.EventListener):
|
||||||
self.analyze_current_view(view, show_popup=False)
|
self.analyze_current_view(view, show_popup=False)
|
||||||
except Exception as msg:
|
except Exception as msg:
|
||||||
logger.warning("Error analyzing current view: %s", msg)
|
logger.warning("Error analyzing current view: %s", msg)
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
def on_post_save_async(self, view):
|
def on_post_save_async(self, view):
|
||||||
"""Handle post-save events."""
|
"""Handle post-save events."""
|
||||||
|
@ -365,6 +370,7 @@ class DevSkimEventListener(sublime_plugin.EventListener):
|
||||||
self.analyze_current_view(view)
|
self.analyze_current_view(view)
|
||||||
except Exception as msg:
|
except Exception as msg:
|
||||||
logger.warning("Error analyzing current view: %s", msg)
|
logger.warning("Error analyzing current view: %s", msg)
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
def analyze_current_view(self, view, show_popup=True, single_line=False):
|
def analyze_current_view(self, view, show_popup=True, single_line=False):
|
||||||
"""Kick off the analysis."""
|
"""Kick off the analysis."""
|
||||||
|
@ -375,7 +381,19 @@ class DevSkimEventListener(sublime_plugin.EventListener):
|
||||||
return
|
return
|
||||||
|
|
||||||
window = view.window()
|
window = view.window()
|
||||||
|
|
||||||
|
if not single_line:
|
||||||
|
self.clear_regions(view)
|
||||||
|
|
||||||
|
# TRY
|
||||||
|
_v = window.extract_variables()
|
||||||
|
filename = _v.get('file', '').replace('\\', '/')
|
||||||
|
if filename:
|
||||||
|
if self.is_file_ignored(filename):
|
||||||
|
logger.info("File is ignored.")
|
||||||
|
return
|
||||||
|
# DONE
|
||||||
|
|
||||||
self.lazy_initialize()
|
self.lazy_initialize()
|
||||||
|
|
||||||
logger.debug("analyze_current_view()")
|
logger.debug("analyze_current_view()")
|
||||||
|
@ -638,8 +656,8 @@ class DevSkimEventListener(sublime_plugin.EventListener):
|
||||||
logger.warning("Error suppressing rules for %s: %s" %
|
logger.warning("Error suppressing rules for %s: %s" %
|
||||||
(rule_id, msg))
|
(rule_id, msg))
|
||||||
|
|
||||||
# Only include active rules -- if 'active' is not specified, assume True
|
# Only include non-disabled rules -- if 'disabled' is not specified, assume False
|
||||||
rules = list(filter(lambda x: x.get('active', True), rules))
|
rules = list(filter(lambda x: not x.get('disabled', False), rules))
|
||||||
|
|
||||||
# Filter by tags, if specified, convert all to lowercase
|
# Filter by tags, if specified, convert all to lowercase
|
||||||
show_only_tags = set([k.lower().strip()
|
show_only_tags = set([k.lower().strip()
|
||||||
|
@ -847,7 +865,15 @@ class DevSkimEventListener(sublime_plugin.EventListener):
|
||||||
if self.is_suppressed(rule, line_list):
|
if self.is_suppressed(rule, line_list):
|
||||||
continue # Don't add the result to the list
|
continue # Don't add the result to the list
|
||||||
|
|
||||||
result_list.append({
|
# If there are conditions, run them now
|
||||||
|
context = {
|
||||||
|
'filename': filename,
|
||||||
|
'file_contents': file_contents,
|
||||||
|
'rule': rule,
|
||||||
|
'pattern': pattern_dict
|
||||||
|
}
|
||||||
|
|
||||||
|
result_details = {
|
||||||
'rule': rule,
|
'rule': rule,
|
||||||
'match_content': match.group(),
|
'match_content': match.group(),
|
||||||
'match_region': sublime.Region(start + offset, end + offset),
|
'match_region': sublime.Region(start + offset, end + offset),
|
||||||
|
@ -855,13 +881,65 @@ class DevSkimEventListener(sublime_plugin.EventListener):
|
||||||
'match_end': end + offset,
|
'match_end': end + offset,
|
||||||
'pattern': orig_pattern_str,
|
'pattern': orig_pattern_str,
|
||||||
'scope_list': scope_list
|
'scope_list': scope_list
|
||||||
})
|
}
|
||||||
|
|
||||||
|
if self.meets_conditions(context, result_details):
|
||||||
|
result_list.append(result_details)
|
||||||
else:
|
else:
|
||||||
logger.debug("Not running rule check [force={0}, rule_applies={1}, syntax={2}, ext={3},]".format(
|
logger.debug("Not running rule check [force={0}, rule_applies={1}, syntax={2}, ext={3},]".format(
|
||||||
force_analyze, rule_applies_to, set(rule_applies_to) & set(syntax_types), extension))
|
force_analyze, rule_applies_to, set(rule_applies_to) & set(syntax_types), extension))
|
||||||
|
|
||||||
return result_list
|
return result_list
|
||||||
|
|
||||||
|
def meets_conditions(self, context, result):
|
||||||
|
"""Checks to see if a finding meets specified conditions from the rule."""
|
||||||
|
logger.debug(context)
|
||||||
|
|
||||||
|
pattern = context.get('pattern')
|
||||||
|
if not pattern:
|
||||||
|
return True # No pattern means same thing as them all passing.
|
||||||
|
|
||||||
|
conditions = pattern.get('conditions')
|
||||||
|
if not conditions:
|
||||||
|
return True # No conditions means same as them all passing.
|
||||||
|
|
||||||
|
match_start = result.get('match_start')
|
||||||
|
line = self.view.substr(self.view.line(match_start))
|
||||||
|
|
||||||
|
if not line:
|
||||||
|
logger.error('No line was found in meets_conditions')
|
||||||
|
return True # No line means something is broken
|
||||||
|
|
||||||
|
def _line_match_all(line, targets):
|
||||||
|
logger.debug('_line_match_all({0}, {1})'.format(line, targets))
|
||||||
|
|
||||||
|
line = line.lower()
|
||||||
|
return all([t.lower() in line for t in targets])
|
||||||
|
|
||||||
|
def _line_match_any(line, targets):
|
||||||
|
line = line.lower()
|
||||||
|
return any([t.lower() in line for t in targets])
|
||||||
|
|
||||||
|
logger.debug('Found {0} conditions'.format(len(conditions)))
|
||||||
|
|
||||||
|
result = True
|
||||||
|
|
||||||
|
for condition in conditions:
|
||||||
|
name = condition.get('name')
|
||||||
|
value = condition.get('value')
|
||||||
|
invert = condition.get('invert', False)
|
||||||
|
|
||||||
|
if name == 'line-match-all':
|
||||||
|
r = _line_match_all(line, value)
|
||||||
|
result &= not r if invert else r
|
||||||
|
elif name == 'line-match-any':
|
||||||
|
r = _line_match_any(line, value)
|
||||||
|
result &= not r if invert else r
|
||||||
|
else:
|
||||||
|
logger.warning('Invalid condition name: {0}'.format(name))
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
def severity_abbreviation(self, severity):
|
def severity_abbreviation(self, severity):
|
||||||
"""Convert a severity name into an abbreviation."""
|
"""Convert a severity name into an abbreviation."""
|
||||||
if severity is None:
|
if severity is None:
|
||||||
|
@ -959,9 +1037,34 @@ class DevSkimEventListener(sublime_plugin.EventListener):
|
||||||
max_width=max_width,
|
max_width=max_width,
|
||||||
max_height=max_height,
|
max_height=max_height,
|
||||||
on_navigate=on_navigate,
|
on_navigate=on_navigate,
|
||||||
on_hide=on_hide), repeat_duration_ms)
|
on_hide=on_hide), repeat_duration_ms)
|
||||||
|
|
||||||
|
def is_file_ignored(self, filename):
|
||||||
|
"""Check to see if a file (by filename) is ignored from analysis."""
|
||||||
|
global user_settings
|
||||||
|
|
||||||
|
if not filename:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not user_settings.get('ignore_from_gitignore', True):
|
||||||
|
return False
|
||||||
|
|
||||||
|
for ignore_pattern in user_settings.get('ignore_files', []):
|
||||||
|
logger.warning("Checking {0}".format(ignore_pattern))
|
||||||
|
if re.match(ignore_pattern, filename, re.IGNORECASE):
|
||||||
|
return True
|
||||||
|
|
||||||
|
try:
|
||||||
|
cwd = os.path.dirname(filename)
|
||||||
|
filename = shlex.quote(filename)
|
||||||
|
output = subprocess.check_output("git check-ignore --no-index {0}; exit 0;".format(filename),
|
||||||
|
cwd=cwd, stderr=subprocess.STDOUT, shell=True)
|
||||||
|
return filename in output.decode('utf-8')
|
||||||
|
except Exception as msg:
|
||||||
|
logger.warning("Error checking if file is ignored: {0}".format(msg))
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
class ReplaceTextCommand(sublime_plugin.TextCommand):
|
class ReplaceTextCommand(sublime_plugin.TextCommand):
|
||||||
"""Simple function to route text changes to view."""
|
"""Simple function to route text changes to view."""
|
||||||
|
|
||||||
|
@ -991,6 +1094,7 @@ class DevSkimAnalyzeCommand(sublime_plugin.TextCommand):
|
||||||
devskim_event_listener.analyze_current_view(self.view)
|
devskim_event_listener.analyze_current_view(self.view)
|
||||||
except Exception as msg:
|
except Exception as msg:
|
||||||
logger.warning("Error analyzing current view: %s" % msg)
|
logger.warning("Error analyzing current view: %s" % msg)
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
|
||||||
class DevSkimReloadRulesCommand(sublime_plugin.TextCommand):
|
class DevSkimReloadRulesCommand(sublime_plugin.TextCommand):
|
||||||
|
@ -1002,10 +1106,10 @@ class DevSkimReloadRulesCommand(sublime_plugin.TextCommand):
|
||||||
rules = []
|
rules = []
|
||||||
stylesheet_content = ""
|
stylesheet_content = ""
|
||||||
|
|
||||||
|
|
||||||
def plugin_loaded():
|
def plugin_loaded():
|
||||||
"""Handle the plugin_loaded event from ST3."""
|
"""Handle the plugin_loaded event from ST3."""
|
||||||
logger.info('DevSkim plugin_loaded(), Sublime Text v%s' % sublime.version())
|
logger.info('DevSkim plugin_loaded(), Sublime Text v%s' % sublime.version())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def plugin_unloaded():
|
def plugin_unloaded():
|
||||||
|
|
|
@ -24,6 +24,13 @@
|
||||||
/* Sort results by either "severity" or "line_number" */
|
/* Sort results by either "severity" or "line_number" */
|
||||||
"sort_results_by": "line_number",
|
"sort_results_by": "line_number",
|
||||||
|
|
||||||
|
/* Ignore files specified in .gitignore */
|
||||||
|
"ignore_from_gitignore": true,
|
||||||
|
|
||||||
|
/* Ignore additional files, each is a regular expression, escaped for JSON */
|
||||||
|
"ignore_files": [
|
||||||
|
],
|
||||||
|
|
||||||
/* Run analysis as soon as a file is opened. */
|
/* Run analysis as soon as a file is opened. */
|
||||||
"show_highlights_on_load": true,
|
"show_highlights_on_load": true,
|
||||||
|
|
||||||
|
@ -52,5 +59,4 @@
|
||||||
/* Globally suppress these rules (list of rule IDs, like "DS123456") */
|
/* Globally suppress these rules (list of rule IDs, like "DS123456") */
|
||||||
"suppress_rules": [
|
"suppress_rules": [
|
||||||
]
|
]
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче