def show_error_marks(view): '''Adds error marks to view.''' erase_error_marks(view) if not get_setting("show_visual_error_marks", True): return fill_outlines = False gutter_mark = 'dot' outlines = {'warning': [], 'illegal': []} fn = sencode(view.file_name()) markers = {'warning': get_setting("marker_warning_scope", "comment"), 'illegal': get_setting("marker_error_scope", "invalid") } for line in ERRORS[fn].keys(): outlines['illegal'].append(view.full_line(view.text_point(line, 0))) for line in WARNINGS[fn].keys(): outlines['warning'].append(view.full_line(view.text_point(line, 0))) for lint_type in outlines: if outlines[lint_type]: args = [ 'sublimeclang-outlines-{0}'.format(lint_type), outlines[lint_type], markers[lint_type], gutter_mark ] if not fill_outlines: args.append(sublime.DRAW_OUTLINED) view.add_regions(*args)
def update_settings(self): cmdline = get_setting("analyzer_commandline", ["clang", "--analyze", "-o", "-"]) opts = get_setting("options") for setting in opts: cmdline.append(setting) self.cmdline = cmdline self.extensions = get_setting("analyzer_extensions")
def load_settings(self): translationunitcache.tuCache.clear() self.dont_complete_startswith = get_setting("dont_complete_startswith", ["operator", "~"]) self.recompile_delay = get_setting("recompile_delay", 1000) self.cache_on_load = get_setting("cache_on_load", True) self.remove_on_close = get_setting("remove_on_close", True) self.time_completions = get_setting("time_completions", False)
def show_error_marks(view): '''Adds error marks to view.''' erase_error_marks(view) if not get_setting("show_visual_error_marks", True): return fill_outlines = False gutter_mark = 'dot' outlines = {'warning': [], 'illegal': []} fn = sencode(view.file_name()) markers = { 'warning': get_setting("marker_warning_scope", "comment"), 'illegal': get_setting("marker_error_scope", "invalid") } for line in ERRORS[fn].keys(): outlines['illegal'].append(view.full_line(view.text_point(line, 0))) for line in WARNINGS[fn].keys(): outlines['warning'].append(view.full_line(view.text_point(line, 0))) for lint_type in outlines: if outlines[lint_type]: args = [ 'sublimeclang-outlines-{0}'.format(lint_type), outlines[lint_type], markers[lint_type], gutter_mark ] if not fill_outlines: args.append(sublime.DRAW_OUTLINED) view.add_regions(*args)
def load_settings(self): translationunitcache.tuCache.clear() self.dont_complete_startswith = get_setting("dont_complete_startswith", ['operator', '~']) self.recompile_delay = get_setting("recompile_delay", 1000) self.cache_on_load = get_setting("cache_on_load", True) self.remove_on_close = get_setting("remove_on_close", True) self.time_completions = get_setting("time_completions", False)
def open(self, window=None): if window == None: window = sublime.active_window() if not self.is_visible(window): self.view = window.get_output_panel("clang") self.view.settings().set("result_file_regex", "^(.+):([0-9]+),([0-9]+)") if get_setting("output_panel_use_syntax_file", False): fileName = get_setting("output_panel_syntax_file", None) if fileName is not None: self.view.set_syntax_file(fileName) self.flush() window.run_command("show_panel", {"panel": "output.clang"})
def is_enabled(self, view): if common.get_setting("enabled", True, view) == False: return False elif clang_complete_enabled == False: return False return True
def load_settings(self): self.recompile_delay = common.get_setting("recompile_delay", 0) self.cache_on_load = common.get_setting("cache_on_load", True) self.not_code_regex = re.compile("(string.)|(comment.)") self.remove_on_close = common.get_setting("remove_on_close", True) self.recompile_delay = common.get_setting("recompile_delay", 1000) self.cache_on_load = common.get_setting("cache_on_load", True) self.remove_on_close = common.get_setting("remove_on_close", True) self.reparse_on_save = common.get_setting("reparse_on_save", True) self.reparse_on_focus = common.get_setting("reparse_on_focus", True) self.reparse_on_edit = common.get_setting("reparse_on_edit", False) self.dont_complete_startswith = ['operator', '~']
def update_regions(self, f, v): regions = [] for range in self.ranges[f]: start = range[0] end = range[1] regions.append(sublime.Region(v.text_point(start[0]-1, start[1]-1), v.text_point(end[0]-1, end[1]))) v.show(regions[0]) v.add_regions("clang.analyzer", regions, get_setting("marker_analyzer_scope", "invalid"), "", sublime.DRAW_OUTLINED)
def recompile(self): view = self.view unsaved_files = [] if view.is_dirty() and get_setting("reparse_use_dirty_buffer", False, view): unsaved_files.append((sencode(view.file_name()), view.substr(Region(0, view.size())))) if not translationunitcache.tuCache.reparse(view, sencode(view.file_name()), unsaved_files, self.reparse_done): # Already parsing so retry in a bit self.restart_recompile_timer(1)
def recompile(self, view, callback): unsaved_files = [] if view.is_dirty() and get_setting("reparse_use_dirty_buffer", False, view): unsaved_files.append((sencode(view.file_name()), view.substr(Region(0, view.size())))) if not translationunitcache.tuCache.reparse(view, sencode(view.file_name()), unsaved_files, callback): print "Already parsing." self.restart_recompile_timer(1)
def collect_all_options(view, filename, language): assert view is not None assert filename is not None assert language is not None assert language.is_supported() assert cindex.conf is not None global SystemIncludes # use clang to figure out the magical -isystem paths # todo: ObjC and ObjCPP ?? if SystemIncludes == None: packages = sublime.packages_path() package = os.path.join(packages, "SublimeClang") source = "" compiler = "" cindex.conf.arch = sublime.arch() if language.kind == Language.C: source = "test.c" compiler = cindex.conf.locate_clang() elif language.kind == Language.CPP: source = "test.cpp" compiler = cindex.conf.locate_clang_cpp() else: raise Error("Unsupported language.") source = os.path.join(package, source) info = common.ClangInfo.collect(compiler, source) SystemIncludes = info.internal_isystem print("Found system includes:") print(SystemIncludes) # this is how we got it from the settings before... #sys_includes = common.get_setting("system_include_paths", []) opt = CompileOptions(language, SystemIncludes) # This is the bitmask sent to index.parse. # For example, to be able to go to the definition of # preprocessed macros, set it to 1, for using an implicit # precompiled header set it to 4 and for caching completion # results, set it to 8. Or all together 1+4+8=13. # See http://clang.llvm.org/doxygen/group__CINDEX__TRANSLATION__UNIT.html#gab1e4965c1ebe8e41d71e90203a723fe9 # and http://clang.llvm.org/doxygen/Index_8h_source.html # for more details opt.index_parse_type = 13 language_options = common.get_setting("language_options", {}) if language_options.has_key(language.key()): opt.language_options = language_options[language.key()] project_file, project_options = common.get_project_settings(filename) if project_file != None: opt.project_file = project_file opt.project_options = project_options return opt
def get_translation_unit(view, filename=None, blocking=False): if filename == None: filename = sencode(view.file_name()) if get_setting("warm_up_in_separate_thread", True, view) and not blocking: stat = warm_up_cache(view, filename) if stat == translationunitcache.TranslationUnitCache.STATUS_NOT_IN_CACHE: return None elif stat == translationunitcache.TranslationUnitCache.STATUS_PARSING: sublime.status_message("Hold your horses, cache still warming up") return None return translationunitcache.tuCache.get_translation_unit(filename, translationunitcache.tuCache.get_opts(view), translationunitcache.tuCache.get_opts_script(view))
def on_selection_modified(self, view): v = output_view.get_view() if not v is None and view.id() == v.id(): region = v.full_line(v.sel()[0].a) v.add_regions("clang.analyze.selection", [region], get_setting("marker_analyzer_output_panel_scope", "invalid"), "", sublime.DRAW_OUTLINED) row, col = v.rowcol(v.sel()[0].a) diag = analyzer.get_diagnostic_at_line(row) self.prepare_ranges(diag.get_ranges(row), diag.files) for f in self.ranges: v = sublime.active_window().open_file(f, sublime.TRANSIENT) if not v.is_loading(): self.update_regions(f, v)
def get_translation_unit(view, filename, language, blocking=False): cache = get_cache() if common.get_setting("warm_up_in_separate_thread", True) and not blocking: stat = warm_up_cache(view, filename, language) if stat == TUCache.STATUS_NOT_IN_CACHE: return None elif stat == TUCache.STATUS_PARSING: sublime.status_message("Hold your horses, cache still warming up") return None opts = collect_all_options(view, filename, language) debug = common.get_setting("debug", False) if debug == True: print("Compiling: '%s'" % (filename)) print("Language: '%s'" % (language)) print("Project File: '%s'" % (opts.project_file)) print("Options:") print(opts) return cache.get_translation_unit(filename, opts)
def update_regions(self, f, v): regions = [] for range in self.ranges[f]: start = range[0] end = range[1] regions.append( sublime.Region(v.text_point(start[0] - 1, start[1] - 1), v.text_point(end[0] - 1, end[1]))) v.show(regions[0]) v.add_regions("clang.analyzer", regions, get_setting("marker_analyzer_scope", "invalid"), "", sublime.DRAW_OUTLINED)
def on_close(self, view): if not common.get_setting("pop_on_close", True, view): return # If the view we just closed was last in the navigation_stack, # consider it "popped" from the stack filename = get_filename(view) while True: if len(navigation_stack) == 0 or \ not navigation_stack[ len(navigation_stack) - 1][1].startswith(filename): break navigation_stack.pop()
def on_close(self, view): if not get_setting("pop_on_close", True, view): return # If the view we just closed was last in the navigation_stack, # consider it "popped" from the stack fn = view.file_name() if fn == None: return fn = sencode(fn) while True: if len(navigation_stack) == 0 or not navigation_stack[len(navigation_stack) - 1][1].startswith(fn): break navigation_stack.pop()
def run(self, **args): show = args["show"] if "show" in args else None aview = sublime.active_window().active_view() error_marks = get_setting("error_marks_on_panel_only", False, aview) if show or (show == None and not clang_error_panel.is_visible(self.window)): clang_error_panel.open(self.window) if error_marks: show_error_marks(aview) else: clang_error_panel.close() if error_marks: erase_error_marks(aview)
def highlight_panel_row(self): if self.view is None: return view = sublime.active_window().active_view() row, col = view.rowcol(view.sel()[0].a) str = "%s:%d" % (view.file_name(), (row + 1)) r = self.view.find(str.replace('\\','\\\\'), 0) panel_marker = get_setting("marker_output_panel_scope", "invalid") if r == None: self.view.erase_regions('highlightText') else: regions = [self.view.full_line(r)] self.view.add_regions('highlightText', regions, panel_marker, 'dot', sublime.DRAW_OUTLINED)
def on_query_context(self, view, key, operator, operand, match_all): if key == "clang_supported_language": if view == None: view = sublime.active_window().active_view() return is_supported_language(view) elif key == "clang_is_code": return self.not_code_regex.search(view.scope_name(view.sel()[0].begin())) == None elif key == "clang_complete_enabled": return clang_complete_enabled elif key == "clang_automatic_completion_popup": return get_setting("automatic_completion_popup", True, view) elif key == "clang_panel_visible": return clang_error_panel.is_visible()
def highlight_panel_row(self): if self.view is None: return view = sublime.active_window().active_view() row, col = view.rowcol(view.sel()[0].a) str = "%s:%d" % (view.file_name(), (row + 1)) r = self.view.find(str.replace('\\', '\\\\'), 0) panel_marker = get_setting("marker_output_panel_scope", "invalid") if r == None: self.view.erase_regions('highlightText') else: regions = [self.view.full_line(r)] self.view.add_regions('highlightText', regions, panel_marker, 'dot', sublime.DRAW_OUTLINED)
def on_close(self, view): if not get_setting("pop_on_close", True, view): return # If the view we just closed was last in the navigation_stack, # consider it "popped" from the stack fn = view.file_name() if fn == None: return fn = sencode(fn) while True: if len(navigation_stack) == 0 or \ not navigation_stack[ len(navigation_stack) - 1][1].startswith(fn): break navigation_stack.pop()
def on_selection_modified(self, view): v = output_view.get_view() if not v is None and view.id() == v.id(): region = v.full_line(v.sel()[0].a) v.add_regions( "clang.analyze.selection", [region], get_setting("marker_analyzer_output_panel_scope", "invalid"), "", sublime.DRAW_OUTLINED) row, col = v.rowcol(v.sel()[0].a) diag = analyzer.get_diagnostic_at_line(row) self.prepare_ranges(diag.get_ranges(row), diag.files) for f in self.ranges: v = sublime.active_window().open_file(f, sublime.TRANSIENT) if not v.is_loading(): self.update_regions(f, v)
def on_post_save(self, view): if is_supported_language(view) and get_setting("reparse_on_save", True, view): self.view = view self.restart_recompile_timer(0.1)
def on_query_completions(self, view, prefix, locations): global clang_complete_enabled if not is_supported_language(view) or not clang_complete_enabled or \ not view.match_selector(locations[0], '-string -comment -constant'): return [] line = view.substr(sublime.Region(view.line(locations[0]).begin(), locations[0])) match = re.search(r"[,\s]*(\w+)\s+\w+$", line) if match != None: valid = ["new", "delete", "return", "goto", "case", "const", "static", "class", "struct", "typedef", "union"] if match.group(1) not in valid: # Probably a variable or function declaration # There's no point in trying to complete # a name that hasn't been typed yet... return self.return_completions([], view) timing = "" tot = 0 start = time.time() tu = get_translation_unit(view) if tu == None: return self.return_completions([], view) ret = None tu.lock() try: if self.time_completions: curr = (time.time() - start)*1000 tot += curr timing += "TU: %f" % (curr) start = time.time() cached_results = None if clang_fast_completions and get_setting("enable_fast_completions", True, view): data = view.substr(sublime.Region(0, locations[0])) try: cached_results = tu.cache.complete(data, prefix) except: traceback.print_exc() if cached_results != None: # print("found fast completions") ret = cached_results else: # print("doing slow completions") row, col = view.rowcol(locations[0] - len(prefix)) unsaved_files = [] if view.is_dirty(): unsaved_files.append((sencode(view.file_name()), view.substr(Region(0, view.size())))) ret = tu.cache.clangcomplete(sencode(view.file_name()), row+1, col+1, unsaved_files, is_member_completion(view, locations[0] - len(prefix))) if self.time_completions: curr = (time.time() - start)*1000 tot += curr timing += ", Comp: %f" % (curr) start = time.time() if len(self.dont_complete_startswith) and ret: i = 0 while i < len(ret): disp = ret[i][0] pop = False for comp in self.dont_complete_startswith: if disp.startswith(comp): pop = True break if pop: ret.pop(i) else: i += 1 if self.time_completions: curr = (time.time() - start)*1000 tot += curr timing += ", Filter: %f" % (curr) timing += ", Tot: %f ms" % (tot) print(timing) sublime.status_message(timing) finally: tu.unlock() if not ret is None: return self.return_completions(ret, view) return self.return_completions([], view)
def return_completions(self, comp, view): if get_setting("inhibit_sublime_completions", True, view): return (comp, sublime.INHIBIT_WORD_COMPLETIONS | sublime.INHIBIT_EXPLICIT_COMPLETIONS) return comp
def display_compilation_results(view): tu = get_translation_unit(view) errString = "" show = False clear_error_marks() # clear visual error marks erase_error_marks(view) if tu == None: return if not tu.try_lock(): return try: errorCount = 0 warningCount = 0 ignoreDirs = [os.path.abspath(os.path.normpath(os.path.normcase(d))) for d in get_setting("diagnostic_ignore_dirs", [], view)] ignore_regex_str = get_setting("diagnostic_ignore_regex", "pragma once in main file") if ignore_regex_str: ignore_regex = re.compile(ignore_regex_str) else: ignore_regex = None if len(tu.var.diagnostics): errString = "" for diag in tu.var.diagnostics: f = diag.location filename = "" if f.file != None: filename = f.file.name if ignore_diagnostic(filename, ignoreDirs): continue err = "%s:%d,%d - %s - %s" % (filename, f.line, f.column, diag.severityName, diag.spelling) if ignore_regex and ignore_regex.search(err): continue try: if len(diag.disable_option) > 0: err = "%s [Disable with %s]" % (err, diag.disable_option) except AttributeError: pass if diag.severity == cindex.Diagnostic.Fatal and \ "not found" in diag.spelling: err = "%s\nDid you configure the include path used by clang properly?\n" \ "See http://github.com/quarnster/SublimeClang for more details on "\ "how to configure SublimeClang." % (err) errString = "%s%s\n" % (errString, err) if diag.severity == cindex.Diagnostic.Warning: warningCount += 1 elif diag.severity >= cindex.Diagnostic.Error: errorCount += 1 """ for range in diag.ranges: errString = "%s%s\n" % (errString, range) for fix in diag.fixits: errString = "%s%s\n" % (errString, fix) """ add_error_mark( diag.severityName, filename, f.line - 1, diag.spelling) show = errString and get_setting("show_output_panel", True, view) finally: tu.unlock() if (errorCount > 0 or warningCount > 0) and get_setting("show_status", True, view): statusString = "Clang Status: " if errorCount > 0: statusString = "%s%d Error%s" % (statusString, errorCount, "s" if errorCount != 1 else "") if warningCount > 0: statusString = "%s%s%d Warning%s" % (statusString, ", " if errorCount > 0 else "", warningCount, "s" if warningCount != 1 else "") view.set_status("SublimeClang", statusString) else: view.erase_status("SublimeClang") window = view.window() clang_error_panel.set_data(errString) update_statusbar(view) if not get_setting("error_marks_on_panel_only", False, view): show_error_marks(view) if not window is None: if show: window.run_command("clang_toggle_panel", {"show": True}) elif get_setting("hide_output_when_empty", False, view): if clang_error_panel.is_visible(): window.run_command("clang_toggle_panel", {"show": False})
def on_query_completions(self, view, prefix, locations): global clang_complete_enabled if not is_supported_language(view) or not clang_complete_enabled or \ not view.match_selector(locations[0], '-string -comment -constant'): return [] line = view.substr( sublime.Region(view.line(locations[0]).begin(), locations[0])) match = re.search(r"[,\s]*(\w+)\s+\w+$", line) if match != None: valid = [ "new", "delete", "return", "goto", "case", "const", "static", "class", "struct", "typedef", "union" ] if match.group(1) not in valid: # Probably a variable or function declaration # There's no point in trying to complete # a name that hasn't been typed yet... return self.return_completions([], view) timing = "" tot = 0 start = time.time() tu = get_translation_unit(view) if tu == None: return self.return_completions([], view) ret = None tu.lock() try: if self.time_completions: curr = (time.time() - start) * 1000 tot += curr timing += "TU: %f" % (curr) start = time.time() cached_results = None if clang_fast_completions and get_setting( "enable_fast_completions", True, view): data = view.substr(sublime.Region(0, locations[0])) try: cached_results = tu.cache.complete(data, prefix) except: traceback.print_exc() if cached_results != None: # print("found fast completions") ret = cached_results else: # print("doing slow completions") row, col = view.rowcol(locations[0] - len(prefix)) unsaved_files = [] if view.is_dirty(): unsaved_files.append((sencode(view.file_name()), view.substr(Region(0, view.size())))) ret = tu.cache.clangcomplete( sencode(view.file_name()), row + 1, col + 1, unsaved_files, is_member_completion(view, locations[0] - len(prefix))) if self.time_completions: curr = (time.time() - start) * 1000 tot += curr timing += ", Comp: %f" % (curr) start = time.time() if len(self.dont_complete_startswith) and ret: i = 0 while i < len(ret): disp = ret[i][0] pop = False for comp in self.dont_complete_startswith: if disp.startswith(comp): pop = True break if pop: ret.pop(i) else: i += 1 if self.time_completions: curr = (time.time() - start) * 1000 tot += curr timing += ", Filter: %f" % (curr) timing += ", Tot: %f ms" % (tot) print(timing) sublime.status_message(timing) finally: tu.unlock() if not ret is None: return self.return_completions(ret, view) return self.return_completions([], view)
def display_status(self): if get_setting("analyzer_status_messages", True): super(Analyzer, self).display_status()
def show_errors(self, view): if self.has_errors(view) and not get_setting("error_marks_on_panel_only", False, view): show_error_marks(view)
def set_data(self, data): self.data = sdecode(data) if get_setting("update_output_panel", True) and self.is_visible(): self.flush()
def suppress_based_on_match(message): suppress_strings = common.get_setting("diagnostic_suppress_match", []) for suppress in suppress_strings: if suppress in message: return True return False
def show_errors(self, view): if self.has_errors(view) and not get_setting( "error_marks_on_panel_only", False, view): show_error_marks(view)
def display_compilation_results(view): tu = get_translation_unit(view) errString = "" show = False clear_error_marks() # clear visual error marks erase_error_marks(view) if tu == None: return if not tu.try_lock(): return try: errorCount = 0 warningCount = 0 ignoreDirs = [ os.path.abspath(os.path.normpath(os.path.normcase(d))) for d in get_setting("diagnostic_ignore_dirs", [], view) ] ignore_regex_str = get_setting("diagnostic_ignore_regex", "pragma once in main file") if ignore_regex_str: ignore_regex = re.compile(ignore_regex_str) else: ignore_regex = None if len(tu.var.diagnostics): errString = "" for diag in tu.var.diagnostics: f = diag.location filename = "" if f.file != None: filename = f.file.name if ignore_diagnostic(filename, ignoreDirs): continue err = "%s:%d,%d - %s - %s" % (filename, f.line, f.column, diag.severityName, diag.spelling) if ignore_regex and ignore_regex.search(err): continue try: if len(diag.disable_option) > 0: err = "%s [Disable with %s]" % (err, diag.disable_option) except AttributeError: pass if diag.severity == cindex.Diagnostic.Fatal and \ "not found" in diag.spelling: err = "%s\nDid you configure the include path used by clang properly?\n" \ "See http://github.com/quarnster/SublimeClang for more details on "\ "how to configure SublimeClang." % (err) errString = "%s%s\n" % (errString, err) if diag.severity == cindex.Diagnostic.Warning: warningCount += 1 elif diag.severity >= cindex.Diagnostic.Error: errorCount += 1 """ for range in diag.ranges: errString = "%s%s\n" % (errString, range) for fix in diag.fixits: errString = "%s%s\n" % (errString, fix) """ add_error_mark(diag.severityName, filename, f.line - 1, diag.spelling) show = errString and get_setting("show_output_panel", True, view) finally: tu.unlock() if (errorCount > 0 or warningCount > 0) and get_setting( "show_status", True, view): statusString = "Clang Status: " if errorCount > 0: statusString = "%s%d Error%s" % (statusString, errorCount, "s" if errorCount != 1 else "") if warningCount > 0: statusString = "%s%s%d Warning%s" % ( statusString, ", " if errorCount > 0 else "", warningCount, "s" if warningCount != 1 else "") view.set_status("SublimeClang", statusString) else: view.erase_status("SublimeClang") window = view.window() clang_error_panel.set_data(errString) update_statusbar(view) if not get_setting("error_marks_on_panel_only", False, view): show_error_marks(view) if not window is None: if show: window.run_command("clang_toggle_panel", {"show": True}) elif get_setting("hide_output_when_empty", False, view): if clang_error_panel.is_visible(): window.run_command("clang_toggle_panel", {"show": False})
def suppress_based_on_location(source_file): suppress_dirs = common.get_setting("diagnostic_suppress_dirs", []) for d in suppress_dirs: if source_file in d: return True return False