def update(self, view, coverage=None): """ Updates a view with the coverage data in a particular file """ self.remove(view) if not coverage: return self.annotate_lines( view=view, name="SublimePHPCoverageGood", lines=coverage.good_lines, scope="markup.inserted", icon="dot" ) self.annotate_lines( view=view, name="SublimePHPCoverageBad", lines=coverage.bad_lines, scope="markup.deleted", icon="bookmark" ) try: percentage = 100 * coverage.covered / float(coverage.statements) except ZeroDivisionError: percentage = 0 status = "%d/%d lines (%.2f%%)" % (coverage.covered, coverage.statements, percentage) debug_message("Code coverage: %s" % status) view.set_status("SublimePHPCoveragePercentage", "Code coverage: %s" % status)
def update(self, view, coverage=None): """ Updates a view with the coverage data in a particular file """ self.remove(view) if not coverage: return self.annotate_lines( view=view, name='SublimePHPCoverageGood', lines=coverage.good_lines, scope='markup.inserted', icon='dot', ) self.annotate_lines( view=view, name='SublimePHPCoverageBad', lines=coverage.bad_lines, scope='markup.deleted', icon='bookmark', ) try: percentage = 100 * coverage.covered / float(coverage.statements) except ZeroDivisionError: percentage = 0 status = '%d/%d lines (%.2f%%)' % ( coverage.covered, coverage.statements, percentage) debug_message('Code coverage: %s' % status) view.set_status( 'SublimePHPCoveragePercentage', 'Code coverage: %s' % status)
def start(self): self.last_mtime = self.mtime() self.last_hash = self.hash() if self.last_mtime: debug_message("[FileWatcher] exists: %s" % self.filename) else: debug_message("[FileWatcher] doesn't exist: %s" % self.filename) super(FileWatcher, self).start()
def run(self, edit, coverage=None, **kwargs): filename = self.view.file_name() if not self.should_include(filename): debug_message("Ignoring excluded file '%s'" % filename) return if not coverage: coverage = self.coverage() update_view(self.view, coverage)
def get_setting(self, key): """ Gets a configuration value by key. """ if key in self.project: value = self.project.get(key) debug_message("[config] [project] %s: '%s'" % (key, value)) return value else: value = self.settings.get(key, None) debug_message("[config]: %s: '%s'" % (key, value)) return value
def load(self): """ Loads the settings from disk into structured data. """ debug_message('[config] Loading config') import sublime self.filename = "SublimePHPCoverage.sublime-settings" self.settings = sublime.load_settings(self.filename) self.project = {} if sublime.active_window() is not None: debug_message('[config] Window is active, loading project') project = sublime.active_window().active_view().settings() if project.has('phpcoverage'): debug_message("[config] Found project settings") project.clear_on_change('phpcoverage') self.project = project.get('phpcoverage') project.add_on_change('phpcoverage', config.load) else: debug_message("[config] No 'phpcoverage' key, ignoring") for key in self.keys: self.settings.clear_on_change(key) setattr(self, key, self.get_setting(key)) self.settings.add_on_change(key, config.load) self.loaded = True
def remove(self, view): """ Unregisters any set-up listeners for a recently closed view. """ # loop over the watchers for id, watcher in list(self.watchers.items()): # delete any callback related to this view watcher.remove_callback(view.id()) # if no more callbacks on this watcher, stop and remove it if not watcher.has_callbacks(): filename = watcher.filename debug_message("Stopping CoverageWatcher for '%s'" % filename) watcher.stop(1) del self.watchers[id]
def update_view(view, coverage=None): """ Updates the coverage data displayed in a view. Arguments are the view to update, and the coverage data. The coverage data should be a CoverageData object, which contains the relevant coverage data for the file shown in the view. If the coverage data doesn't exist for the file shown in the view, or the coverage data is None, then the displayed coverage data will be removed from the view. """ filename = view.file_name() debug_message('Updating coverage for ' + filename) file_coverage = coverage.get_file(filename) if coverage else None ViewUpdater().update(view, file_coverage)
def find(self, filename): """ Finds the coverage file for a given filename. """ # start from the file's directory parent, current = os.path.split(os.path.abspath(filename)) path = os.path.normcase(os.path.normpath(config.report_path)) # iterate through parent directories until coverage file found while current: coverage = os.path.join(parent, path) if os.path.exists(coverage): debug_message("Coverage for %s in %s" % (filename, coverage)) return coverage parent, current = os.path.split(parent) debug_message("Coverage file not found for " + str(filename)) return None
def dispatch(self, event): """ Dispatches an event, calling all relevant callbacks. """ callbacks = self.callbacks[event] debug_message("[FileWatcher] %s '%s'" % (event, self.filename)) debug_message("[FileWatcher] %d callbacks" % len(callbacks)) for callback in callbacks.values(): debug_message("[FileWatcher] Calling %s" % repr(callback)) callback()
def add(self, view): """ Sets up a watcher for a newly opened view. """ if not config.watch_report: return # find coverage file for the view's file filename = view.file_name() if filename is None: return if not self.matcher.should_include(filename): debug_message("Ignoring excluded file '%s'" % filename) return coverage = self.coverage_finder.find(filename) # nothing can be done if the coverage file can't be found if not coverage: return # ensure a CoverageWatcher exists for the coverage file if not coverage in self.watchers: debug_message("Creating CoverageWatcher for " + coverage) self.watchers[coverage] = CoverageWatcher(coverage) else: debug_message("Found existing CoverageWatcher for " + coverage) watcher = self.watchers[coverage] # add callbacks as defined at construction time, also adding in # the relevant view as a parameter to the callback for event, callback in self.callbacks.items(): wrapped = self.prepare_callback(callback, view) watcher.add_callback(event, view.id(), wrapped) # start the watcher if it's not already running if not watcher.is_alive(): debug_message("Starting CoverageWatcher for " + coverage) watcher.start()
def dispatch(self, event): """ Dispatches an event, calling all relevant callbacks. Overridden to pass coverage data to the callback, taken from the coverage file being watched by this CoverageWatcher. """ callbacks = self.callbacks[event] debug_message("[CoverageWatcher] %s '%s'" % (event, self.filename)) debug_message("[CoverageWatcher] %d callbacks" % len(callbacks)) data = self.get_coverage_factory().factory(self.filename) for callback in callbacks.values(): debug_message("[CoverageWatcher] Calling %s" % repr(callback)) callback(data)
def plugin_loaded(): """ Called automatically by Sublime when the API is ready to be used. Updates coverage for any open views, and adds them to the mediator. """ config.load() # add open views to the mediator for window in sublime.windows(): debug_message("[plugin_loaded] Found window %d" % window.id()) for view in window.views(): debug_message("[plugin_loaded] Found view %d" % view.id()) mediator.add(view) set_timeout_async( lambda: view.run_command('phpcoverage_update'), 1 ) debug_message("[plugin_loaded] Finished.")
import sublime from php_coverage.debug import debug_message sublime3 = int(sublime.version()) >= 3000 if sublime3: set_timeout_async = sublime.set_timeout_async else: debug_message("Adding Sublime 3 polyfills") set_timeout_async = sublime.set_timeout