lombok-intellij-plugin插件开发
本文配合 [[扩展你的lombok-2.0]] 所更新。在 [[扩展你的lombok]] 里面有旧版插件开发。
环境准备
克隆仓库
git clone https://github.com/mplushnikov/lombok-intellij-plugin.git
下载完成后使用idea打开该项目。之前需要修改大量的配置,现在不需要了。
修改gradle.properties,修改插件版本号 pluginVersion
和平台版本号platformVersion
(也就是对应的idea版本):
pluginVersion=999.1.1
#platformVersion = LATEST-EAP-SNAPSHOT
platformVersion = 2023.3.8
LATEST-EAP-SNAPSHOT会获取最新社区版,但是项目使用了几个旧版本的API。这边就不改原代码了,直接使用旧版本。
修改build.gradle,添加本地仓库和
repositories {
mavenCentral()
// 添加本地仓库,用来获取我们开发的lombok
maven{
url "file://c://repo"
}
}
dependencies {
// lombok group: 'org.projectlombok', name: 'lombok', version: '1.18.30', classifier: 'sources', ext: 'jar'
// 我们自己开发的插件
lombok group: 'org.projectlombok', name: 'lombok', version: '1.18.29-bro', classifier: 'sources', ext: 'jar'
testImplementation("junit:junit:4.13.2")
testImplementation("org.mockito:mockito-core:5.8.0")
testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.10.1")
}
执行准备环境task
prepareUiTestingSandbox
开发功能
LombokClassNames添加TO_JSON_STRING
package de.plushnikov.intellij.plugin;
public interface LombokClassNames {
// 添加ToJsonString注解全限定类名
@NonNls String TO_JSON_STRING = "lombok.extern.json.ToJsonString";
}
添加ToJsonStringProcessor
package de.plushnikov.intellij.plugin.processor.clazz;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import de.plushnikov.intellij.plugin.LombokClassNames;
import de.plushnikov.intellij.plugin.problem.ProblemSink;
import de.plushnikov.intellij.plugin.psi.LombokLightMethodBuilder;
import de.plushnikov.intellij.plugin.thirdparty.LombokAddNullAnnotations;
import de.plushnikov.intellij.plugin.util.PsiClassUtil;
import de.plushnikov.intellij.plugin.util.PsiMethodUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class ToJsonStringProcessor extends AbstractClassProcessor {
public static final String TO_JSON_STRING_METHOD_NAME = "toJsonString";
public ToJsonStringProcessor() {
super(PsiMethod.class, LombokClassNames.TO_JSON_STRING);
}
@Override
protected boolean possibleToGenerateElementNamed(@Nullable String nameHint, @NotNull PsiClass psiClass,
@NotNull PsiAnnotation psiAnnotation) {
return nameHint == null || nameHint.equals(TO_JSON_STRING_METHOD_NAME);
}
@Override
protected boolean validate(@NotNull PsiAnnotation psiAnnotation, @NotNull PsiClass psiClass, @NotNull ProblemSink builder) {
boolean result = true;
if (psiClass.isAnnotationType() || psiClass.isInterface()) {
result = false;
}
if (result) {
if (hasToJsonStringMethodDefined(psiClass)) {
builder.addWarningMessage("inspection.message.not.generated.s.method.with.same.name.already.exists", TO_JSON_STRING_METHOD_NAME);
}
}
return result;
}
@Override
protected void generatePsiElements(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation, @NotNull List<? super PsiElement> target, @Nullable String nameHint) {
target.addAll(createToJsonStringMethod4All(psiClass, psiAnnotation));
}
private boolean hasToJsonStringMethodDefined(@NotNull PsiClass psiClass) {
final Collection<PsiMethod> classMethods = PsiClassUtil.collectClassMethodsIntern(psiClass);
return PsiMethodUtil.hasMethodByName(classMethods, TO_JSON_STRING_METHOD_NAME, 0);
}
@NotNull
Collection<PsiMethod> createToJsonStringMethod4All(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation) {
if (hasToJsonStringMethodDefined(psiClass)) {
return Collections.emptyList();
}
final PsiMethod stringMethod = createToJsonStringMethod(psiClass, psiAnnotation);
return Collections.singletonList(stringMethod);
}
@NotNull
public PsiMethod createToJsonStringMethod(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation) {
final PsiManager psiManager = psiClass.getManager();
// 直接生成对应的string
final String body = "com.alibaba.fastjson.JSON.toJSONString(this)";
final String blockText = String.format("return %s;", body);
final LombokLightMethodBuilder methodBuilder = new LombokLightMethodBuilder(psiManager, TO_JSON_STRING_METHOD_NAME)
.withMethodReturnType(PsiType.getJavaLangString(psiManager, GlobalSearchScope.allScope(psiClass.getProject())))
.withContainingClass(psiClass)
.withNavigationElement(psiAnnotation)
.withModifier(PsiModifier.PUBLIC)
.withBodyText(blockText);
LombokAddNullAnnotations.createRelevantNonNullAnnotation(psiClass, methodBuilder);
return methodBuilder;
}
}
添加LombokToJsonStringHandler
package de.plushnikov.intellij.plugin.action.lombok;
import com.intellij.psi.*;
import de.plushnikov.intellij.plugin.LombokClassNames;
import org.jetbrains.annotations.NotNull;
public class LombokToJsonStringHandler extends BaseLombokHandler {
@Override
protected void processClass(@NotNull PsiClass psiClass) {
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(psiClass.getProject());
final PsiClassType stringClassType = factory.createTypeByFQClassName(CommonClassNames.JAVA_LANG_STRING, psiClass.getResolveScope());
final PsiMethod toStringMethod = findPublicNonStaticMethod(psiClass, "toJsonString", stringClassType);
if (null != toStringMethod) {
toStringMethod.delete();
}
addAnnotation(psiClass, LombokClassNames.TO_JSON_STRING);
}
}
添加LombokToJsonStringAction
package de.plushnikov.intellij.plugin.action.lombok;
public class LombokToJsonStringAction extends BaseLombokAction {
public LombokToJsonStringAction() {
super(new LombokToJsonStringHandler());
}
}
修改LombokProcessorManager
package de.plushnikov.intellij.plugin.processor;
@Service
public final class LombokProcessorManager {
// json
private final ToJsonStringProcessor myToJsonStringProcessor = new ToJsonStringProcessor();
public ToJsonStringProcessor getToJsonStringProcessor() {
return myToJsonStringProcessor;
}
@NotNull
private Collection<Processor> getAllProcessors() {
return Arrays.asList(
// 添加
myToJsonStringProcessor,
....
)
}
}
添加DelombokToJsonStringAction
package de.plushnikov.intellij.plugin.action.delombok;
import de.plushnikov.intellij.plugin.processor.LombokProcessorManager;
import org.jetbrains.annotations.NotNull;
public class DelombokToJsonStringAction extends AbstractDelombokAction {
@Override
@NotNull protected DelombokHandler createHandler() {
return new DelombokHandler(LombokProcessorManager.getInstance().getToJsonStringProcessor());
}
}
修改LombokBundle.properties
# tojsonstring
action.defaultLombokToJsonString.text=Default @ToJsonString
action.defaultLombokToJsonString.description=Action to replace toString method with lombok @ToJsonString annotation
# 添加相关msg
action.delombokToJsonString.text=@ToJsonString
action.delombokToJsonString.description=Action to replace lombok @ToJsonString annotation with vanilla java methods
修改plugin.xml
<actions>
<group id="LombokActionGroup" class="de.plushnikov.intellij.plugin.action.LombokMenuGroup" icon="LombokIcons.Lombok" popup="true">
<!-- 添加Action -->
<action id="defaultLombokToJsonString" class="de.plushnikov.intellij.plugin.action.lombok.LombokToJsonStringAction"/>
<add-to-group group-id="RefactoringMenu" anchor="last"/>
</group>
<group id="DelombokActionGroup" class="de.plushnikov.intellij.plugin.action.LombokMenuGroup" icon="LombokIcons.Lombok" popup="true">
<action id="delombokToJsonString" class="de.plushnikov.intellij.plugin.action.delombok.DelombokToJsonStringAction"/>
<!-- 添加Action -->
<add-to-group group-id="RefactoringMenu" anchor="last"/>
</group>
</actions>
测试
执行测试task
runIdeForUiTests
会启动一个idea供我们调试,我们在这个idea测试环境创建我们的测试项目
添加我们的lombok依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.31</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.29-bro</version>
<scope>provided</scope>
</dependency>
使用新开发的注解
package com.bigbrotherlee;
import lombok.extern.json.ToJsonString;
@ToJsonString
@lombok.Data
public class Data {
private String name;
private int id = 1;
}
使用:
package com.bigbrotherlee;
public class Main {
public static void main(String[] args) {
Data data = new Data();
System.out.println(data.toJsonString());
data.setName("bro");
System.out.println(data.toJsonString());
}
}
如果我们的psi无法处理,toJsonString会报红。但是运行不会有错误,因为运行使用的是javac。如果psi正确,那么我们也可以在Structure卡查看到toJsonString。
打包与发布
运行buildPlugin task,打包完成后可以使用build/distributions/目录下面的插件发布到你公司内部的Idea插件仓库,或者直接提供zip从硬盘安装使用。如果build/distributions/目录没有,则在gradle界面执行buildPlugin task打包插件。