Skip to content

Commit e3d5897

Browse files
committed
Merge branch 'release/0.2'
2 parents 7414317 + 2bbe668 commit e3d5897

17 files changed

+727
-52
lines changed

ChangeLog.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Version 0.2
2+
-----------
3+
4+
* new kind descriptor
5+
* ctags flags support
6+
* excmd
7+
* fields
8+
* format
9+
* new test case layer
10+
* introduce debug mode
11+
* bug fixes: #3 and #4
12+
13+
Version 0.1
14+
-----------
15+
16+
* ctags compatible
17+
* surppoted tokens
18+
* constant
19+
* variable
20+
* function
21+
* class
22+
* class method
23+
* class property
24+
* class constant
25+
* interface
26+
* scope field support
27+
* access field support

PHPCtags.class.php

Lines changed: 116 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -5,57 +5,79 @@ class PHPCtags
55

66
private $mParser;
77

8-
public function __construct($file)
8+
private static $mKinds = array(
9+
'c' => 'class',
10+
'm' => 'method',
11+
'f' => 'function',
12+
'p' => 'property',
13+
'd' => 'constant',
14+
'v' => 'variable',
15+
'i' => 'interface',
16+
);
17+
18+
public function __construct()
919
{
10-
//@todo Check for existence
11-
$this->mFile = $file;
1220
$this->mParser = new PHPParser_Parser(new PHPParser_Lexer);
1321
}
1422

15-
private function getAccess($node)
23+
public static function getMKinds()
24+
{
25+
return self::$mKinds;
26+
}
27+
28+
private function getNodeAccess($node)
1629
{
1730
if ($node->isPrivate()) return 'private';
1831
if ($node->isProtected()) return 'protected';
1932
return 'public';
2033
}
2134

22-
private function struct($node, $class_name = NULL, $function_name = NULL)
35+
private static function helperSortByLine($a, $b) {
36+
return $a['line'] > $b['line'] ? 1 : 0;
37+
}
38+
39+
private function struct($node, $reset=FALSE, $parent=array())
2340
{
41+
static $scope = array();
2442
static $structs = array();
2543

26-
$kind = $name = $line = $scope = $access = '';
44+
if ($reset) {
45+
$structs = array();
46+
}
47+
48+
$kind = $name = $line = $access = '';
49+
50+
if(!empty($parent)) array_push($scope, $parent);
51+
2752
if (is_array($node)) {
2853
foreach ($node as $subNode) {
29-
$this->struct($subNode, $class_name, $function_name);
54+
$this->struct($subNode);
3055
}
3156
} elseif ($node instanceof PHPParser_Node_Stmt_Class) {
3257
$kind = 'c';
3358
$name = $node->name;
3459
$line = $node->getLine();
3560
foreach ($node as $subNode) {
36-
$this->struct($subNode, $name);
61+
$this->struct($subNode, FALSE, array('class' => $name));
3762
}
3863
} elseif ($node instanceof PHPParser_Node_Stmt_Property) {
39-
$kind = 'v';
64+
$kind = 'p';
4065
$prop = $node->props[0];
4166
$name = $prop->name;
4267
$line = $prop->getLine();
43-
$scope = "class:" . $class_name;
44-
$access = $this->getAccess($node);
68+
$access = $this->getNodeAccess($node);
4569
} elseif ($node instanceof PHPParser_Node_Stmt_ClassConst) {
4670
$kind = 'd';
4771
$cons = $node->consts[0];
4872
$name = $cons->name;
4973
$line = $cons->getLine();
50-
$scope = "class:" . $class_name;
5174
} elseif ($node instanceof PHPParser_Node_Stmt_ClassMethod) {
52-
$kind = 'f';
75+
$kind = 'm';
5376
$name = $node->name;
5477
$line = $node->getLine();
55-
$scope = "class:" . $class_name;
56-
$access = $this->getAccess($node);
78+
$access = $this->getNodeAccess($node);
5779
foreach ($node as $subNode) {
58-
$this->struct($subNode, $class_name, $name);
80+
$this->struct($subNode, FALSE, array('method' => $name));
5981
}
6082
} elseif ($node instanceof PHPParser_Node_Stmt_Const) {
6183
$kind = 'd';
@@ -76,7 +98,7 @@ private function struct($node, $class_name = NULL, $function_name = NULL)
7698
$name = $node->name;
7799
$line = $node->getLine();
78100
foreach ($node as $subNode) {
79-
$this->struct($subNode, $class_name, $name);
101+
$this->struct($subNode, FALSE, array('function' => $name));
80102
}
81103
} elseif ($node instanceof PHPParser_Node_Stmt_Trait) {
82104
//@todo
@@ -85,19 +107,19 @@ private function struct($node, $class_name = NULL, $function_name = NULL)
85107
$name = $node->name;
86108
$line = $node->getLine();
87109
foreach ($node as $subNode) {
88-
$this->struct($subNode, $name);
110+
$this->struct($subNode, FALSE, array('interface' => $name));
89111
}
90112
} elseif ($node instanceof PHPParser_Node_Stmt_Namespace) {
91113
//@todo
114+
foreach ($node as $subNode) {
115+
$this->struct($subNode);
116+
}
92117
} elseif ($node instanceof PHPParser_Node_Expr_Assign) {
93-
$kind = 'v';
94-
$node = $node->var;
95-
$name = $node->name;
96-
$line = $node->getLine();
97-
if (!empty($class_name) && !empty($function_name)) {
98-
$scope = "function:" . $class_name . '::' . $function_name;
99-
} elseif (!empty($function_name)) {
100-
$scope = "function:" . $function_name;
118+
if(is_string($node->var->name)) {
119+
$kind = 'v';
120+
$node = $node->var;
121+
$name = $node->name;
122+
$line = $node->getLine();
101123
}
102124
} elseif ($node instanceof PHPParser_Node_Expr_FuncCall) {
103125
switch ($node->name) {
@@ -122,38 +144,90 @@ private function struct($node, $class_name = NULL, $function_name = NULL)
122144
);
123145
}
124146

147+
if(!empty($parent)) array_pop($scope);
148+
149+
usort($structs, 'self::helperSortByLine');
150+
125151
return $structs;
126152
}
127153

128-
private function render($structs)
154+
private function render($structs, $options)
129155
{
130156
$str = '';
131157
$lines = file($this->mFile);
132-
foreach ($structs as $stuct) {
133-
if (empty($stuct['name']) || empty($stuct['line']) || empty($stuct['kind']))
158+
foreach ($structs as $struct) {
159+
if (empty($struct['name']) || empty($struct['line']) || empty($struct['kind']))
134160
return;
135161

136-
if ($stuct['kind'] == 'v') {
137-
$str .= "$" . $stuct['name'];
162+
if ($struct['kind'] == 'v') {
163+
$str .= "$" . $struct['name'];
138164
} else {
139-
$str .= $stuct['name'];
165+
$str .= $struct['name'];
140166
}
167+
141168
$str .= "\t" . $this->mFile;
142-
$str .= "\t" . "/^" . rtrim($lines[$stuct['line'] - 1], "\n") . "$/;\"";
143-
$str .= "\t" . $stuct['kind'];
144-
$str .= "\t" . "line:" . $stuct['line'];
145-
!empty($stuct['scope']) && $str .= "\t" . $stuct['scope'];
146-
!empty($stuct['access']) && $str .= "\t" . "access:" . $stuct['access'];
169+
170+
if ($options['excmd'] == 'number') {
171+
$str .= "\t" . $struct['line'];
172+
} else { //excmd == 'mixed' or 'pattern', default behavior
173+
$str .= "\t" . "/^" . rtrim($lines[$struct['line'] - 1], "\n") . "$/";
174+
}
175+
176+
if ($options['format'] == 1) {
177+
$str .= "\n";
178+
continue;
179+
}
180+
181+
$str .= ";\"";
182+
183+
#field=k, kind of tag as single letter
184+
if (in_array('k', $options['fields'])) {
185+
in_array('z', $options['fields']) && $str .= "kind:";
186+
$str .= "\t" . $struct['kind'];
187+
} else
188+
#field=K, kind of tag as fullname
189+
if (in_array('K', $options['fields'])) {
190+
in_array('z', $options['fields']) && $str .= "kind:";
191+
$str .= "\t" . self::$mKinds[$struct['kind']];
192+
}
193+
194+
#field=n
195+
if (in_array('n', $options['fields'])) {
196+
$str .= "\t" . "line:" . $struct['line'];
197+
}
198+
199+
#field=s
200+
if (in_array('s', $options['fields']) && !empty($struct['scope'])) {
201+
$scope = array_pop($struct['scope']);
202+
list($type,$name) = each($scope);
203+
switch ($type) {
204+
case 'method':
205+
$scope = array_pop($struct['scope']);
206+
list($p_type,$p_name) = each($scope);
207+
$scope = 'method:' . $p_name . '::' . $name;
208+
break;
209+
default:
210+
$scope = $type . ':' . $name;
211+
break;
212+
}
213+
$str .= "\t" . $scope;
214+
}
215+
216+
#field=a
217+
if (in_array('a', $options['fields']) && !empty($struct['access'])) {
218+
$str .= "\t" . "access:" . $struct['access'];
219+
}
220+
147221
$str .= "\n";
148222
}
149223
return $str;
150224
}
151225

152-
public function export()
226+
public function export($file, $options)
153227
{
154-
$code = file_get_contents($this->mFile);
155-
$stmts = $this->mParser->parse($code);
156-
$structs = $this->struct($stmts);
157-
echo $this->render($structs);
228+
//@todo Check for existence
229+
$this->mFile = $file;
230+
$structs = $this->struct($this->mParser->parse(file_get_contents($this->mFile)), TRUE);
231+
echo $this->render($structs, $options);
158232
}
159233
}

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,16 @@ dependency.
2525
curl -s http://getcomposer.org/installer | php
2626
php composer.phar install
2727

28+
See [phpctags on packagist](http://packagist.org/packages/techlivezheng/phpctags)
29+
for more details.
30+
2831
Requirements
2932
------------
3033

31-
PHP CLI 5.3+
34+
* PHP CLI 5.3+
35+
* [PHP-Parser](https://github.com/nikic/PHP-Parser)
3236

3337
Acknowledgements
3438
----------------
3539

36-
* [Snapi](https://github.com/sanpii)
40+
* [Snapi](https://github.com/sanpii) for composer support.

phpctags

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#!/usr/bin/php -q
22
<?php
3-
43
if (is_dir($vendor = __DIR__ . '/vendor')) {
54
require($vendor . '/autoload.php');
65
} elseif (is_dir($vendor = __DIR__ . '/../../../vendor')) {
@@ -13,17 +12,40 @@ if (is_dir($vendor = __DIR__ . '/vendor')) {
1312
);
1413
}
1514

16-
$options = getopt('f:',array('version'));
15+
$options = getopt('f:',array(
16+
'debug',
17+
'excmd::',
18+
'fields::',
19+
'format::',
20+
'version',
21+
));
22+
23+
if(!isset($options['debug'])) {
24+
error_reporting(0);
25+
}
1726

1827
if(isset($options['version'])) {
1928
echo <<<'EOF'
29+
Version: 0.2
30+
2031
Exuberant Ctags compatiable PHP enhancement, Copyright (C) 2012 Techlive Zheng
2132
Addresses: <techlivezheng@gmail.com>, https://github.com/techlivezheng/phpctags
33+
2234
EOF;
2335
exit;
2436
}
2537

2638
$file = realpath(array_pop($argv));
2739

28-
$ctags = new PHPCtags($file);
29-
$ctags->export();
40+
if(!isset($options['excmd']))
41+
$options['excmd'] = 'pattern';
42+
if(!isset($options['format']))
43+
$options['format'] = 2;
44+
if(!isset($options['fields'])) {
45+
$options['fields'] = array('n', 'k','s', 'a');
46+
} else {
47+
$options['fields'] = str_split($options['fields']);
48+
}
49+
50+
$ctags = new PHPCtags();
51+
$ctags->export($file, $options);

0 commit comments

Comments
 (0)