Attachment 'reindent.py'

Download

   1 import gedit
   2 __version__ = "1"
   3 
   4 import tokenize
   5 import os
   6 import sys
   7 import gtk
   8 
   9 verbose = 0
  10 recurse = 0
  11 dryrun  = 0
  12 
  13 
  14 def _rstrip(line, JUNK='\n \t'):
  15     """Return line stripped of trailing spaces, tabs, newlines.
  16 
  17     Note that line.rstrip() instead also strips sundry control characters,
  18     but at least one known Emacs user expects to keep junk like that, not
  19     mentioning Barry by name or anything <wink>.
  20     """
  21 
  22     i = len(line)
  23     while i > 0 and line[i-1] in JUNK:
  24         i -= 1
  25     return line[:i]
  26 
  27 class Reindenter:
  28 
  29     def __init__(self, text):
  30         self.find_stmt = 1  # next token begins a fresh stmt?
  31         self.level = 0      # current indent level
  32 
  33         # Raw file lines.
  34         self.raw = text
  35 
  36         # File lines, rstripped & tab-expanded.  Dummy at start is so
  37         # that we can use tokenize's 1-based line numbering easily.
  38         # Note that a line is all-blank iff it's "\n".
  39         self.lines = [_rstrip(line).expandtabs() + "\n"
  40                       for line in self.raw]
  41         self.lines.insert(0, None)
  42         self.index = 1  # index into self.lines of next line
  43 
  44         # List of (lineno, indentlevel) pairs, one for each stmt and
  45         # comment line.  indentlevel is -1 for comment lines, as a
  46         # signal that tokenize doesn't know what to do about them;
  47         # indeed, they're our headache!
  48         self.stats = []
  49 
  50     def run(self):
  51         tokenize.tokenize(self.getline, self.tokeneater)
  52         # Remove trailing empty lines.
  53         lines = self.lines
  54         while lines and lines[-1] == "\n":
  55             lines.pop()
  56         # Sentinel.
  57         stats = self.stats
  58         stats.append((len(lines), 0))
  59         # Map count of leading spaces to # we want.
  60         have2want = {}
  61         # Program after transformation.
  62         after = self.after = []
  63         # Copy over initial empty lines -- there's nothing to do until
  64         # we see a line with *something* on it.
  65         i = stats[0][0]
  66         after.extend(lines[1:i])
  67         for i in range(len(stats)-1):
  68             thisstmt, thislevel = stats[i]
  69             nextstmt = stats[i+1][0]
  70             have = getlspace(lines[thisstmt])
  71             want = thislevel * 4
  72             if want < 0:
  73                 # A comment line.
  74                 if have:
  75                     # An indented comment line.  If we saw the same
  76                     # indentation before, reuse what it most recently
  77                     # mapped to.
  78                     want = have2want.get(have, -1)
  79                     if want < 0:
  80                         # Then it probably belongs to the next real stmt.
  81                         for j in xrange(i+1, len(stats)-1):
  82                             jline, jlevel = stats[j]
  83                             if jlevel >= 0:
  84                                 if have == getlspace(lines[jline]):
  85                                     want = jlevel * 4
  86                                 break
  87                     if want < 0:           # Maybe it's a hanging
  88                                            # comment like this one,
  89                         # in which case we should shift it like its base
  90                         # line got shifted.
  91                         for j in xrange(i-1, -1, -1):
  92                             jline, jlevel = stats[j]
  93                             if jlevel >= 0:
  94                                 want = have + getlspace(after[jline-1]) - \
  95                                        getlspace(lines[jline])
  96                                 break
  97                     if want < 0:
  98                         # Still no luck -- leave it alone.
  99                         want = have
 100                 else:
 101                     want = 0
 102             assert want >= 0
 103             have2want[have] = want
 104             diff = want - have
 105             if diff == 0 or have == 0:
 106                 after.extend(lines[thisstmt:nextstmt])
 107             else:
 108                 for line in lines[thisstmt:nextstmt]:
 109                     if diff > 0:
 110                         if line == "\n":
 111                             after.append(line)
 112                         else:
 113                             after.append(" " * diff + line)
 114                     else:
 115                         remove = min(getlspace(line), -diff)
 116                         after.append(line[remove:])
 117         return self.raw != self.after
 118 
 119 
 120     # Line-getter for tokenize.
 121     def getline(self):
 122         if self.index >= len(self.lines):
 123             line = ""
 124         else:
 125             line = self.lines[self.index]
 126             self.index += 1
 127         return line
 128 
 129     # Line-eater for tokenize.
 130     def tokeneater(self, type, token, (sline, scol), end, line,
 131                    INDENT=tokenize.INDENT,
 132                    DEDENT=tokenize.DEDENT,
 133                    NEWLINE=tokenize.NEWLINE,
 134                    COMMENT=tokenize.COMMENT,
 135                    NL=tokenize.NL):
 136 
 137         if type == NEWLINE:
 138             # A program statement, or ENDMARKER, will eventually follow,
 139             # after some (possibly empty) run of tokens of the form
 140             #     (NL | COMMENT)* (INDENT | DEDENT+)?
 141             self.find_stmt = 1
 142 
 143         elif type == INDENT:
 144             self.find_stmt = 1
 145             self.level += 1
 146 
 147         elif type == DEDENT:
 148             self.find_stmt = 1
 149             self.level -= 1
 150 
 151         elif type == COMMENT:
 152             if self.find_stmt:
 153                 self.stats.append((sline, -1))
 154                 # but we're still looking for a new stmt, so leave
 155                 # find_stmt alone
 156 
 157         elif type == NL:
 158             pass
 159 
 160         elif self.find_stmt:
 161             # This is the first "real token" following a NEWLINE, so it
 162             # must be the first token of the next program statement, or an
 163             # ENDMARKER.
 164             self.find_stmt = 0
 165             if line:   # not endmarker
 166                 self.stats.append((sline, self.level))
 167 
 168 # Count number of leading blanks.
 169 def getlspace(line):
 170     i, n = 0, len(line)
 171     while i < n and line[i] == " ":
 172         i += 1
 173     return i
 174 
 175 class ReindentPython(gedit.Plugin):
 176         def __init__(self):
 177             gedit.Plugin.__init__(self)
 178 
 179         def activate(self, window):
 180             actions = [
 181                     ("Reindent", None, "Reindent code", None,"Reindent code following PEP008 Guideline", self.reindent)] 
 182             windowdata = dict()
 183             window.set_data("ReindentPluginWindowDataKey", windowdata)
 184             windowdata["reindent_action_group"] = gtk.ActionGroup("GeditReindentPluginActions")
 185             windowdata["reindent_action_group"].add_actions(actions, window)
 186             manager = window.get_ui_manager()
 187             manager.insert_action_group(windowdata["reindent_action_group"], -1)
 188             windowdata["ui_id"] = manager.new_merge_id ()
 189             
 190             action = gtk.ActionGroup("GeditReindentPluginActions")
 191             manager = window.get_ui_manager()
 192             manager.insert_action_group(action, -1)
 193             submenu = """
 194                 <ui>
 195                   <menubar name='MenuBar'>
 196                     <menu name='ToolsMenu' action='Tools'>
 197                       <placeholder name='ToolsOps_2'>
 198                         <menuitem action='Reindent'/>
 199                     <separator/>
 200                   </placeholder>
 201                 </menu>
 202               </menubar>
 203             </ui>"""
 204             manager.add_ui_from_string(submenu)
 205 
 206 
 207         def deactivate(self, window):
 208             windowdata = window.get_data("ReindentPluginWindowDataKey")
 209             manager = window.get_ui_manager()
 210             manager.remove_ui(windowdata["ui_id"])
 211             manager.remove_action_group(windowdata["reindent_action_group"])
 212 
 213         def update_ui(self, window):
 214             view = window.get_active_view()
 215             windowdata = window.get_data("ReindentPluginWindowDataKey") 
 216             windowdata["reindent_action_group"].set_sensitive(bool(view and view.get_editable()))
 217         
 218         def reindent(self, widget, window):
 219             doc = window.get_active_document()
 220             bounds = doc.get_bounds()
 221             text = doc.get_text(*bounds)
 222             text_array = text.split("\n")
 223             r = Reindenter(text_array)
 224             r.run()
 225             text = ""
 226             for i in r.after:
 227                 text += i   
 228             doc.set_text(text)

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.
  • [get | view] (2021-02-25 09:43:45, 0.3 KB) [[attachment:reindent.gedit-plugin]]
  • [get | view] (2021-02-25 09:43:45, 8.1 KB) [[attachment:reindent.py]]
  • [get | view] (2021-02-25 09:43:45, 6.2 KB) [[attachment:reindent.pyc]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.