import gedit import os, gobject, gtk, re from subprocess import * JSLINTJS = os.path.join(os.path.split(__file__)[0], "jslint.js") LINERE = re.compile(r"^Lint at line ([0-9]+) character ([0-9]+): (.*)$") class JSLintPlugin(gedit.Plugin): def activate(self, window): handler_id = window.connect("tab-added", self.on_window_tab_added) window.set_data(self.__class__.__name__, handler_id) for doc in window.get_documents(): self.connect_document(doc) def deactivate(self, window): name = self.__class__.__name__ handler_id = window.get_data(name) window.disconnect(handler_id) window.set_data(name, None) for doc in window.get_documents(): handler_id = doc.get_data(name) doc.disconnect(handler_id) doc.set_data(name, None) def update_ui(self, window): pass def connect_document(self, doc): handler_id = doc.connect("saved", self.on_document_saved) doc.set_data(self.__class__.__name__, handler_id) def on_window_tab_added(self, window, tab): self.connect_document(tab.get_document()) def on_document_saved(self, doc, *args): if doc.get_language() and doc.get_language().get_name() == "Javascript": # jslint cmdlist = ["rhino",JSLINTJS,doc.get_uri_for_display()] output = Popen(cmdlist, stdout=PIPE).communicate()[0] errors = 0 for line in output.split("\n"): if line.startswith("Lint at line "): errors += 1 sb = gedit.app_get_default().get_active_window().get_statusbar() context_id = sb.get_context_id("jslint") sb.push(context_id, "%s jslint errors" % errors) self.display_errors(output, doc) def display_errors(self, jslintoutput, doc): if not hasattr(self, "pane"): self.errorlines = gtk.ListStore(str,str,str) self.pane = gtk.ScrolledWindow() treeview = gtk.TreeView(model=self.errorlines) lineno = gtk.TreeViewColumn('Line') charno = gtk.TreeViewColumn('Char') message = gtk.TreeViewColumn('Message') treeview.append_column(lineno) treeview.append_column(charno) treeview.append_column(message) cell1 = gtk.CellRendererText() cell2 = gtk.CellRendererText() cell3 = gtk.CellRendererText() lineno.pack_start(cell1,True) charno.pack_start(cell2, True) message.pack_start(cell3, True) lineno.set_attributes(cell1, text=0) charno.set_attributes(cell2, text=1) message.set_attributes(cell3, text=2) win = gedit.app_get_default().get_active_window() bottom = win.get_bottom_panel() image = gtk.Image() image.set_from_icon_name('stock_mark', gtk.ICON_SIZE_MENU) self.pane.add(treeview) bottom.add_item(self.pane, 'JSLint', image) treeview.connect("row-activated", self.row_clicked, doc) self.pane.show_all() self.errorlines.clear() self.lines = [] for line in jslintoutput.split("\n"): mtch = LINERE.match(line) if not mtch: continue lineno, charno, message = mtch.groups() self.errorlines.append([lineno, charno, message]) self.lines.append([int(lineno), int(charno)]) def row_clicked(self, treeview, path, view_column, doc): lineno, charno = self.lines[path[0]] tab = gedit.tab_get_from_document(doc) view = tab.get_view() bf = view.get_buffer() col = charno - 1 line = lineno - 1 if col < get_num_cols_at_line(bf, line): it = bf.get_iter_at_line_offset(line, col) else: it = bf.get_iter_at_line(line) (start, end) = bf.get_bounds() bf.place_cursor(it) view.scroll_to_iter(end, within_margin=0.25, use_align=False) view.scroll_to_iter(it, within_margin=0.25, use_align=False) view.grab_focus() # from html-tidy gedit plugin by mig def get_num_cols_at_line(bf, line): """ Retrieves the number of columns in a given line of a gtk.TextBuffer. Keyword arguments: bf -- The gtk.TextBuffer object. line -- The line number. """ line_start_it = bf.get_iter_at_line(line) it = line_start_it count = 0 while not it.is_end() and it.get_char() != '\n': count = count + 1 it.forward_char() return count