起因
前一篇写了反射类生成php速查表。过年了在家也没事,想完成自己想要实现的sublime自动完成文件生成。就是sublime里输入关键字后按tab自动补全一个全的函数名和参数的,按tab可以切换一致到最后的。也是给别人的承诺,今年肯定会实现sublime的tp5插件的。
地址在 https://github.com/yangweijie/SublimeThinkPHP5.0
思路
搞清sublime 完成的写法
"HTML_FILE_SUFFIX", ???????"TEMPLATE_NAME", ???????{ "trigger": "_ad", "contents": "protected function _after_delete(\\$data,\\$options) {\n ??${1:}\n}$0" }, ???????{ "trigger": "_af", "contents": "protected function _after_find(&\\$result,\\$options) {\n ?${1:}\n}$0" }, ???????{ "trigger": "_ai", "contents": "protected function _after_insert(\\$data,\\$options) {\n ??${1:}\n}$0" }, ???????{ "trigger": "_as", "contents": "protected function _after_select(&\\$result,\\$options){\n ${1:foreach(\\$result as &\\$record)\\{\n ??????${2:\\$this->_after_find(\\$record,\\$options);}\n ?\\}}\n}$0" }, ???????{ "trigger": "_au", "contents": "protected function _after_update(\\$data,\\$options) {\n ??${1:}\n}$0" }, ???????{ "trigger": "_bi", "contents": "protected function _before_insert(&\\$data,\\$options) {\n ${1:}\n}$0" }, ???????{ "trigger": "_bu", "contents": "protected function _before_update(&\\$data,\\$options) {\n ${1:}\n}$0" }, ???????{ "trigger": "->_empty", "contents": "\\$this->_empty()$0" }, ???????{ "trigger": "_get", "contents": "_get(‘${1:\\$name}‘)$0" }, ???????{ "trigger": "_post", "contents": "_post(‘${1:\\$name}‘)$0" }, ???????{ "trigger": "->_sql", "contents": "->_sql(‘${1:\\$name}‘)$0" }, ???????{ "trigger": "->addAll", "contents": "->addAll(\\$${1:dataList},\\$${2:options},\\$${3:replace})$0" },
这个是写在Sublime的ThinkPHP 插件里 php.sublime-completions 文件里的。
completion 和snippet的区别在于 snippet只能一个文件一个补全。命令面版里有提示。
字符串
json 里直接字符串
tab占位
$0
完成的最后光标
tab 选中光标 ??${1:XX}
?1自增下标,: 选中的内容
trigger 触发器
表示什么文本末按tab会触发完成。
注意点
$ 换行之类的要转义
php如何获取
先上核心代码:
<?phpnamespace util;class SnippetBuilder{ ???public $cme; ???public function __construct($classes) ???{ ???????// if (false == defined(‘THINK_VERSION‘)) { ???????// ????throw new \Exception(‘请在thinkphp应用中执行本类‘, 1); ???????// } ???????$this->cme ???????????= new ClassMethodExtractor(); ???????// $this->framework_type = version_compare(THINK_VERSION, ‘5‘) >= 0 ? 5 : 3; ???????$this->classes ???????= $classes; ???} ???public function buildAll($path) ???{ ???????$consts ???= $this->getConsts(); ???????$functions = $this->getFunctions(); ???????$classes = $this->classes; ???????$this->ret = []; ???????if ($consts) { ???????????$this->buildConsts($consts); ???????} ???????if ($functions) { ???????????$this->buildFunction($functions); ???????} ???????if ($classes) { ???????????foreach ($classes as $class) { ???????????????$class2 = $this->cme->getClassAnnotation(new \ReflectionClass($class)); ???????????????$this->buildClass($class2); ???????????} ???????} ???????if ($this->ret) { ???????????file_put_contents($path, $this->parseAll($this->ret)); ???????} else { ???????????exit(‘没有可生成的内容‘); ???????} ???} ???// 获取常量数组 ???public function getConsts() ???{ ???????$all_consts = get_defined_constants(true); ???????return array_keys($all_consts[‘user‘]); ???} ???// 生成常量完成 ???public function buildConsts($consts) ???{ ???????foreach ($consts as $key => &$const) { ???????????$this->ret[] = $const; ???????} ???} ???// 生成类的完成 ???public function buildClass($classes) ???{ ???} ???// 获取定义的函数 ???public function getFunctions() ???{ ???????$arr = get_defined_functions(); ???????$ret = []; ???????foreach ($arr[‘user‘] as $key => $name) { ???????????$foo ????????= []; ???????????$refFunc ????= new \ReflectionFunction($name); ???????????$foo[‘args‘] = $refFunc->getParameters(); ???????????$ret[$name] ?= $foo; ???????} ???????return $ret; ???} ???// 生成函数 ???public function buildFunction($functions) ???{ ???}}
常量
// 获取常量数组public function getConsts(){ ???????$all_consts = get_defined_constants(true); ???????return array_keys($all_consts[‘user‘]);}
php有函数,且会区分core user 和扩展的。我们只需要user定义,也就是tp框架定义的。
函数
// 获取定义的函数public function getFunctions(){ ???????$arr = get_defined_functions(); ???????$ret = []; ???????foreach ($arr[‘user‘] as $key => $name) { ???????????????$foo ????????= []; ???????????????$refFunc ????= new \ReflectionFunction($name); ???????????????$foo[‘args‘] = $refFunc->getParameters(); ???????????????$ret[$name] ?= $foo; ???????} ???????return $ret;}
php也有个获取全部函数名的方法。同样只拿user
然后新的函数反射类 new \ReflectionFunction($name);
获取它的参数 $refFunc->getParameters();
类
$all_class = get_declared_classes();
这回没有user好事了,不过可以反射类isInternal区分。
// 获取全部用户定义的类 ???public function getAllUserClasses(){ ???????$all_class = get_declared_classes(); ???????$ret = []; ???????foreach ($all_class as $class) { ???????????$rf = new \ReflectionClass($class); ???????????if(false == $rf->isInternal()){ ???????????????if(‘app\index\controller\Index‘ == $class){ ???????????????????continue; ???????????????} ???????????????$ret[] = $class; ???????????} ???????} ???????return $ret; ???}
????????if ($classes) { ???????????????foreach ($classes as $class) { ???????????????????$class2 = $this->cme->getClassAnnotation(new \ReflectionClass($class)); ???????????????????????$this->buildClass($class2); ???????????????} ???????????}
用cme类提取出类和对应方法及参数。
生成
我写了 SublimeSnippetBuilder来继承我之前的 SnippetBuilder,因为我想做的是所有文本编辑器的完成生成。先架构好结构,以后慢慢填坑。
先整体代码:
<?phpnamespace util;class SublimeSnippetBuilder extends SnippetBuilder{ ???// 生成函数 ???public function buildFunction($functions) ???{ ???????foreach ($functions as $name => $fun) { ???????????$args_arr = [$name, ‘(‘, ‘)‘]; ???????????if ($fun[‘args‘]) { ???????????????$args_arr = [$name, ‘(‘]; ???????????????$index ???= 1; ???????????????foreach ($fun[‘args‘] as $key => $arg) { ???????????????????$p = new \ReflectionParameter($name, $key); ???????????????????if ($p->isPassedByReference()) { ???????????????????????$arg_str_new = ‘\&\\$‘ . $p->getName(); ???????????????????} else { ???????????????????????$arg_str_new = ‘\\$‘ . $p->getName(); ???????????????????} ???????????????????if ($p->isOptional() && $p->isDefaultValueAvailable()) { ???????????????????????// 获取某些内部类的参数会抛异常,且异常时$class会变化不是我们想知道的哪个类方法一场了 ???????????????????????try { ???????????????????????????$defaul = $p->getDefaultValue(); ???????????????????????????$arg_str_new .= is_array($defaul) ? ‘ = []‘ : ‘ = ‘ . var_export($defaul, 1); ???????????????????????} catch (\Exception $e) { ???????????????????????????trace($p->isVariadic()); ???????????????????????????trace($name . ‘_‘ . $key); ???????????????????????} ???????????????????} ???????????????????if ($index == 1) { ???????????????????????$p_str = sprintf(‘${%d:%s}‘, $index, $arg_str_new); ???????????????????} else { ???????????????????????$p_str = sprintf(‘${%d: ,%s}‘, $index, $arg_str_new); ???????????????????} ???????????????????$args_arr[] = $p_str; ???????????????????$index++; ???????????????} ???????????????$args_arr[] = ‘)‘; ???????????} ???????????$contens = implode(‘‘, $args_arr) . ‘$0‘; ???????????$foo ????= [ ???????????????‘trigger‘ ?=> $name, ???????????????‘contents‘ => $contens, ???????????]; ???????????$this->ret[] = $foo; ???????} ???} ???public function buildClass($class) ???{ ???????if($class[‘methods‘]){ ???????????foreach ($class[‘methods‘] as $name => $fun) { ???????????????switch ($fun[‘type‘]) { ???????????????????case ‘public_static‘: ???????????????????case ‘private_static‘: ???????????????????????$trigger_name = "::{$name}"; ???????????????????????break; ???????????????????case ‘public_public‘: ???????????????????case ‘private_public‘: ???????????????????????$trigger_name = "->{$name}"; ???????????????????????break; ???????????????????default: ???????????????????????$trigger_name = ‘‘; ???????????????????????break; ???????????????} ???????????????$args_arr = [$trigger_name, ‘(‘, ‘)‘]; ???????????????if (empty($fun[‘args‘]) == false) { ???????????????????$args_arr = [$trigger_name, ‘(‘]; ???????????????????$index ???= 1; ???????????????????foreach ($fun[‘args‘] as $key => $p) { ???????????????????????if ($p->isPassedByReference()) { ???????????????????????????$arg_str_new = ‘\&\\$‘ . $p->getName(); ???????????????????????} else { ???????????????????????????$arg_str_new = ‘\\$‘ . $p->getName(); ???????????????????????} ???????????????????????if ($p->isOptional() && $p->isDefaultValueAvailable()) { ???????????????????????????// 获取某些内部类的参数会抛异常,且异常时$class会变化不是我们想知道的哪个类方法一场了 ???????????????????????????try { ???????????????????????????????$defaul = $p->getDefaultValue(); ???????????????????????????????$arg_str_new .= is_array($defaul) ? ‘ = []‘ : ‘ = ‘ . var_export($defaul, 1); ???????????????????????????} catch (\Exception $e) { ???????????????????????????????trace($p->isVariadic()); ???????????????????????????????trace($name . ‘_‘ . $key); ???????????????????????????} ???????????????????????} ???????????????????????if ($index == 1) { ???????????????????????????$p_str = sprintf(‘${%d:%s}‘, $index, $arg_str_new); ???????????????????????} else { ???????????????????????????$p_str = sprintf(‘${%d: ,%s}‘, $index, $arg_str_new); ???????????????????????} ???????????????????????$args_arr[] = $p_str; ???????????????????????$index++; ???????????????????} ???????????????????$args_arr[] = ‘)‘; ???????????????} ???????????????$contens = implode(‘‘, $args_arr) . ‘$0‘; ???????????????$foo ????= [ ???????????????????‘trigger‘ ?=> $trigger_name, ???????????????????‘contents‘ => $contens, ???????????????]; ???????????????$this->ret[] = $foo; ???????????} ???????} ???} ???public function parseAll($ret) ???{ ???????// dump($ret); ???????$ret = [ ???????????"scope" ??????=> "source.php - variable.other.php", ???????????"completions" => $ret, ???????]; ???????return json_encode($ret, JSON_PRETTY_PRINT); ???}}
其实 就是生成对应的多维数组,数字索引为常量,函数方法为多维数组。
函数的trigger就是函数名,类无非::
、->
后跟方法名
然后区分代参不带参,带参再遍历参数,区分引用和默认值的情况。用了sprintf 方法拼接${index:content} 这种格式。
最终生成json,用了JSON_PRETTY_PRINT 格式化输出后写文件。
整体思路还是很清晰的。
其他语言的可以借鉴。
效果:
一下子两千行,手写得累死。
其实,大家只需扩展getAllUserClasses 就可以生成其他类的,比如swoole的。
题外话,新插件提交wbond 开始用branch 结果不推荐了要tag。我折腾了几次才通过ci。人家不高兴了。不给我过,有能力的直接git ?clone吧。视图那没找到规律先手写的。
php类库到sublime完成
原文地址:http://blog.51cto.com/yangweijie/2072928