├── Makefile ├── README.md ├── folding.plugin └── folding.py /Makefile: -------------------------------------------------------------------------------- 1 | GEDIT_PLUGIN_DIR = ~/.local/share/gedit/plugins 2 | 3 | install: 4 | @if [ ! -d $(GEDIT_PLUGIN_DIR) ]; then \ 5 | mkdir -p $(GEDIT_PLUGIN_DIR);\ 6 | fi 7 | @echo "installing folding plugin"; 8 | @rm -rf $(GEDIT_PLUGIN_DIR)/folding*; 9 | @cp -R folding* $(GEDIT_PLUGIN_DIR); 10 | 11 | uninstall: 12 | @echo "uninstalling folding plugin"; 13 | @rm -rf $(GEDIT_PLUGIN_DIR)/folding*; 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Code Folding for Gedit 2 | ======================== 3 | 4 | A simple plugin that adds keyboard-based code folding to Gedit. 5 | 6 | Installation 7 | -------------- 8 | 9 | #### Unix/Linux 10 | * Simplest: Move `folding.plugin` and `folding.py` into `~/.local/share/gedit/plugins` 11 | * More convenient: 12 | ``` 13 | cd ~/.local/share/gedit/plugins 14 | git clone https://github.com/aeischeid/gedit-folding 15 | ``` 16 | You can replace aeischeid by other developer - pick active one: https://github.com/aeischeid/gedit-folding/network 17 | 18 | Then 19 | 20 | * In Gedit, go to Edit → Preferences → Plugins to enable the plugin. 21 | 22 | #### Windows 23 | 24 | **Note**: As stated in IRC conversation at #gedit public channel, gedit3 is not ready for Windows (yet). Eventually this installation section will be updated when gedit3 supports Windows. 25 | 26 | Usage 27 | -------- 28 | 29 | * `Alt-Z` on selected lines will collapse them 30 | * `Alt-Z` on an indented block's top line will collapse that block 31 | * `Alt-Z` on a folded block will expand it 32 | * `Alt-X` will collapse all blocks on the deepest indention column (you can keep pressing Alt-X until all indention levels are folded) 33 | * `Shift-Alt-X` will expand all the collapsed blocks 34 | -------------------------------------------------------------------------------- /folding.plugin: -------------------------------------------------------------------------------- 1 | [Plugin] 2 | Loader=python3 3 | Module=folding 4 | IAge=3 5 | Name=Folding 6 | Description=Highlight the text and press Alt-Z to collapse. Put the cursor at the start of a code block (such as def, if, class, etc.) and press Alt-Z to collapse a block of indented text. Put the cursor on a folded line and press Alt-Z to expand. Press Alt-X to collapse code block columns until all code is folded. Press Shift-Alt-X to expand all the collapsed blocks. 7 | Description[ru]=Выделите текст и нажмите Alt-Z для сворачивания. Поставьте курсор на строку, после которой идет текст с отступом (например def, if, class и т.п.) и нажмите Alt-Z чтобы свернуть блок текста с отступом. Поставьте курсор в свернутую строку и нажмите Alt-Z для разворачивания. Нажмите Shift-Alt-X для разворачивания всех свернутых блоков. 8 | Authors=Kawaikunee 9 | Copyright=Copyright © 2009 Kawaikunee 10 | Website=https://github.com/aeischeid/gedit-folding 11 | -------------------------------------------------------------------------------- /folding.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from gi.repository import GObject, Gedit, Gio 3 | 4 | actions = [ 5 | ("fold", "Z", "Fold/Unfold"), 6 | ("fold_deepest", "X", "Fold Deepest"), 7 | ("unfold_all", "X", "Un-Fold All") 8 | ] 9 | 10 | 11 | class FoldingPyPluginAppActivatable(GObject.Object, Gedit.AppActivatable): 12 | 13 | app = GObject.property(type=Gedit.App) 14 | 15 | def do_activate(self): 16 | self.menu_ext = self.extend_menu("tools-section") 17 | for action_name, key, menu_name in actions: 18 | fullname = "win." + action_name 19 | self.app.add_accelerator(key, fullname, None) 20 | item = Gio.MenuItem.new(_(menu_name), fullname) 21 | self.menu_ext.append_menu_item(item) 22 | 23 | def do_deactivate(self): 24 | for action_name, key, menu_name in actions: 25 | self.app.remove_accelerator("win." + action_name, None) 26 | self.menu_ext = None 27 | 28 | 29 | class FoldingPyPlugin(GObject.Object, Gedit.WindowActivatable): 30 | __gtype_name__ = 'FoldingPyPlugin' 31 | window = GObject.property(type=Gedit.Window) 32 | 33 | def __init__(self): 34 | GObject.Object.__init__(self) 35 | 36 | def do_activate(self): 37 | self.do_update_state() 38 | for action_name, key, menu_name in actions: 39 | action = Gio.SimpleAction(name=action_name) 40 | action.connect('activate', getattr(self, action_name)) 41 | self.window.add_action(action) 42 | 43 | def do_update_state(self): 44 | self.doc = self.window.get_active_document() 45 | if self.doc: 46 | self.view = self.window.get_active_view() 47 | table = self.doc.get_tag_table() 48 | self.fld = table.lookup('fld') 49 | if self.fld is None: 50 | self.fld = self.doc.create_tag( 51 | 'fld', 52 | foreground="#333333", 53 | paragraph_background="#aadc5c" 54 | ) 55 | self.inv = table.lookup('inv') 56 | if self.inv is None: 57 | self.inv = self.doc.create_tag('inv', invisible=True) 58 | 59 | def detect_sps(self, sps): 60 | sps_lstrip = sps.lstrip() 61 | i = sps.index(sps_lstrip) 62 | sps = sps[:i] 63 | return sps.count(' ') + sps.count('\t') * self.view.get_tab_width() 64 | 65 | def fold_deepest(self, action, data=None): 66 | deepest = 0 67 | lines = list() 68 | s = self.doc.get_iter_at_line(0) 69 | e = s.copy() 70 | sg = 0 71 | eg = 0 72 | while s.forward_visible_line(): 73 | if s.get_char() == "\n": 74 | continue 75 | e.set_line(s.get_line()) 76 | e.forward_to_line_end() 77 | text = s.get_text(e) 78 | if not text.strip(): 79 | continue 80 | indent = self.detect_sps(text) 81 | if not indent: 82 | continue 83 | if indent > deepest: 84 | deepest = indent 85 | lines = list() 86 | sg = s.get_line() 87 | eg = s.get_line() 88 | elif indent < deepest and eg: 89 | lines.append((sg-1, eg)) 90 | eg = 0 91 | elif indent == deepest: 92 | if not eg: 93 | sg = s.get_line() 94 | eg = s.get_line() 95 | if eg: 96 | lines.append((sg-1, eg)) 97 | for (sg, eg) in lines: 98 | s.set_line(sg) 99 | e.set_line(eg) 100 | self.fold(None, s, e) 101 | 102 | def unfold_all(self, action, data=None): 103 | s, e = self.doc.get_bounds() 104 | self.doc.remove_tag(self.fld, s, e) 105 | self.doc.remove_tag(self.inv, s, e) 106 | 107 | def fold(self, action, a=None, c=None): 108 | if a is None: 109 | a = self.doc.get_iter_at_mark(self.doc.get_insert()) 110 | if a.has_tag(self.fld): 111 | try: 112 | a.set_line_offset(0) 113 | b = a.copy() 114 | b.forward_line() 115 | self.doc.remove_tag(self.fld, a, b) 116 | a.forward_to_tag_toggle(self.inv) 117 | b.forward_to_tag_toggle(self.inv) 118 | self.doc.remove_tag(self.inv, a, b) 119 | except: 120 | pass 121 | elif ( 122 | a is not None 123 | and c is not None # and is stronger than or 124 | or len(self.doc.get_selection_bounds()) == 2 125 | ): 126 | if c is None: 127 | a, c = self.doc.get_selection_bounds() 128 | if a.get_line() == c.get_line(): 129 | return 130 | b = a.copy() 131 | a.set_line_offset(0) 132 | b.forward_line() 133 | c.forward_line() 134 | self.doc.apply_tag(self.fld, a, b) 135 | # TODO: Don't remove already folded tags 136 | # and keep track of nested tags 137 | self.doc.remove_tag(self.fld, b, c) 138 | self.doc.remove_tag(self.inv, b, c) 139 | self.doc.apply_tag(self.inv, b, c) 140 | else: 141 | a.set_line_offset(0) 142 | line = a.get_line() 143 | sfold = a.copy() 144 | sfold.forward_line() 145 | text = a.get_text(sfold) 146 | if text.strip(): 147 | main_indent = self.detect_sps(text) 148 | fin = a.copy() 149 | e = a.copy() 150 | while 1 == 1: 151 | if not e.forward_line(): 152 | fin.forward_to_end() 153 | line = fin.get_line() 154 | break 155 | if e.get_char() == "\n": 156 | continue 157 | ne = e.copy() 158 | ne.forward_to_line_end() 159 | text = e.get_text(ne) 160 | if text.strip() == "": 161 | continue 162 | child_indent = self.detect_sps(text) 163 | if child_indent <= main_indent: 164 | break 165 | line = e.get_line() 166 | fin.set_line(line) 167 | fin.forward_line() 168 | 169 | if a.get_line() < line: 170 | self.doc.apply_tag(self.fld, a, sfold) 171 | # TODO: Don't remove already folded tags and 172 | # keep track of nested tags 173 | self.doc.remove_tag(self.fld, sfold, fin) 174 | self.doc.remove_tag(self.inv, sfold, fin) 175 | self.doc.apply_tag(self.inv, sfold, fin) 176 | 177 | --------------------------------------------------------------------------------