背景需求

在面向对象的设计中,典型如Java语言,为了控制对象属性的修改入口,我们常用的做法是把属性设置为private,然后通过getter和setter方法访问、修改该属性。

但是在Pthon语言中,并没有Java的访问控制符,对象的属性可以直接访问、修改。

为了良好的设计规范,我们可以规定,在Python类中,所有的对象属性均以下划线"_"前缀开头,同时编写该属性的getter和setter方法,在其他地方引用的时候,禁止出现直接引用。

在IDEA等IDE中,可以对Java的对象属性直接生成getter和setter方法,但是针对Python没有这样的功能。大量的getter和setter方法,很耗费精力,所以需要一款插件来辅助自动化生成Python对象属性的getter和setter方法。

搭建环境

编写IDEA系列的插件开发环境,可以看我之前的一篇文章:《IntelliJ IDEA/Android Studio插件开发指南》

官方开发文档:IntelliJ Platform SDK

过程拆解

Python文件例子:

  1. class Test(object):
  2. def __init__(self):
  3. self._var1 = ""
  4. self._var2 = 0

明确了需求、输入(python对象属性定义代码)、输出(PyCharm插件自动生成getter和setter)后,我们针对这个插件的流程进行拆解:

  1. 首先,用户选中了对应行的文本内容,插件获取到该内容文本
  2. 在内容文本中过滤出变量,在本例中,就是过滤出_var1, _var2
  3. 拼装变量的getter和setter方法
  4. 计算出要插入的位置
  5. 回写到编辑器中

1. 获取文本

在PyCharm插件中,Editor对象是编辑器的总览,其中包含很多Model,比如

  1. CaretModel caretModel=editor.getCaretModel(); // 用于描述插入光标
  2. SelectionModel selectionModel = editor.getSelectionModel(); // 用于描述选中的文本
  3. FoldingModel foldingModel = editor.getFoldingModel(); // 用于描述代码折叠区域
  4. IndentsModel indentModel = editor.getIndentsModel(); // 用于描述缩进
  5. ……

在这里,我们只需要SelectionModel。

  1. // 获取光标选中文本段对象
  2. SelectionModel selectionModel = editor.getSelectionModel();
  3. // 拿到选中部分字符串
  4. String selectedText = selectionModel.getSelectedText();

2. 正则匹配

拿到选中文本后,有可能选择了多行,里面包含多个变量,所以我们需要获取到变量列表。

观察到所有的变量都是self.abc=xxx的模式,我们可以考虑用正则匹配把其中的abc获取到。

Java中负责正则匹配并获取匹配字符串的类是PatternMatcher

  1. /**
  2. * 获取选中文本中所有的self.value中的value <br>
  3. * e.g. self.value = xxx,or self._value = xxx, <br>
  4. * 可以获取到其中的value
  5. *
  6. * @param selectedText 选中文本
  7. * @return 变量字符串列表
  8. */
  9. public ArrayList<String> getFieldList(String selectedText) {
  10. ArrayList<String> list = new ArrayList<>();
  11. // 删除所有空格
  12. selectedText = selectedText.replaceAll(" ", "");
  13. // 正则匹配获得变量字符串
  14. String reg = "self.(.*?)=";
  15. Pattern pattern = Pattern.compile(reg);
  16. Matcher matcher = pattern.matcher(selectedText);
  17. while (matcher.find()) {
  18. list.add(matcher.group(1));
  19. }
  20. return list;
  21. }

3. 拼装方法

Python中的getter和setter方法都非常简单,我们可以先创造一个模板:

  1. // 定义Getter和Setter的模板
  2. String getterTemplate = " def get_word(self):\n return self.field\n ";
  3. String setterTemplate = " def set_word(self, word):\n self.field = word\n ";

之所以存在空格,是为了匹配PyCharm的缩进,我这里使用的4个空格做缩进,如果你使用两个空格的话,在这里修改成两个空格即可。

在这里不能使用\t,我尝试了\t,在PyCharm中无法自动转换为4个空格,会报错。

上一步获取到的变量,有可能不存在下换线前缀,也有可能存在1个或者2个下划线前缀,比如var,_var,__var,他们对应的gett和setter如下:

  1. # 假如变量为_var
  2. def get_var(self):
  3. return self._var;
  4. def set_var(self, var):
  5. self._var = var;

可以看到在self.xxx中需要使用变量,而在get_xxx和setter的参数中,需要删除对应的下划线。所以有:

  1. ……
  2. // 对于 “_value” 类型的变量,在set方法参数中,只需要“value”
  3. for (String field : fieldList) {
  4. String tmp = field;
  5. int i = 0;
  6. while (tmp.charAt(i) == '_') {
  7. tmp = tmp.substring(1);
  8. }
  9. // 替换掉模板中的变量
  10. String customGetter = getterTemplate.replaceAll("word", tmp).replaceAll("field", field);
  11. String customSetter = setterTemplate.replaceAll("word", tmp).replaceAll("field", field);
  12. stringBuilder.append("\n").append(customGetter).append("\n").append(customSetter);
  13. }
  14. ……

4. 计算位置

首先需要获取到Document对象,这是负责描述文档的,里面有很多负责文档的方法,比如在文件中插入字符串,计算文件行数,计算文档长度,删除相应内容等等。

  1. Document document = editor.getDocument();

为了方便简单,我们设定在选中文本的下一行生成getter和setter。

  1. // 得到选中字符串的结束位置
  2. int endOffset = selectionModel.getSelectionEnd();
  3. // 得到最大插入字符串(生成的Getter和Setter函数字符串)的位置
  4. int maxOffset = document.getTextLength();
  5. // 计算选中字符串所在的行号,通过行号得到下一行的第一个字符的起始偏移量
  6. int curLineNumber = document.getLineNumber(endOffset);
  7. int docLineCount = document.getLineCount();
  8. // 如果目前文件行数不足以支持选中文本的下一行,也就是选中文本包含最后一行,就插入一个空行
  9. if (docLineCount - 1 < curLineNumber + 1) {
  10. Runnable runnable = () -> document.insertString(maxOffset,"\n");
  11. WriteCommandAction.runWriteCommandAction(project, runnable);
  12. }
  13. int nextLineStartOffset = document.getLineStartOffset(curLineNumber + 1);

5. 回写

将字符串插入文档中,不能直接使用document.insertString,会error: Assertion failed: Write access is allowed inside write-action only (see com.intellij.openapi.application.Application.runWriteAction())

需要把这个任务放入一个Runnable中,然后由WriteCommandAction来调度。

参考:Write access is allowed inside write-action only

  1. // 对文档进行操作部分代码,需要放入runnable,不然IDEA会卡住
  2. Runnable runnable = () -> document.insertString(nextLineStartOffset, genGetterAndGetter(fieldList));
  3. // 加入任务,由IDE调度任务
  4. WriteCommandAction.runWriteCommandAction(project, runnable);

效果

目前来看效果还不错,关于安装方法、使用方法,见github的README。

资源

github链接:https://github.com/mybichu/PyGetterAndSetter

PyCharm插件开发实践-PyGetterAndSetter的更多相关文章

  1. vue插件开发实践与要点

    其实就跟组件差不多意思,组件也可以实现相关的效果,但要在用到的地方都引用插件就可以全局注册,不需引用 试着撸一个插件,有2个功能,提示和对话框 网上找了个toast插件的代码,改了改,扩展加了个dia ...

  2. 原生Javascript插件开发实践

    前言 之前公司设计的网站比较混乱,很多地方不统一,其中一个就是弹出层,导致这个原因是因为,公司的UI换了好几个人,而他们每个人做出来的都不太一样.最近公司开始整顿这个问题,对于统一的这种东西当然是做成 ...

  3. Fiddler4插件开发实践

    Fiddler4 是一款 巴拉巴拉..... 连接在这:http://www.telerik.com/fiddler 开发文档在这:http://docs.telerik.com/fiddler/Ex ...

  4. pinpoint插件开发实践

    plugin基本结构 一个plugin主要由三部分构成,插件类增强定义(ProfilerPlugin接口实现).插件描述定义(TraceMetadataProvider接口实现).增强类拦截器实现(A ...

  5. 🏆【Java技术专区】「开发实战专题」Lombok插件开发实践必知必会操作!

    前言 在目前众多编程语言中,Java 语言的表现还是抢眼,不论是企业级服务端开发,还是 Andorid 客户端开发,都是作为开发语言的首选,甚至在大数据开发领域,Java 语言也能占有一席之地,如Ha ...

  6. PyCharm远程开发配置及一些问题的解决方案

    PyCharm远程开发配置 具体请参考:https://www.jianshu.com/p/79df9ac88e96 Tips:必须要安装PyCharm专业版 实践过程中遇到的问题 背景 因项目需要, ...

  7. Eclipse插件开发_学习_00_资源帖

    一.官方资料 1.eclipse api 2.GEF Developer's Guide 二. 精选资料 1.开发 Eclipse 插件 2.Eclipse, RCP, Plugin and OSGi ...

  8. Hexo 相册实践

    灵感 想给自已的blog添加一个相册功能.给生活中的点点滴滴留影记录.搜寻网络上给Next主题添加相册功能的基本上没有,只能重头到尾开始一点点的实践.    大致的想法:  1. 相册展示类似于归档一 ...

  9. 适合 JS 新手学习的开源项目——在 GitHub 学编程

    作者:HelloGitHub-小鱼干 这里是 HelloGitHub 的<GitHub 上适合新手的开源项目>系列的最后一篇,系列文章: C++ 篇 Python 篇 Go 篇 Java ...

随机推荐

  1. 解决log4net多进程日志文件被占用

    <log4net debug="true"> <appender name="RollingLogFileAppender" type=&qu ...

  2. mfc HackerTools防止程序双开

    来自:https://github.com/TonyChen56/HackerTools 1 HANDLE hMutex = CreateMutexA(NULL, FALSE, "GuiSh ...

  3. shiro(二)

    public class AuthorizerTest { @Test public void testIsPermitted() { login("classpath:shiro-auth ...

  4. linux 常用命令(一)——查看硬盘空间-内存-线程的cpu负载-线程内存

    系统参数检查: df -h [enter] 检查硬盘空间 TIP: 使用 man df 可查看该命令使用说明 ; q 退出. free检查内存使用情况: free [enter] TIP: 使用 ma ...

  5. Android WorkManager 定时任务

    App有时可能需要定期运行某些工作.例如,可能要定期备份数据.上传信息到服务器,定期获取新的内容等等. 在app运行期间,我们使用Handler也可以完成定期的功能.在这里我们介绍WorkManage ...

  6. Qt中的Q_PROPERTY宏浅析

    1. Q_PROPERTY Qt提供了一个绝妙的属性系统,Q_PROPERTY()是一个宏,用来在一个类中声明一个属性property,由于该宏是qt特有的,需要用moc进行编译,故必须继承于QObj ...

  7. mybaits源码分析--事务管理(八)

    一.事务管理 写到这也快进入收尾阶段了了,在介绍MyBatis中的事务管理时不可避免的要接触到DataSource的内容,所以接下来会分别来介绍DataSource和Transaction两块内容. ...

  8. JQ动画

    /* //基本 show([s,[e],[fn]]) 显示元素 hide([s,[e],[fn]]) 隐藏元素 //滑动 slideDown([s],[e],[fn]) 向下滑动 slideUp([s ...

  9. 各色Tarjan集合

    #include<bits/stdc++.h> using namespace std; const int N=100000,M=200000; //所有Tarjan都要: // dfn ...

  10. Spring Cloud Eureka 之常用配置解析

    [原创内容,转载.引用请注明出处] 1. 配置项解析 1.1 通用配置 # 应用名称,将会显示在Eureka界面的应用名称列 spring.application.name=config-servic ...