1# -*- coding: utf-8 -*- 2# 3# doc.py - doc commander module 4# 5# Copyright (C) 2010 - Jesse van den Kieboom 6# 7# This program is free software; you can redistribute it and/or modify 8# it under the terms of the GNU General Public License as published by 9# the Free Software Foundation; either version 2 of the License, or 10# (at your option) any later version. 11# 12# This program is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15# GNU General Public License for more details. 16# 17# You should have received a copy of the GNU General Public License 18# along with this program; if not, write to the Free Software 19# Foundation, Inc., 51 Franklin Street, Fifth Floor, 20# Boston, MA 02110-1301, USA. 21 22import commander.commands as commands 23import commander.commands.completion 24import commander.commands.result 25import commander.commands.exceptions 26import re 27 28__commander_module__ = True 29 30class Argument: 31 def __init__(self, argtype, typename, name): 32 self.type = argtype.strip() 33 self.type_name = typename.strip() 34 self.name = name.strip() 35 36class Function: 37 def __init__(self, text): 38 self._parse(text) 39 40 def _parse(self, text): 41 self.valid = False 42 43 parser = re.compile('^\\s*(?:(?:\\b(?:static|inline)\\b)\\s+)?(([a-z_:][a-z0-9_:<>]*)(?:\\s*(?:\\b(?:const)\\b)\\s*)?\\s*[*&]*\\s+)?([a-z_][a-z0-9_:~]*)\\s*\\(([^)]*)\\)(\\s*const)?', re.I) 44 45 m = parser.match(text) 46 47 if not m: 48 return 49 50 self.valid = True 51 52 self.return_type = m.group(1) and m.group(1).strip() != 'void' and m.group(1).strip() 53 self.return_type_name = self.return_type and m.group(2).strip() 54 55 parts = m.group(3).split('::') 56 self.name = parts[-1] 57 58 if len(parts) > 1: 59 self.classname = '::'.join(parts[0:-1]) 60 else: 61 self.classname = None 62 63 self.constructor = self.name == self.classname 64 self.destructor = self.name == '~%s' % (self.classname,) 65 66 self.const = m.group(5) != None 67 self.args = [] 68 69 argre = re.compile('(([a-z_:][a-z0-9_:<>]*)(?:\\s*(?:\\s*\\bconst\\b\\s*|[*&])\s*)*)\\s*([a-z_][a-z_0-9]*)$', re.I) 70 71 for arg in m.group(4).split(','): 72 arg = arg.strip() 73 74 if arg == 'void' or arg == '': 75 continue 76 else: 77 m2 = argre.match(arg.strip()) 78 79 if not m2: 80 self.valid = False 81 return 82 83 arg = Argument(m2.group(1), m2.group(2), m2.group(3)) 84 85 self.args.append(arg) 86 87class Documenter: 88 def __init__(self, window, view, iter): 89 self.window = window 90 self.view = view 91 self.iter = iter 92 93 bus = self.window.get_message_bus() 94 self.canplaceholder = bus.is_registered('/plugins/snippets', 'parse-and-activate') 95 96 self.placeholder = 1 97 self.text = '' 98 99 def append(self, *args): 100 for text in args: 101 self.text += str(text) 102 103 return self 104 105 def append_placeholder(self, *args): 106 if not self.canplaceholder: 107 return self.append(*args) 108 109 text = " ".join(map(lambda x: str(x), args)) 110 self.text += "${%d:%s}" % (self.placeholder, text) 111 self.placeholder += 1 112 113 return self 114 115 def insert(self): 116 if self.canplaceholder: 117 bus = self.window.get_message_bus() 118 bus.send('/plugins/snippets', 'parse-and-activate', snippet=self.text, iter=self.iter, view=self.view) 119 120def _make_documenter(window, view): 121 buf = view.get_buffer() 122 123 bus = window.get_message_bus() 124 canplaceholder = bus.lookup('/plugins/snippets', 'parse-and-activate') != None 125 126 insert = buf.get_iter_at_mark(buf.get_insert()) 127 insert.set_line_offset(0) 128 129 offset = insert.get_offset() 130 131 end = insert.copy() 132 133 # This is just something random 134 if not end.forward_chars(500): 135 end = buf.get_end_iter() 136 137 text = insert.get_text(end) 138 func = Function(text) 139 140 if not func.valid: 141 raise commander.commands.exceptions.Execute('Could not find function specification') 142 143 doc = Documenter(window, view, insert) 144 return doc, func 145 146def gtk(window, view): 147 """Generate gtk-doc documentation: doc.gtk 148 149Generate a documentation template for the C or C++ function defined at the 150cursor. The cursor needs to be on the first line of the function declaration 151for it to work.""" 152 153 buf = view.get_buffer() 154 lang = buf.get_language() 155 156 if not lang or not lang.get_id() in ('c', 'chdr', 'cpp'): 157 raise commander.commands.exceptions.Execute('Don\'t know about this language') 158 159 doc, func = _make_documenter(window, view) 160 161 # Generate docstring for this function 162 doc.append("/**\n * ", func.name, ":\n") 163 structp = re.compile('([A-Z]+[a-zA-Z]*)|struct\s+_([A-Z]+[a-zA-Z]*)') 164 165 for arg in func.args: 166 sm = structp.match(arg.type_name) 167 doc.append(" * @", arg.name, ": ") 168 169 if sm: 170 doc.append_placeholder("a #%s" % (sm.group(1) or sm.group(2),)) 171 else: 172 doc.append_placeholder("Description") 173 174 doc.append(".\n") 175 176 doc.append(" *\n * ").append_placeholder("Description").append(".\n") 177 178 if func.return_type: 179 sm = structp.match(func.return_type_name) 180 doc.append(" *\n * Returns: ") 181 182 if sm: 183 doc.append_placeholder("a #%s" % (sm.group(1) or sm.group(2),)) 184 else: 185 doc.append_placeholder("Description") 186 187 doc.append(".\n") 188 189 doc.append(" *\n **/\n") 190 doc.insert() 191 192def doxygen(window, view): 193 """Generate doxygen documentation: doc.doxygen 194 195Generate a documentation template for the function defined at the 196cursor. The cursor needs to be on the first line of the function declaration 197for it to work.""" 198 199 buf = view.get_buffer() 200 201 if not buf.get_language().get_id() in ('c', 'chdr', 'cpp'): 202 raise commander.commands.exceptions.Execute('Don\'t know about this language') 203 204 doc, func = _make_documenter(window, view) 205 206 # Generate docstring for this function 207 doc.append("/** \\brief ").append_placeholder("Short description") 208 209 if func.const: 210 doc.append(" (const)") 211 212 doc.append(".\n") 213 214 for arg in func.args: 215 doc.append(" * @param ", arg.name, " ").append_placeholder("Description").append("\n") 216 217 doc.append(" *\n * ") 218 219 if func.constructor: 220 doc.append("Constructor.\n *\n * ") 221 elif func.destructor: 222 doc.append("Destructor.\n *\n * ") 223 224 doc.append_placeholder("Detailed description").append(".\n") 225 226 if func.return_type: 227 doc.append(" *\n * @return: ") 228 229 if func.return_type == 'bool': 230 doc.append("true if ").append_placeholder("Description").append(", false otherwise") 231 else: 232 doc.append_placeholder("Description") 233 234 doc.append("\n") 235 236 doc.append(" *\n */\n") 237 doc.insert() 238 239# ex:ts=4:et 240