├── README ├── extractor ├── extractor.php └── sqlite.php └── plugin ├── phpcompletion.gedit-plugin └── phpcompletion ├── __init__.py ├── phpdb.py ├── phpproposals.py ├── phpprovider.py ├── phpsymbols.db └── utils.py /README: -------------------------------------------------------------------------------- 1 | Gedit plugin providing static PHP symbol completion. This plugin uses 2 | gtksourcecompletion (GtkSourceView >= 2.9) to provide automatic completion 3 | of static PHP symbols (functions, classes). 4 | 5 | To install, from the cloned repository: 6 | 7 | cp -r plugin/* $HOME/.gnome2/gedit/plugins/ 8 | 9 | If you want to generate your own symbol database, you can use the extractor 10 | script in 'extractor'. 11 | -------------------------------------------------------------------------------- /extractor/extractor.php: -------------------------------------------------------------------------------- 1 | dom = null; 9 | $this->ref = $ref; 10 | 11 | $prefix = '/usr/share/doc/php-doc/html/'; 12 | 13 | if (is_a($ref, 'ReflectionMethod')) 14 | { 15 | $class = $ref->class; 16 | $name = $ref->name; 17 | 18 | $name = preg_replace('/^_+/', '', $name); 19 | $filename = $prefix . strtolower($class) . '.' . strtolower($name) . '.html'; 20 | } 21 | else if (is_a($ref, 'ReflectionFunction')) 22 | { 23 | 24 | $filename = $prefix . 'function.' . str_replace('_', '-', $ref->getName()) . '.html'; 25 | } 26 | else 27 | { 28 | return; 29 | } 30 | 31 | $this->dom = new DOMDocument(); 32 | $this->dom->substituteEntities = true; 33 | $this->dom->preserveWhitespace = false; 34 | 35 | if (!@$this->dom->loadHTMLFile($filename)) 36 | { 37 | $this->dom = null; 38 | } 39 | else 40 | { 41 | $this->parse(); 42 | } 43 | } 44 | 45 | function documentation_found() 46 | { 47 | return $this->dom != null; 48 | } 49 | 50 | function short_description() 51 | { 52 | return $this->doc['short_description']; 53 | } 54 | 55 | function description() 56 | { 57 | return $this->doc['description']; 58 | } 59 | 60 | function arguments() 61 | { 62 | return $this->doc['arguments']; 63 | } 64 | 65 | private function node_text_normalize($text) 66 | { 67 | $text = str_replace("\n", ' ', $text); 68 | return trim(preg_replace('/\s+/', ' ', $text)); 69 | } 70 | 71 | private function valid_markup($node, &$tag) 72 | { 73 | $markups = array( 74 | 'i' => 'i', 75 | 'b' => 'b', 76 | 'strong' => 'b', 77 | 'em' => 'i' 78 | ); 79 | 80 | if (array_key_exists($node->nodeName, $markups)) 81 | { 82 | $tag = $markups[$node->nodeName]; 83 | return true; 84 | } 85 | 86 | return false; 87 | } 88 | 89 | private function escape($text) 90 | { 91 | return htmlentities($text); 92 | } 93 | 94 | private function node_text($node, $select = null) 95 | { 96 | if (is_a($node, 'DOMText')) 97 | { 98 | return $this->node_text_normalize($this->escape($node->nodeValue)); 99 | } 100 | 101 | $text = ''; 102 | $except = array(); 103 | 104 | if ($select != null) 105 | { 106 | $xpath = new DOMXPath($this->dom); 107 | $ret = $xpath->query($select, $node); 108 | 109 | foreach ($ret as $child) 110 | { 111 | $except[] = $child; 112 | } 113 | } 114 | 115 | foreach ($node->childNodes as $child) 116 | { 117 | $exception = false; 118 | 119 | foreach ($except as $ex) 120 | { 121 | if ($ex->isSameNode($child)) 122 | { 123 | $exception = true; 124 | break; 125 | } 126 | } 127 | 128 | if ($exception) 129 | { 130 | continue; 131 | } 132 | 133 | $node_text = $this->node_text($child); 134 | $tag = ''; 135 | 136 | if ($text != '') 137 | { 138 | $text .= ' '; 139 | } 140 | 141 | if ($this->valid_markup($child, $tag)) 142 | { 143 | $text .= '<' . $tag . '>' . $node_text . ''; 144 | } 145 | else 146 | { 147 | $text .= $node_text; 148 | } 149 | } 150 | 151 | return $this->node_text_normalize($text); 152 | } 153 | 154 | private function extract_text($path, $node = null, $all = false) 155 | { 156 | $xpath = new DOMXPath($this->dom); 157 | $ret = $xpath->query($path, $node !== null ? $node : $this->dom); 158 | 159 | if ($ret->length == 0) 160 | { 161 | return ''; 162 | } 163 | 164 | $text = ''; 165 | 166 | foreach ($ret as $child) 167 | { 168 | $text .= ' ' . $this->node_text($child); 169 | 170 | if (!$all) 171 | { 172 | break; 173 | } 174 | } 175 | 176 | return $this->node_text_normalize($text); 177 | } 178 | 179 | function extract_argument_info() 180 | { 181 | if (!$this->dom) 182 | { 183 | return array(); 184 | } 185 | 186 | $xpath = new DOMXPath($this->dom); 187 | $names = $xpath->query('//div[@class="methodsynopsis dc-description"]'); 188 | 189 | if ($names->length == 0) 190 | { 191 | return array(); 192 | } 193 | 194 | $content = $this->dom->saveXML($names->item(0)); 195 | $arguments = array(); 196 | 197 | if (preg_match_all('/(.*?)<\/span>\s*(.*?)<\/tt><\/span>/', $content, $matches, PREG_SET_ORDER)) 198 | { 199 | foreach ($matches as $match) 200 | { 201 | if (preg_match('/(.*?)<\/a>/', $match[1], $types)) 202 | { 203 | $type = $types[1]; 204 | } 205 | else 206 | { 207 | $type = $match[1]; 208 | } 209 | 210 | $name = $match[2][0] == '$' ? substr($match[2], 1) : $match[2]; 211 | $arguments[] = array('type' => $type, 'name' => $name); 212 | } 213 | } 214 | 215 | // Little hack to get which arguments are optional 216 | $content = preg_replace('/[^,[\]]/', '', $content); 217 | $parts = explode(',', $content); 218 | 219 | for ($i = 0; $i < count($parts); $i++) 220 | { 221 | $arguments[$i]['optional'] = !($parts[$i] == ''); 222 | } 223 | 224 | return $arguments; 225 | } 226 | 227 | function parse_function() 228 | { 229 | $this->doc = array( 230 | 'short_description' => $this->extract_text('//span[@class="dc-title"]'), 231 | 'description' => $this->extract_text('//p[@class="para rdfs-comment"]'), 232 | 'arguments' => array() 233 | ); 234 | 235 | $xpath = new DOMXPath($this->dom); 236 | $names = $xpath->query('//div[@class="refsect1 parameters"]/dl/dt'); 237 | $descriptions = $xpath->query('//div[@class="refsect1 parameters"]/dl/dd'); 238 | 239 | for ($i = 0; $i < $names->length; $i++) 240 | { 241 | $name = $this->extract_text('span[@class="term"]//tt[@class="parameter"]', $names->item($i)); 242 | $description = $this->node_text($descriptions->item($i), 'div[@class="example"]|blockquote'); 243 | 244 | $this->doc['arguments'][] = array('name' => $name, 'description' => $description); 245 | } 246 | 247 | $this->doc['returns'] = $this->extract_text('//div[@class="refsect1 returnvalues"]/p[@class="para"]', null, true); 248 | } 249 | 250 | function parse_class() 251 | { 252 | // TODO 253 | } 254 | 255 | function parse() 256 | { 257 | if (is_a($this->ref, 'ReflectionFunctionAbstract')) 258 | { 259 | $this->parse_function(); 260 | } 261 | else if (is_a($this->ref, 'ReflectionClass')) 262 | { 263 | $this->parse_class(); 264 | } 265 | } 266 | 267 | function make_args_from_params($params) 268 | { 269 | $ret = array(); 270 | 271 | for ($i = 0; $i < count($params); $i++) 272 | { 273 | $param = $params[$i]; 274 | 275 | $p = array( 276 | 'name' => $param->getName(), 277 | 'type' => '', 278 | 'optional' => $param->isOptional() 279 | ); 280 | 281 | $ret[$param->getPosition()] = $p; 282 | } 283 | 284 | return $ret; 285 | } 286 | } 287 | 288 | function insert_function($db, $ref, $classid) 289 | { 290 | $extractor = new DocumentationExtractor($ref); 291 | $short_description = ''; 292 | $description = ''; 293 | $arguments_doc = array(); 294 | 295 | if ($extractor->documentation_found()) 296 | { 297 | $short_description = $extractor->short_description(); 298 | $description = $extractor->description(); 299 | $arguments_doc = $extractor->arguments(); 300 | } 301 | 302 | $params = array( 303 | 'name' => $ref->getName(), 304 | 'class' => $classid, 305 | 'short_description' => $short_description, 306 | 'description' => $description 307 | ); 308 | 309 | if (is_a($ref, 'ReflectionMethod')) 310 | { 311 | if ($ref->isPrivate()) 312 | { 313 | return; 314 | } 315 | 316 | $flags = intval($ref->isConstructor()) << 0 | 317 | intval($ref->isDestructor()) << 1 | 318 | intval($ref->isAbstract()) << 2 | 319 | intval($ref->isFinal()) << 3 | 320 | intval($ref->isProtected()) << 4 | 321 | intval($ref->isPublic()) << 5 | 322 | intval($ref->isStatic()) << 6; 323 | 324 | $params['flags'] = $flags; 325 | } 326 | 327 | $db->insert('functions', $params); 328 | $id = intval($db->last_insert_id()); 329 | 330 | $args = $extractor->extract_argument_info(); 331 | $params = $ref->getParameters(); 332 | 333 | if (count($params) > count($args)) 334 | { 335 | $args = $extractor->make_args_from_params($params); 336 | } 337 | 338 | for ($i = 0; $i < count($args); $i++) 339 | { 340 | $description = ''; 341 | 342 | if ($i < count($arguments_doc)) 343 | { 344 | $description = $arguments_doc[$i]['description']; 345 | } 346 | 347 | $db->insert('arguments', array( 348 | 'function' => $id, 349 | 'index' => $i, 350 | 'name' => $args[$i]['name'], 351 | 'optional' => $args[$i]['optional'] ? 1 : 0, 352 | 'type' => $args[$i]['type'], 353 | 'description' => $description 354 | )); 355 | } 356 | } 357 | 358 | function insert_property($db, $ref, $classid) 359 | { 360 | if ($ref->isPrivate()) 361 | { 362 | return; 363 | } 364 | 365 | $doc = $ref->getDocComment(); 366 | 367 | $flags = intval($ref->isProtected()) << 4 | 368 | intval($ref->isPublic()) << 5 | 369 | intval($ref->isStatic()) << 6 | 370 | intval($ref->isDefault()) << 7; 371 | 372 | $db->insert('properties', array( 373 | 'name' => $ref->getName(), 374 | 'doc' => $doc ? $doc : '', 375 | 'class' => $classid, 376 | 'flags' => $flags 377 | )); 378 | } 379 | 380 | function insert_class($db, $ref) 381 | { 382 | $doc = $ref->getDocComment(); 383 | 384 | $db->insert('classes', array( 385 | 'name' => $ref->getName(), 386 | 'doc' => $doc ? $doc : '', 387 | 'parent' => 0, 388 | 'interface' => $ref->isInterface() ? 1 : 0 389 | )); 390 | 391 | $id = $db->last_insert_id(); 392 | 393 | foreach ($ref->getMethods() as $method) 394 | { 395 | insert_function($db, $method, $id); 396 | } 397 | 398 | foreach ($ref->getProperties() as $property) 399 | { 400 | insert_property($db, $property, $id); 401 | } 402 | 403 | foreach ($ref->getConstants() as $name => $value) 404 | { 405 | $db->insert('constants', array('name' => $name, 'class' => $id)); 406 | } 407 | } 408 | 409 | $dbname = 'phpsymbols.db'; 410 | 411 | if (file_exists($dbname)) 412 | { 413 | unlink($dbname); 414 | } 415 | 416 | $db = new SQLiteDB($dbname); 417 | 418 | $db->query("CREATE TABLE `classes` (`id` INTEGER PRIMARY KEY, `name` STRING, `doc` STRING, `parent` INTEGER, `interface` INTEGER)"); 419 | $db->query("CREATE INDEX `class_name` ON `classes` (`name`)"); 420 | $db->query("CREATE INDEX `class_parent` ON `classes` (`parent`)"); 421 | 422 | $db->query("CREATE TABLE `interfaces` (`class` INTEGER, `interface` INTEGER)"); 423 | $db->query("CREATE INDEX `interface_class` ON `interfaces` (`class`)"); 424 | $db->query("CREATE INDEX `interface_interface` ON `interfaces` (`interface`)"); 425 | 426 | $db->query("CREATE TABLE `properties` (`id` INTEGER PRIMARY KEY, `name` STRING, `doc` STRING, `class` INTEGER, `flags` INTEGER)"); 427 | $db->query("CREATE INDEX `property_name` ON `properties` (`name`)"); 428 | $db->query("CREATE INDEX `property_class` ON `properties` (`class`)"); 429 | $db->query("CREATE INDEX `property_flags` ON `properties` (`flags`)"); 430 | 431 | $db->query("CREATE TABLE `constants` (`id` INTEGER PRIMARY KEY, `name` STRING, `class` INTEGER)"); 432 | $db->query("CREATE INDEX `constant_name` ON `properties` (`name`)"); 433 | 434 | $db->query("CREATE TABLE `globals` (`id` INTEGER PRIMARY KEY, `name` STRING)"); 435 | $db->query("CREATE INDEX `global_name` ON `properties` (`name`)"); 436 | 437 | $db->query("CREATE TABLE `functions` (`id` INTEGER PRIMARY KEY, `name` STRING, `short_description` STRING, `description` STRING, `class` INTEGER, `flags` INTEGER)"); 438 | $db->query("CREATE INDEX `function_name` ON `functions` (`name`)"); 439 | $db->query("CREATE INDEX `function_class` ON `functions` (`class`)"); 440 | $db->query("CREATE INDEX `function_flags` ON `functions` (`flags`)"); 441 | 442 | $db->query("CREATE TABLE `arguments` (`function` INTEGER, `index` INTEGER, `name` STRING, `optional` INTEGER, `type` STRING, `default` STRING, `description` STRING)"); 443 | $db->query("CREATE INDEX `argument_function` ON `arguments` (`function`)"); 444 | $db->query("CREATE INDEX `argument_index` ON `arguments` (`index`)"); 445 | 446 | /* Iterate over all the classes */ 447 | $classes = get_declared_classes(); 448 | $num = count($classes); 449 | 450 | $db->query('BEGIN TRANSACTION'); 451 | 452 | for ($i = 0; $i < $num; $i++) 453 | { 454 | $ref = new ReflectionClass($classes[$i]); 455 | insert_class($db, $ref); 456 | } 457 | 458 | $interfaces = get_declared_interfaces(); 459 | foreach ($interfaces as $interface) 460 | { 461 | $ref = new ReflectionClass($interface); 462 | insert_class($db, $ref); 463 | } 464 | 465 | for ($i = 0; $i < $num; $i++) 466 | { 467 | $ref = new ReflectionClass($classes[$i]); 468 | 469 | foreach ($ref->getInterfaces() as $name => $class) 470 | { 471 | if ($name == 'Reflector') 472 | { 473 | continue; 474 | } 475 | 476 | $id = $db->query_value('SELECT id FROM classes WHERE name = "' . $name . '"'); 477 | $db->insert('interfaces', array('class' => $i + 1, 'interface' => $id)); 478 | } 479 | 480 | $parent = $ref->getParentClass(); 481 | 482 | if ($parent == null) 483 | { 484 | continue; 485 | } 486 | 487 | $id = $db->query_value('SELECT id FROM classes WHERE name = "' . $parent->getName() . '"'); 488 | 489 | if ($id !== null) 490 | { 491 | $db->update('classes', 'id = ' . ($i + 1), array('parent' => $id)); 492 | } 493 | } 494 | 495 | $funcs = get_defined_functions(); 496 | 497 | /* Iterate over all the functions */ 498 | $num = count($funcs['internal']); 499 | for ($i = 0; $i < $num; $i++) 500 | { 501 | $ref = new ReflectionFunction($funcs['internal'][$i]); 502 | insert_function($db, $ref, 0); 503 | } 504 | 505 | $constants = get_defined_constants(); 506 | 507 | foreach ($constants as $name => $value) 508 | { 509 | $db->insert('constants', array('name' => $name, 'class' => 0)); 510 | } 511 | 512 | foreach ($GLOBALS as $name => $value) 513 | { 514 | if ($name[0] != '_') 515 | { 516 | continue; 517 | } 518 | 519 | $db->insert('globals', array('name' => $name)); 520 | } 521 | 522 | $db->query('COMMIT TRANSACTION'); 523 | ?> 524 | -------------------------------------------------------------------------------- /extractor/sqlite.php: -------------------------------------------------------------------------------- 1 | last_error()); 37 | } 38 | 39 | function SQLiteDB($filename) 40 | { 41 | if ($filename) 42 | $this->db_open($filename); 43 | } 44 | 45 | function db_open($filename) 46 | { 47 | if ($filename != NULL) 48 | { 49 | $this->resource = new PDO('sqlite:' . $filename); 50 | 51 | if (!$this->resource) 52 | { 53 | return false; 54 | } 55 | } 56 | 57 | return true; 58 | } 59 | 60 | function last_error() 61 | { 62 | $error = $this->resource->errorInfo(); 63 | return $error[2]; 64 | } 65 | 66 | function query($query) 67 | { 68 | if (!$this->resource) 69 | return; 70 | 71 | $handle = $this->resource->query($query); 72 | 73 | if ($handle === false) 74 | { 75 | $this->error(array('query' => $query)); 76 | return null; 77 | } 78 | else if ($handle !== true) 79 | { 80 | $this->last_result = $handle->fetchAll(); 81 | return $this->last_result; 82 | } 83 | 84 | return true; 85 | } 86 | 87 | function query_value($query, $idx = 0) 88 | { 89 | $result = $this->query_first($query); 90 | 91 | if (is_array($result)) 92 | return $result[$idx]; 93 | 94 | return $result; 95 | } 96 | 97 | function query_first($query) 98 | { 99 | $result = $this->query($query); 100 | 101 | if (is_string($result)) 102 | return $result; 103 | else if (!is_array($result) || count($result) == 0) 104 | return NULL; 105 | else 106 | return $result[0]; 107 | } 108 | 109 | function close() 110 | { 111 | if ($this->resource) 112 | { 113 | $this->resource = null; 114 | } 115 | } 116 | 117 | function serialize($item) 118 | { 119 | if (!is_array($item)) 120 | return $item; 121 | 122 | $result = array(); 123 | 124 | foreach ($item as $i) 125 | $result[] = str_replace(',', '\,', str_replace("\\", "\\\\", $i)); 126 | 127 | return $this->quote(implode(', ', $result)); 128 | } 129 | 130 | function insert($table, $vals, $literals = array()) 131 | { 132 | if (!$this->resource) 133 | return; 134 | 135 | $query = 'INSERT INTO `' . $table . '`'; 136 | $keys = array_keys($vals); 137 | 138 | $k = "("; 139 | $v = "VALUES("; 140 | 141 | for ($i = 0; $i < count($keys); $i++) 142 | { 143 | if ($i != 0) 144 | { 145 | $k .= ', '; 146 | $v .= ', '; 147 | } 148 | 149 | $k .= '`' . $keys[$i] . '`'; 150 | 151 | if (is_string($vals[$keys[$i]]) && !in_array($keys[$i], $literals)) 152 | $v .= $this->quote($vals[$keys[$i]]); 153 | elseif ($vals[$keys[$i]] === null) 154 | $v .= 'null'; 155 | else 156 | $v .= $this->serialize($vals[$keys[$i]]); 157 | } 158 | 159 | $query = $query . ' ' . $k . ') ' . $v . ');'; 160 | return $this->query($query); 161 | } 162 | 163 | function last_insert_id() 164 | { 165 | if (!$this->resource) 166 | return 0; 167 | 168 | return $this->resource->lastInsertId(); 169 | } 170 | 171 | function delete($table, $condition, $limit = 1) 172 | { 173 | if (!$this->resource) 174 | return; 175 | 176 | $this->query('DELETE FROM `' . $table . '` ' . ($condition ? ('WHERE ' . $condition . ' ') : '') . ($limit > 0 ? ('LIMIT ' . $limit) : '')); 177 | } 178 | 179 | function update($table, $condition, $vals, $literals = array()) 180 | { 181 | if (!$this->resource) 182 | return; 183 | 184 | $query = 'UPDATE `' . $table . '` SET '; 185 | $keys = array_keys($vals); 186 | $k = ""; 187 | 188 | for ($i = 0; $i < count($keys); $i++) { 189 | if ($i != 0) 190 | $k .= ', '; 191 | 192 | $k .= '`' . $keys[$i] . "` = "; 193 | 194 | if (is_string($vals[$keys[$i]]) && !in_array($keys[$i], $literals)) 195 | $k .= $this->quote($vals[$keys[$i]]); 196 | elseif ($vals[$keys[$i]] === null) 197 | $k .= 'null'; 198 | else 199 | $k .= $this->serialize($vals[$keys[$i]]); 200 | } 201 | 202 | $query .= $k; 203 | 204 | if ($condition) 205 | $query .= ' WHERE ' . $condition; 206 | 207 | return $this->query($query); 208 | } 209 | 210 | function quote($s) 211 | { 212 | return $this->resource->quote($s); 213 | } 214 | } 215 | ?> 216 | -------------------------------------------------------------------------------- /plugin/phpcompletion.gedit-plugin: -------------------------------------------------------------------------------- 1 | [Gedit Plugin] 2 | Loader=python 3 | Module=phpcompletion 4 | IAge=2 5 | Name=PHP Completion 6 | Description=PHP completion using the completion framework 7 | Icon=gedit-plugin 8 | Authors=Jesse van den Kieboom \nIgnacio Casal Quinteiro 9 | Copyright=Copyright © 2009 Jesse van den Kieboom\nCopyright © 2009 Ignacio Casal Quinteiro 10 | Website=http://www.gedit.org 11 | -------------------------------------------------------------------------------- /plugin/phpcompletion/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # phpcompletion.py - PHP completion using the completion framework 4 | # 5 | # Copyright (C) 2009 - Jesse van den Kieboom 6 | # Copyright (C) 2009 - Ignacio Casal Quinteiro 7 | # 8 | # This program is free software; you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation; either version 2 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program; if not, write to the Free Software 20 | # Foundation, Inc., 59 Temple Place, Suite 330, 21 | # Boston, MA 02111-1307, USA. 22 | 23 | import gtk 24 | import gedit 25 | from gettext import gettext as _ 26 | import gtksourceview2 as gsv 27 | import phpprovider 28 | import utils 29 | import os 30 | 31 | class PHPCompletionWindowHelper: 32 | def __init__(self, plugin, window): 33 | self._window = window 34 | self._plugin = plugin 35 | 36 | # TODO: use get_data_dir when db is nicely installed in the correct place 37 | self._provider = phpprovider.PHPProvider(os.path.join(plugin.get_install_dir(), 'phpcompletion', 'phpsymbols.db')) 38 | 39 | for view in self._window.get_views(): 40 | self.add_view(view) 41 | 42 | self._tab_added_id = self._window.connect('tab-added', self.on_tab_added) 43 | self._tab_removed_id = self._window.connect('tab-removed', self.on_tab_removed) 44 | 45 | def deactivate(self): 46 | # Remove the provider from all the views 47 | for view in self._window.get_views(): 48 | view.get_completion().completion.remove_provider(self._provider) 49 | 50 | self._window.disconnect(self._tab_added_id) 51 | self._window.disconnect(self._tab_removed_id) 52 | 53 | self._window = None 54 | self._plugin = None 55 | 56 | def update_ui(self): 57 | pass 58 | 59 | def add_view(self, view): 60 | view.get_completion().add_provider(self._provider) 61 | view.get_completion().connect('populate-context', self.on_populate_context) 62 | 63 | def remove_view(self, view): 64 | view.get_completion().remove_provider(self._provider) 65 | 66 | def on_tab_added(self, window, tab): 67 | # Add provider to the new view 68 | self.add_view(tab.get_view()) 69 | 70 | def on_tab_removed(self, window, tab): 71 | # Remove provider from the view 72 | self.remove_view(tab.get_view()) 73 | 74 | def check_is_class(self, context): 75 | piter = context.get_iter() 76 | 77 | start = piter.copy() 78 | start.backward_char() 79 | ch = start.get_char() 80 | 81 | #Move to the start of the word 82 | while ch.isalnum() or ch == '_' or ch == ':' and not start.starts_line(): 83 | if not start.backward_char(): 84 | break 85 | 86 | ch = start.get_char() 87 | 88 | #Now we check that the previous word is 'new' 89 | start2, word = utils.get_word(start) 90 | if word == 'new': 91 | return True 92 | else: 93 | return False 94 | 95 | def check_is_class_const(self, context): 96 | start, word = utils.get_word(context.get_iter()) 97 | 98 | if word: 99 | split = word.split('::') 100 | if len(split) == 2: 101 | return True 102 | else: 103 | return False 104 | else: 105 | return False 106 | 107 | def is_php_statement(self, context): 108 | end, word = utils.get_word(context.get_iter()) 109 | 110 | if not word == 'php' or not end: 111 | return False 112 | 113 | start = end.copy() 114 | return start.backward_chars(2) and start.get_text(end) == '[, %s]' % (name,) 63 | continue 64 | else: 65 | ret += ', ' 66 | 67 | if arg[1]: 68 | ret += '[, %s]' % (name,) 69 | else: 70 | ret += name 71 | 72 | return ret 73 | 74 | def class_info(self, fid): 75 | query = "SELECT `id` FROM functions WHERE `class` = ? AND `flags` = ? %s" 76 | func_id = self.complete(query, -1, fid, Flags.Constructor | Flags.Public) 77 | 78 | if func_id: 79 | return self.function_info(func_id[0][0]) 80 | else: 81 | return '' 82 | 83 | def complete(self, query, maxresults, *args): 84 | if not self.db: 85 | return [] 86 | 87 | if maxresults > 0: 88 | extra = 'LIMIT %d' % (maxresults,) 89 | else: 90 | extra = '' 91 | 92 | try: 93 | query = query % (extra,) 94 | 95 | if args[0] == None: 96 | result = self.db.execute(query) 97 | else: 98 | result = self.db.execute(query, args) 99 | except Exception as e: 100 | sys.stderr.write("PHPCompletion: Error in query: %s\n" % (str(e), )) 101 | return [] 102 | 103 | return list(result) 104 | 105 | def complete_function(self, func, classid = 0, maxresults = -1): 106 | query = "SELECT `id`, `name`, `description`, `short_description` FROM functions WHERE `class` = ? AND `name` LIKE ? || '%%' ORDER BY `name` %s" 107 | 108 | return self.complete(query, maxresults, classid, func) 109 | 110 | def complete_const(self, const, classid = 0, maxresults = -1,): 111 | query = "SELECT `id`, `name` FROM constants WHERE `class` = ? AND `name` LIKE ? || '%%' ORDER BY `name` %s" 112 | 113 | return self.complete(query, maxresults, classid, const) 114 | 115 | def complete_class(self, class_name, maxresults = -1): 116 | if not class_name: 117 | query = "SELECT `id`, `name`, `doc` FROM classes %s" 118 | else: 119 | query = "SELECT `id`, `name`, `doc` FROM classes WHERE `name` LIKE ? || '%%' ORDER BY `name` %s" 120 | 121 | return self.complete(query, maxresults, class_name) 122 | 123 | def complete_class_const(self, class_name, const, maxresults = -1): 124 | class_query = "SELECT `id` FROM classes where `name` = ? %s" 125 | class_id = self.complete(class_query, 1, class_name) 126 | 127 | result = [] 128 | if class_id: 129 | if not const: 130 | query = "SELECT `id`, `name` FROM constants WHERE `class` = ? ORDER BY `name` %s" 131 | result = self.complete(query, maxresults, class_id[0][0]) 132 | else: 133 | query = "SELECT `id`, `name` FROM constants WHERE `class` = ? AND `name` LIKE ? || '%%' ORDER BY `name` %s" 134 | result = self.complete(query, maxresults, class_id[0][0], const) 135 | 136 | return result 137 | 138 | # ex:ts=4:et: 139 | -------------------------------------------------------------------------------- /plugin/phpcompletion/phpproposals.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # phpproposals.py - PHP completion using the completion framework 4 | # 5 | # Copyright (C) 2009 - Jesse van den Kieboom 6 | # Copyright (C) 2009 - Ignacio Casal Quinteiro 7 | # 8 | # This program is free software; you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation; either version 2 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program; if not, write to the Free Software 20 | # Foundation, Inc., 59 Temple Place, Suite 330, 21 | # Boston, MA 02111-1307, USA. 22 | 23 | import gtksourceview2 as gsv 24 | import gobject 25 | 26 | class PHPProposal(gobject.GObject, gsv.CompletionProposal): 27 | def __init__(self, db, fid, name): 28 | gobject.GObject.__init__(self) 29 | 30 | self.db = db 31 | self.fid = fid 32 | self.name = name 33 | 34 | def do_get_text(self): 35 | return self.name 36 | 37 | def do_get_label(self): 38 | return self.name 39 | 40 | def do_get_info(self): 41 | # FIXME: gettext 42 | return 'No extra info available' 43 | 44 | class PHPProposalFunction(PHPProposal): 45 | def __init__(self, db, fid, name, description): 46 | PHPProposal.__init__(self, db, fid, name) 47 | 48 | self.description = description 49 | 50 | def do_get_info(self): 51 | return "%s (%s)\n\n%s" % (self.name, self.db.function_info(self.fid), self.description) 52 | 53 | class PHPProposalClass(PHPProposal): 54 | def __init__(self, db, fid, name, doc): 55 | PHPProposal.__init__(self, db, fid, name) 56 | 57 | self.doc = doc 58 | 59 | def do_get_info(self): 60 | return "%s (%s)\n\n%s" % (self.name, self.db.class_info(self.fid), self.doc) 61 | 62 | gobject.type_register(PHPProposal) 63 | 64 | # ex:ts=4:et: 65 | -------------------------------------------------------------------------------- /plugin/phpcompletion/phpprovider.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # phpprovider.py - PHP completion using the completion framework 4 | # 5 | # Copyright (C) 2009 - Jesse van den Kieboom 6 | # Copyright (C) 2009 - Ignacio Casal Quinteiro 7 | # 8 | # This program is free software; you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation; either version 2 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program; if not, write to the Free Software 20 | # Foundation, Inc., 59 Temple Place, Suite 330, 21 | # Boston, MA 02111-1307, USA. 22 | 23 | import gtk 24 | import gedit 25 | from gettext import gettext as _ 26 | import gtksourceview2 as gsv 27 | from phpdb import PHPDb 28 | import gobject 29 | from phpproposals import PHPProposal, PHPProposalFunction, PHPProposalClass 30 | import utils 31 | 32 | PHP_PROVIDER_IS_CLASS_DATA_KEY = 'PHPProviderIsClassData' 33 | PHP_PROVIDER_IS_CLASS_CONST_DATA_KEY = 'PHPProviderIsClassConstantData' 34 | PHP_PROVIDER_IS_PHP_STATEMENT_DATA_KEY = 'PHPProviderIsPHPStatementData' 35 | 36 | class PHPProvider(gobject.GObject, gsv.CompletionProvider): 37 | MARK_NAME = 'PHPProviderCompletionMark' 38 | 39 | def __init__(self, database): 40 | gobject.GObject.__init__(self) 41 | 42 | self.mark = None 43 | self.db = PHPDb(database) 44 | 45 | def move_mark(self, buf, start): 46 | # TODO: see do_get_mark_iter 47 | mark = buf.get_mark(self.MARK_NAME) 48 | 49 | if not mark: 50 | buf.create_mark(self.MARK_NAME, start, True) 51 | else: 52 | buf.move_mark(mark, start) 53 | 54 | def get_proposals(self, is_class, is_class_const, is_php_statement, word): 55 | # Just get functions and classes for now 56 | proposals = [] 57 | 58 | if is_class_const: 59 | const = word.split('::') 60 | for class_const in self.db.complete_class_const(const[0], const[1]): 61 | proposals.append(PHPProposal(self.db, class_const[0], class_const[1])) 62 | elif is_class: 63 | for class_name in self.db.complete_class(word): 64 | proposals.append(PHPProposalClass(self.db, class_name[0], class_name[1], class_name[2])) 65 | elif not is_php_statement: 66 | for func in self.db.complete_function(word): 67 | if len(func) > 2: 68 | if func[3]: 69 | doc = func[3] 70 | else: 71 | doc = func[2] 72 | else: 73 | doc = '' 74 | 75 | proposals.append(PHPProposalFunction(self.db, func[0], func[1], doc)) 76 | 77 | for const in self.db.complete_const(word): 78 | proposals.append(PHPProposal(self.db, const[0], const[1])) 79 | 80 | return proposals 81 | 82 | def do_get_start_iter(self, context, proposal): 83 | buf = context.get_iter().get_buffer() 84 | mark = buf.get_mark(self.MARK_NAME) 85 | 86 | if not mark: 87 | return None 88 | 89 | return buf.get_iter_at_mark(mark) 90 | 91 | def do_match(self, context): 92 | lang = context.get_iter().get_buffer().get_language() 93 | 94 | if not lang: 95 | return False 96 | 97 | if lang.get_id() != 'php' and lang.get_id() != 'html': 98 | return False 99 | 100 | start, word = utils.get_word(context.get_iter()) 101 | is_class = context.get_data(PHP_PROVIDER_IS_CLASS_DATA_KEY) 102 | return is_class or (word and len(word) > 2) 103 | 104 | def do_populate(self, context): 105 | is_class = context.get_data(PHP_PROVIDER_IS_CLASS_DATA_KEY) 106 | is_class_const = context.get_data(PHP_PROVIDER_IS_CLASS_CONST_DATA_KEY) 107 | is_php_statement = context.get_data(PHP_PROVIDER_IS_PHP_STATEMENT_DATA_KEY) 108 | 109 | start, word = utils.get_word(context.get_iter()) 110 | if not is_class: 111 | if not word: 112 | context.add_proposals(self, [], True) 113 | return 114 | elif is_class_const: 115 | # FIXME: This should be implemented in activation 116 | start = context.get_iter() 117 | else: 118 | if not word: 119 | start = context.get_iter() 120 | 121 | proposals = self.get_proposals(is_class, is_class_const, is_php_statement, word) 122 | self.move_mark(context.get_iter().get_buffer(), start) 123 | 124 | context.add_proposals(self, proposals, True) 125 | 126 | def do_get_name(self): 127 | return _('PHP') 128 | 129 | def do_get_activation(self): 130 | return gsv.COMPLETION_ACTIVATION_INTERACTIVE | gsv.COMPLETION_ACTIVATION_USER_REQUESTED 131 | 132 | def do_activate_proposal(self, proposal, piter): 133 | # TODO: implement 134 | return False 135 | 136 | gobject.type_register(PHPProvider) 137 | 138 | # ex:ts=4:et: 139 | -------------------------------------------------------------------------------- /plugin/phpcompletion/phpsymbols.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jessevdk/gedit-php-completion/9d44614b10bcd908f1a48d5865b98325ec0baf10/plugin/phpcompletion/phpsymbols.db -------------------------------------------------------------------------------- /plugin/phpcompletion/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # phpcompletion.py - PHP completion using the completion framework 4 | # 5 | # Copyright (C) 2009 - Jesse van den Kieboom 6 | # Copyright (C) 2009 - Ignacio Casal Quinteiro 7 | # 8 | # This program is free software; you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation; either version 2 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program; if not, write to the Free Software 20 | # Foundation, Inc., 59 Temple Place, Suite 330, 21 | # Boston, MA 02111-1307, USA. 22 | 23 | def get_word(piter): 24 | if not piter.ends_word or piter.get_char() == '_': 25 | return None, None 26 | 27 | start = piter.copy() 28 | 29 | while True: 30 | if start.starts_line(): 31 | break 32 | 33 | start.backward_char() 34 | ch = start.get_char() 35 | 36 | if not (ch.isalnum() or ch == '_' or ch == ':'): 37 | start.forward_char() 38 | break 39 | 40 | if start.equal(piter): 41 | return None, None 42 | 43 | while (not start.equal(piter)) and start.get_char().isdigit(): 44 | start.forward_char() 45 | 46 | if start.equal(piter): 47 | return None, None 48 | 49 | return start, start.get_text(piter) 50 | 51 | # ex:ts=4:et: 52 | --------------------------------------------------------------------------------