PyCharm插件開發實踐之PyGetterAndSetter詳解
背景需求
在面向對象的設計中,典型如Java語言,為瞭控制對象屬性的修改入口,我們常用的做法是把屬性設置為private,然後通過getter和setter方法訪問、修改該屬性。
但是在Python語言中,並沒有Java的訪問控制符,對象的屬性可以直接訪問、修改。
為瞭良好的設計規范,我們可以規定,在Python類中,所有的對象屬性均以下劃線”_”前綴開頭,同時編寫該屬性的getter和setter方法,在其他地方引用的時候,禁止出現直接引用。
在IDEA等IDE中,可以對Java的對象屬性直接生成getter和setter方法,但是針對Python沒有這樣的功能。大量的getter和setter方法,很耗費精力,所以需要一款插件來輔助自動化生成Python對象屬性的getter和setter方法。
搭建環境
編寫IDEA系列的插件開發環境,可以看我之前的一篇文章:《IntelliJ IDEA/Android Studio插件開發指南》
官方開發文檔:IntelliJ Platform SDK
過程拆解
Python文件例子:
class Test(object): def __init__(self): self._var1 = "" self._var2 = 0
明確瞭需求、輸入(python對象屬性定義代碼)、輸出(PyCharm插件自動生成getter和setter)後,我們針對這個插件的流程進行拆解:
- 首先,用戶選中瞭對應行的文本內容,插件獲取到該內容文本
- 在內容文本中過濾出變量,在本例中,就是過濾出_var1, _var2
- 拼裝變量的getter和setter方法
- 計算出要插入的位置
- 回寫到編輯器中
1. 獲取文本
在PyCharm插件中,Editor對象是編輯器的總覽,其中包含很多Model,比如
CaretModel caretModel=editor.getCaretModel(); // 用於描述插入光標 SelectionModel selectionModel = editor.getSelectionModel(); // 用於描述選中的文本 FoldingModel foldingModel = editor.getFoldingModel(); // 用於描述代碼折疊區域 IndentsModel indentModel = editor.getIndentsModel(); // 用於描述縮進 ……
在這裡,我們隻需要SelectionModel。
// 獲取光標選中文本段對象 SelectionModel selectionModel = editor.getSelectionModel(); // 拿到選中部分字符串 String selectedText = selectionModel.getSelectedText();
2. 正則匹配
拿到選中文本後,有可能選擇瞭多行,裡面包含多個變量,所以我們需要獲取到變量列表。
觀察到所有的變量都是self.abc=xxx
的模式,我們可以考慮用正則匹配把其中的abc
獲取到。
Java中負責正則匹配並獲取匹配字符串的類是Pattern
和Matcher
。
/** * 獲取選中文本中所有的self.value中的value <br> * e.g. self.value = xxx,or self._value = xxx, <br> * 可以獲取到其中的value * * @param selectedText 選中文本 * @return 變量字符串列表 */ public ArrayList<String> getFieldList(String selectedText) { ArrayList<String> list = new ArrayList<>(); // 刪除所有空格 selectedText = selectedText.replaceAll(" ", ""); // 正則匹配獲得變量字符串 String reg = "self.(.*?)="; Pattern pattern = Pattern.compile(reg); Matcher matcher = pattern.matcher(selectedText); while (matcher.find()) { list.add(matcher.group(1)); } return list; }
3. 拼裝方法
Python中的getter和setter方法都非常簡單,我們可以先創造一個模板:
// 定義Getter和Setter的模板 String getterTemplate = " def get_word(self):\n return self.field\n "; 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如下:
# 假如變量為_var def get_var(self): return self._var; def set_var(self, var): self._var = var;
可以看到在self.xxx
中需要使用變量,而在get_xxx
和setter的參數中,需要刪除對應的下劃線。所以有:
…… // 對於 “_value” 類型的變量,在set方法參數中,隻需要“value” for (String field : fieldList) { String tmp = field; int i = 0; while (tmp.charAt(i) == '_') { tmp = tmp.substring(1); } // 替換掉模板中的變量 String customGetter = getterTemplate.replaceAll("word", tmp).replaceAll("field", field); String customSetter = setterTemplate.replaceAll("word", tmp).replaceAll("field", field); stringBuilder.append("\n").append(customGetter).append("\n").append(customSetter); } ……
4. 計算位置
首先需要獲取到Document對象,這是負責描述文檔的,裡面有很多負責文檔的方法,比如在文件中插入字符串,計算文件行數,計算文檔長度,刪除相應內容等等。
Document document = editor.getDocument();
為瞭方便簡單,我們設定在選中文本的下一行生成getter和setter。
// 得到選中字符串的結束位置 int endOffset = selectionModel.getSelectionEnd(); // 得到最大插入字符串(生成的Getter和Setter函數字符串)的位置 int maxOffset = document.getTextLength(); // 計算選中字符串所在的行號,通過行號得到下一行的第一個字符的起始偏移量 int curLineNumber = document.getLineNumber(endOffset); int docLineCount = document.getLineCount(); // 如果目前文件行數不足以支持選中文本的下一行,也就是選中文本包含最後一行,就插入一個空行 if (docLineCount - 1 < curLineNumber + 1) { Runnable runnable = () -> document.insertString(maxOffset,"\n"); WriteCommandAction.runWriteCommandAction(project, runnable); } 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
// 對文檔進行操作部分代碼,需要放入runnable,不然IDEA會卡住 Runnable runnable = () -> document.insertString(nextLineStartOffset, genGetterAndGetter(fieldList)); // 加入任務,由IDE調度任務 WriteCommandAction.runWriteCommandAction(project, runnable);
效果
目前來看效果還不錯,關於安裝方法、使用方法,見github的README。
資源
github鏈接:https://github.com/mybichu/PyGetterAndSetter
到此這篇關於PyCharm插件開發實踐之PyGetterAndSetter詳解的文章就介紹到這瞭,更多相關PyCharm插件開發內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Java正則表達式之分組和替換方式
- java 如何判斷是否是26個英文字母
- 解決javaBean規范導致json傳參首字母大寫將永遠獲取不到問題
- Java中Pattern用法實例(正則表達式)
- 如何使用PyCharm及常用配置詳解