├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src ├── main │ ├── resources │ │ ├── image │ │ │ └── wechat.png │ │ ├── icons │ │ │ ├── checked.svg │ │ │ ├── checked_dark.svg │ │ │ ├── collapseAll.svg │ │ │ ├── expandAll.svg │ │ │ ├── collapseAll_dark.svg │ │ │ ├── expandAll_dark.svg │ │ │ ├── clearCash.svg │ │ │ ├── clearCash_dark.svg │ │ │ ├── refresh.svg │ │ │ ├── refresh_dark.svg │ │ │ ├── editorPreview.svg │ │ │ ├── editorPreview_dark.svg │ │ │ ├── markdown.svg │ │ │ ├── markdown_dark.svg │ │ │ ├── copy.svg │ │ │ ├── copy_dark.svg │ │ │ ├── export.svg │ │ │ ├── export_dark.svg │ │ │ ├── httpAPI.svg │ │ │ ├── download.svg │ │ │ ├── download_dark.svg │ │ │ ├── upload.svg │ │ │ ├── upload_dark.svg │ │ │ ├── httpAPI_dark.svg │ │ │ ├── pin.svg │ │ │ ├── pin_dark.svg │ │ │ ├── clear.svg │ │ │ ├── clear_dark.svg │ │ │ ├── json.svg │ │ │ ├── json_dark.svg │ │ │ ├── settings.svg │ │ │ └── settings_dark.svg │ │ └── META-INF │ │ │ └── pluginIcon.svg │ └── java │ │ ├── com │ │ └── liuzhihang │ │ │ └── doc │ │ │ └── view │ │ │ ├── listener │ │ │ └── DocViewServiceListener.java │ │ │ ├── constant │ │ │ ├── LombokConstant.java │ │ │ ├── Constant.java │ │ │ ├── JsonPropertyConstant.java │ │ │ ├── HeaderConstant.java │ │ │ ├── MethodConstant.java │ │ │ ├── DubboConstant.java │ │ │ ├── SwaggerConstant.java │ │ │ ├── FieldTypeConstant.java │ │ │ └── ValidationConstant.java │ │ │ ├── integration │ │ │ ├── dto │ │ │ │ ├── YApiModified.java │ │ │ │ ├── YApiParam.java │ │ │ │ ├── YApiResponse.java │ │ │ │ ├── YApiQuery.java │ │ │ │ ├── YApiBodyForm.java │ │ │ │ ├── YApiHeader.java │ │ │ │ ├── YuQueCreate.java │ │ │ │ ├── YuQueUpdate.java │ │ │ │ ├── YApiCat.java │ │ │ │ ├── ShowDocUpdateResponse.java │ │ │ │ ├── ShowDocUpdateRequest.java │ │ │ │ └── YapiSave.java │ │ │ ├── ShowDocFacadeService.java │ │ │ ├── YApiFacadeService.java │ │ │ ├── YuQueFacadeService.java │ │ │ └── impl │ │ │ │ ├── ShowDocFacadeServiceImpl.java │ │ │ │ ├── YuQueFacadeServiceImpl.java │ │ │ │ └── YApiFacadeServiceImpl.java │ │ │ ├── enums │ │ │ ├── FrameworkEnum.java │ │ │ └── ContentTypeEnum.java │ │ │ ├── action │ │ │ ├── toolbar │ │ │ │ ├── AbstractToolbarUploadAction.java │ │ │ │ ├── window │ │ │ │ │ ├── WindowSettingsAction.java │ │ │ │ │ ├── WindowClearAction.java │ │ │ │ │ ├── catalog │ │ │ │ │ │ ├── CatalogUploadYApiAction.java │ │ │ │ │ │ ├── CatalogUploadYuQueAction.java │ │ │ │ │ │ ├── CatalogUploadShowDocAction.java │ │ │ │ │ │ ├── AbstractCatalogUploadAction.java │ │ │ │ │ │ ├── CatalogClearAction.java │ │ │ │ │ │ ├── CatalogOpenAction.java │ │ │ │ │ │ ├── CatalogHttpClientAction.java │ │ │ │ │ │ └── CatalogExportAction.java │ │ │ │ │ ├── WindowCollapseAction.java │ │ │ │ │ ├── WindowExpandAction.java │ │ │ │ │ ├── WindowRefreshAction.java │ │ │ │ │ ├── WindowUploadAction.java │ │ │ │ │ └── WindowExportAction.java │ │ │ │ └── preview │ │ │ │ │ ├── PreviewRightExportAction.java │ │ │ │ │ ├── MenuExportAllAction.java │ │ │ │ │ ├── PreviewRightCopyAction.java │ │ │ │ │ └── PreviewRightUploadAction.java │ │ │ ├── upload │ │ │ │ ├── YApiUploadAction.java │ │ │ │ ├── YuQueUploadAction.java │ │ │ │ ├── ShowDocUploadAction.java │ │ │ │ └── AbstractUploadAction.java │ │ │ ├── PreviewAction.java │ │ │ └── EditorAction.java │ │ │ ├── dom │ │ │ ├── BeansDomElement.java │ │ │ ├── DubboServiceDomElement.java │ │ │ ├── BeansDescription.java │ │ │ └── DubboDefinitionSearch.java │ │ │ ├── utils │ │ │ ├── StorageUtils.java │ │ │ ├── EditorUtils.java │ │ │ ├── SpringHeaderUtils.java │ │ │ ├── DialogUtil.java │ │ │ ├── VelocityUtils.java │ │ │ ├── FeignPsiUtil.java │ │ │ └── GsonFormatUtil.java │ │ │ ├── dto │ │ │ ├── Header.java │ │ │ ├── Param.java │ │ │ ├── Body.java │ │ │ ├── DocViewParamData.java │ │ │ └── DocView.java │ │ │ ├── ui │ │ │ ├── editor │ │ │ │ └── EditorFloatingToolbarProvider.java │ │ │ ├── window │ │ │ │ ├── DocViewToolWindowFactory.java │ │ │ │ ├── DocViewNode.java │ │ │ │ ├── RootNode.java │ │ │ │ ├── MethodNode.java │ │ │ │ ├── ClassNode.java │ │ │ │ └── ModuleNode.java │ │ │ ├── SupportForm.java │ │ │ ├── ParamDocEditor.form │ │ │ ├── Preview.form │ │ │ ├── WindowFilterForm.java │ │ │ ├── ShowDocSettingForm.java │ │ │ ├── YApiSettingForm.java │ │ │ ├── YuQueSettingForm.java │ │ │ └── TemplateSettingForm.form │ │ │ ├── provider │ │ │ └── DocViewActionProvider.java │ │ │ ├── DocViewBundle.java │ │ │ ├── exception │ │ │ └── DocViewException.java │ │ │ ├── config │ │ │ ├── YApiSettings.java │ │ │ ├── ShowDocSettings.java │ │ │ ├── ApplicationSettings.java │ │ │ ├── YuQueSettings.java │ │ │ ├── WindowSettings.java │ │ │ ├── SettingsConfigurable.java │ │ │ ├── YApiSettingsConfigurable.java │ │ │ ├── TemplateConfigurable.java │ │ │ ├── YuQueSettingsConfigurable.java │ │ │ ├── ShowDocSettingsConfigurable.java │ │ │ └── TemplateSettings.java │ │ │ ├── data │ │ │ └── DocViewDataKeys.java │ │ │ ├── notification │ │ │ └── DocViewStartupNotification.java │ │ │ └── service │ │ │ └── impl │ │ │ ├── WriterService.java │ │ │ ├── ShowDocServiceImpl.java │ │ │ └── DubboDocViewServiceImpl.java │ │ └── icons │ │ └── DocViewIcons.java └── test │ ├── java │ └── com │ │ └── liuzhihang │ │ └── doc │ │ └── view │ │ ├── DocxExportTest.java │ │ └── MainTest.java │ └── http │ └── YApiGetTest.http ├── HELP.md ├── CHANGELOG.md ├── .github └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── gradle.properties ├── parts └── description.html ├── LICENSE ├── .gitignore └── README.md /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'doc-view' 2 | 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhihang/doc-view/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/resources/image/wechat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzhihang/doc-view/HEAD/src/main/resources/image/wechat.png -------------------------------------------------------------------------------- /HELP.md: -------------------------------------------------------------------------------- 1 | ### 1. 控制台打印日志 2 | 3 | 右上角 `runlde` -> `Edit Configurations...` -> `配置 logs` 4 | 5 | 路径为: 6 | 7 | `/Users/liuzhihang/workspace/github/doc-view/build/idea-sandbox/system/log/idea.log` -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/listener/DocViewServiceListener.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.listener; 2 | 3 | /** 4 | * @author liuzhihang 5 | * @date 2022/4/4 17:30 6 | */ 7 | public class DocViewServiceListener { 8 | } 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [Unreleased] 2 | 3 | ## 1.3.11 4 | 5 | ### Added 6 | 7 | - 修改 DocViewData 类以特殊处理集合和 map 类型的参数,保证在展示时忽略 Map 和 List 的属性 8 | 9 | ### Changed 10 | 11 | ### Deprecated 12 | 13 | ### Removed 14 | 15 | ### Fixed 16 | 17 | ### Security -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **希望增加什么功能** 11 | 12 | 13 | **该功能的使用场景** 14 | 15 | 16 | **建议或实现方式** 17 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Bug 描述** 11 | 简要描述 Bug 信息 12 | 13 | **Version** 14 | - Doc View: [e.g. v1.0.0] 15 | - IDEA [e.g. 2021.1] 16 | 17 | **Bug 详情** 18 | 异常日志、可能导致的原因、参数信息等 19 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/constant/LombokConstant.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.constant; 2 | 3 | /** 4 | * @author liuzhihang 5 | * @date 2021/6/11 16:03 6 | */ 7 | public final class LombokConstant { 8 | private LombokConstant() { 9 | } 10 | 11 | 12 | public static final String NON_NULL = "lombok.NonNull"; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/integration/dto/YApiModified.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.integration.dto; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author liuzhihang 7 | * @date 2021/6/8 23:52 8 | */ 9 | @Data 10 | public class YApiModified { 11 | 12 | private Long ok; 13 | private Long nModified; 14 | private Long n; 15 | } 16 | -------------------------------------------------------------------------------- /src/test/java/com/liuzhihang/doc/view/DocxExportTest.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view; 2 | 3 | import com.intellij.execution.ExecutionException; 4 | 5 | /** 6 | * @author liuzhihang 7 | * @date 2021/8/5 16:27 8 | */ 9 | public class DocxExportTest { 10 | 11 | public static void main(String[] args) throws ExecutionException { 12 | 13 | 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/integration/dto/YApiParam.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.integration.dto; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author liuzhihang 7 | * @date 2021/6/8 22:58 8 | */ 9 | @Data 10 | public class YApiParam { 11 | 12 | private String name; 13 | 14 | 15 | private String example; 16 | 17 | private String desc; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/test/java/com/liuzhihang/doc/view/MainTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Ant Group 3 | * Copyright (c) 2004-2022 All Rights Reserved. 4 | */ 5 | package com.liuzhihang.doc.view; 6 | 7 | /** 8 | * @author liuzhihang 9 | * @version MainTest.java, v 0.1 2022年06月08日 2:39 PM liuzhihang 10 | */ 11 | public class MainTest { 12 | 13 | public static void main(String[] args) { 14 | 15 | 16 | } 17 | } -------------------------------------------------------------------------------- /src/main/resources/icons/checked.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/main/resources/icons/checked_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/enums/FrameworkEnum.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.enums; 2 | 3 | /** 4 | * 框架类型 Spring Dubbo 5 | * 6 | * @author liuzhihang 7 | * @version FrameworkEnum.java, v 0.1 2022年06月17日 10:28 AM liuzhihang 8 | */ 9 | public enum FrameworkEnum { 10 | /** 11 | * Spring 12 | */ 13 | SPRING, 14 | 15 | /** 16 | * Dubbo 17 | */ 18 | DUBBO; 19 | 20 | } -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/constant/Constant.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.constant; 2 | 3 | /** 4 | * 常量 5 | * 6 | * @author liuzhihang 7 | * @date 2021/8/5 18:12 8 | */ 9 | public class Constant { 10 | private Constant() { 11 | } 12 | 13 | public static final String DOC_VIEW = "DocView"; 14 | 15 | /** 16 | * 返回 void 17 | */ 18 | public static final String RETURN_VOID = "void"; 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/integration/dto/YApiResponse.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.integration.dto; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * @author liuzhihang 9 | * @date 2021/6/8 20:03 10 | */ 11 | @Data 12 | public class YApiResponse implements Serializable { 13 | 14 | private String errmsg; 15 | private Long errcode; 16 | private T data; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/resources/icons/collapseAll.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/resources/icons/expandAll.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/resources/icons/collapseAll_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/resources/icons/expandAll_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/constant/JsonPropertyConstant.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.constant; 2 | 3 | /** 4 | * JsonProperty 配置 5 | * 6 | * @author liuzhihang 7 | * @since 2023/12/23 18:42 8 | */ 9 | public class JsonPropertyConstant { 10 | private JsonPropertyConstant() { 11 | } 12 | 13 | /** 14 | * 注解 @JsonProperty 的全路径 15 | */ 16 | public static final String JSON_PROPERTY = "com.fasterxml.jackson.annotation.JsonProperty"; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/integration/dto/YApiQuery.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.integration.dto; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author liuzhihang 7 | * @date 2021/6/8 22:58 8 | */ 9 | @Data 10 | public class YApiQuery { 11 | 12 | private String name; 13 | 14 | private String type; 15 | 16 | private String example; 17 | 18 | private String desc; 19 | 20 | /** 21 | * 枚举: 1,0 22 | */ 23 | private String required; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/integration/dto/YApiBodyForm.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.integration.dto; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author liuzhihang 7 | * @date 2021/6/8 22:58 8 | */ 9 | @Data 10 | public class YApiBodyForm { 11 | 12 | private String name; 13 | 14 | private String type; 15 | 16 | private String example; 17 | 18 | private String desc; 19 | 20 | /** 21 | * 枚举: 1,0 22 | */ 23 | private String required; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/integration/dto/YApiHeader.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.integration.dto; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author liuzhihang 7 | * @date 2021/6/8 22:58 8 | */ 9 | @Data 10 | public class YApiHeader { 11 | 12 | private String name; 13 | 14 | 15 | private String example; 16 | 17 | private String desc; 18 | private String value; 19 | 20 | /** 21 | * 枚举: 1,0 22 | */ 23 | private String required = "1"; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/integration/dto/YuQueCreate.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.integration.dto; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * 语雀创建文档 7 | *

8 | * https://www.yuque.com/yuque/developer/doc#63851c78 9 | *

10 | * 默认创建都是私密文档 11 | * 12 | * @author liuzhihang 13 | * @date 2022/4/1 22:54 14 | */ 15 | @Data 16 | public class YuQueCreate { 17 | 18 | private String title; 19 | 20 | private String slug; 21 | 22 | private String format = "markdown"; 23 | 24 | private String body; 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/integration/dto/YuQueUpdate.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.integration.dto; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * 语雀更新文档 7 | *

8 | * https://www.yuque.com/yuque/developer/doc#c2e9ee2a 9 | *

10 | * 默认创建都是私密文档 11 | * 12 | * @author liuzhihang 13 | * @date 2022/4/1 22:54 14 | */ 15 | @Data 16 | public class YuQueUpdate { 17 | 18 | private String title; 19 | 20 | private String slug; 21 | 22 | private String body; 23 | 24 | private Integer _force_asl = 1; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/action/toolbar/AbstractToolbarUploadAction.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.action.toolbar; 2 | 3 | import com.liuzhihang.doc.view.action.upload.AbstractUploadAction; 4 | import com.liuzhihang.doc.view.service.DocViewUploadService; 5 | 6 | /** 7 | * @author liuzhihang 8 | * @date 2021/10/23 23:13 9 | */ 10 | public abstract class AbstractToolbarUploadAction extends AbstractUploadAction { 11 | 12 | @Override 13 | protected DocViewUploadService uploadService() { 14 | return null; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/constant/HeaderConstant.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.constant; 2 | 3 | /** 4 | * @author liuzhihang 5 | * @date 2020/3/6 13:27 6 | */ 7 | public final class HeaderConstant { 8 | 9 | private HeaderConstant() { 10 | } 11 | 12 | public static final String APPLICATION_JSON = "application/json"; 13 | 14 | public static final String APPLICATION_JSON_UTF8_VALUE = "application/json;charset=UTF-8"; 15 | 16 | 17 | public static final String APPLICATION_FORM = "application/x-www-form-urlencoded"; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/integration/dto/YApiCat.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.integration.dto; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import lombok.Data; 5 | 6 | /** 7 | * 分组,菜单列表 8 | * 9 | * @author liuzhihang 10 | * @date 2021/6/8 23:19 11 | */ 12 | @Data 13 | public class YApiCat { 14 | 15 | private String yapiUrl; 16 | 17 | private String token; 18 | 19 | @SerializedName("project_id") 20 | private Long projectId; 21 | 22 | @SerializedName("_id") 23 | private Long id; 24 | 25 | private String name; 26 | 27 | private String desc; 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/dom/BeansDomElement.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.dom; 2 | 3 | import com.intellij.util.xml.DomElement; 4 | import com.intellij.util.xml.SubTagList; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * 解析 xml 将其中的 beans 标签映射为 BeansDomElement 10 | *

11 | * https://jetbrains.org/intellij/sdk/docs/reference_guide/frameworks_and_external_apis/xml_dom_api.html 12 | * 13 | * @author liuzhihang 14 | * @date 2022/4/11 23:40 15 | */ 16 | public interface BeansDomElement extends DomElement { 17 | 18 | @SubTagList("dubbo:service") 19 | List getDubboServiceDomElements(); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/utils/StorageUtils.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.utils; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.project.ProjectKt; 5 | 6 | import java.nio.file.Path; 7 | import java.nio.file.Paths; 8 | import java.util.Objects; 9 | 10 | /** 11 | * @author liaozan 12 | * @since 2021/12/16 13 | */ 14 | public class StorageUtils { 15 | 16 | public static Path getConfigDir(Project project) { 17 | Path configDir = ProjectKt.getStateStore(project).getDirectoryStorePath(); 18 | return Paths.get(Objects.requireNonNull(configDir).toString(), "doc-view", "temp"); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/enums/ContentTypeEnum.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.enums; 2 | 3 | import lombok.Getter; 4 | 5 | /** 6 | * http 请求 content-type 枚举 7 | * 8 | * @author liuzhihang 9 | * @version ContentTypeEnum.java, v 0.1 2022年06月16日 8:38 PM liuzhihang 10 | */ 11 | @Getter 12 | public enum ContentTypeEnum { 13 | 14 | JSON("Content-Type", "application/json"), 15 | FORM("Content-Type", "application/x-www-form-urlencoded"), 16 | 17 | ; 18 | 19 | ContentTypeEnum(String key, String value) { 20 | this.key = key; 21 | this.value = value; 22 | } 23 | 24 | private String key; 25 | 26 | private String value; 27 | 28 | } -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/dom/DubboServiceDomElement.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.dom; 2 | 3 | import com.intellij.util.xml.Attribute; 4 | import com.intellij.util.xml.DomElement; 5 | import com.intellij.util.xml.GenericAttributeValue; 6 | import com.intellij.util.xml.NameValue; 7 | 8 | /** 9 | * xml 配置中的 标签 10 | * 11 | * @author liuzhihang 12 | * @date 2022/4/11 23:54 13 | */ 14 | public interface DubboServiceDomElement extends DomElement { 15 | 16 | /** 17 | * interface 属性, 就是接口的全路径 18 | * 19 | * @return 20 | */ 21 | @NameValue 22 | @Attribute("interface") 23 | GenericAttributeValue getInterface(); 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/dto/Header.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.dto; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import lombok.Data; 5 | 6 | /** 7 | * @author liuzhihang 8 | * @date 2020/2/28 10:40 9 | */ 10 | @Data 11 | public class Header { 12 | 13 | /** 14 | * 节点 15 | */ 16 | private PsiElement psiElement; 17 | 18 | /** 19 | * 是否必须 1必须 0非必须 20 | */ 21 | private Boolean required; 22 | /** 23 | * 参数名 24 | */ 25 | private String name; 26 | 27 | /** 28 | * 值 29 | */ 30 | private String value; 31 | 32 | /** 33 | * 参数备注 34 | */ 35 | private String desc; 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/resources/icons/clearCash.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 8 | 9 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/integration/ShowDocFacadeService.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.integration; 2 | 3 | import com.liuzhihang.doc.view.integration.dto.ShowDocUpdateRequest; 4 | import com.liuzhihang.doc.view.integration.dto.ShowDocUpdateResponse; 5 | 6 | /** 7 | * ShowDoc 文档对接 8 | *

9 | *

10 | * https://www.showdoc.com.cn/page/102098 11 | * 12 | * @author liuzhihang 13 | * @date 2021/7/27 12:43 14 | */ 15 | public interface ShowDocFacadeService { 16 | 17 | /** 18 | * 上传到 ShowDoc 19 | * 20 | * @param request 21 | * @return 22 | */ 23 | ShowDocUpdateResponse updateByApi(ShowDocUpdateRequest request) throws Exception; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/resources/icons/clearCash_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 8 | 9 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/ui/editor/EditorFloatingToolbarProvider.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.ui.editor; 2 | 3 | import com.intellij.openapi.editor.toolbar.floating.AbstractFloatingToolbarProvider; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | /** 7 | * @author liuzhihang 8 | * @version EditorWidgetActionProvider.java, v 0.1 2022/4/20 15:28 liuzhihan 9 | */ 10 | public class EditorFloatingToolbarProvider extends AbstractFloatingToolbarProvider { 11 | 12 | 13 | public EditorFloatingToolbarProvider() { 14 | super("liuzhihang.doc.preview.editor.floating"); 15 | } 16 | 17 | @Override 18 | public boolean getAutoHideable() { 19 | return true; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/resources/icons/refresh.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/resources/icons/refresh_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/resources/icons/editorPreview.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | -------------------------------------------------------------------------------- /src/main/resources/icons/editorPreview_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/action/toolbar/window/WindowSettingsAction.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.action.toolbar.window; 2 | 3 | import com.intellij.openapi.actionSystem.AnAction; 4 | import com.intellij.openapi.actionSystem.AnActionEvent; 5 | import com.intellij.openapi.options.ShowSettingsUtil; 6 | import com.liuzhihang.doc.view.config.SettingsConfigurable; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | /** 10 | * @author liuzhihang 11 | * @date 2021/10/23 19:55 12 | */ 13 | public class WindowSettingsAction extends AnAction { 14 | 15 | @Override 16 | public void actionPerformed(@NotNull AnActionEvent e) { 17 | ShowSettingsUtil.getInstance().showSettingsDialog(e.getProject(), SettingsConfigurable.class); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/resources/icons/markdown.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 8 | 9 | -------------------------------------------------------------------------------- /src/main/resources/icons/markdown_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 8 | 9 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/constant/MethodConstant.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Ant Group 3 | * Copyright (c) 2004-2022 All Rights Reserved. 4 | */ 5 | package com.liuzhihang.doc.view.constant; 6 | 7 | /** 8 | * 方法常量 9 | * 10 | * @author liuzhihang 11 | * @version MethodConstant.java, v 0.1 2022年06月17日 11:55 AM zijun.lzh 12 | */ 13 | public final class MethodConstant { 14 | 15 | public static final String GET = "GET"; 16 | public static final String POST = "POST"; 17 | public static final String PUT = "PUT"; 18 | public static final String DELETE = "DELETE"; 19 | public static final String HEAD = "HEAD"; 20 | public static final String OPTIONS = "OPTIONS"; 21 | public static final String PATCH = "PATCH"; 22 | public static final String DUBBO = "DUBBO"; 23 | 24 | } -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/action/toolbar/window/WindowClearAction.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.action.toolbar.window; 2 | 3 | import com.intellij.openapi.actionSystem.AnAction; 4 | import com.intellij.openapi.actionSystem.AnActionEvent; 5 | import com.intellij.openapi.project.Project; 6 | import com.liuzhihang.doc.view.utils.CustomFileUtils; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | /** 10 | * @author liuzhihang 11 | * @date 2021/10/23 19:55 12 | */ 13 | public class WindowClearAction extends AnAction { 14 | 15 | @Override 16 | public void actionPerformed(@NotNull AnActionEvent e) { 17 | Project project = e.getProject(); 18 | if (project == null) { 19 | return; 20 | } 21 | CustomFileUtils.delete(project, "Doc View"); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/constant/DubboConstant.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.constant; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | /** 7 | * dubbo 相关常量 8 | * 9 | * @author liuzhihang 10 | * @date 2022/4/4 20:16 11 | */ 12 | public class DubboConstant { 13 | 14 | /** 15 | * 包含类注解名称 16 | */ 17 | public static Set SERVICE_ANNOTATIONS = new HashSet<>() {{ 18 | add(DUBBO_SERVICE); 19 | add(SERVICE_1); 20 | add(SERVICE_2); 21 | }}; 22 | 23 | public static final String DUBBO_SERVICE = "org.apache.dubbo.config.annotation.DubboService"; 24 | public static final String SERVICE_1 = "org.apache.dubbo.config.annotation.Service"; 25 | public static final String SERVICE_2 = "com.alibaba.dubbo.config.annotation.Service"; 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/provider/DocViewActionProvider.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.provider; 2 | 3 | /** 4 | * 编辑窗口,右上角 5 | * 6 | * @author liuzhihang 7 | * @since 2023/5/22 23:10 8 | */ 9 | // public class DocViewActionProvider implements InspectionWidgetActionProvider { 10 | // @Nullable 11 | // @Override 12 | // public AnAction createAction(@NotNull Editor editor) { 13 | // 14 | // Project project = editor.getProject(); 15 | // if (project == null || project.isDefault()) { 16 | // return null; 17 | // } 18 | // 19 | // return new AnAction("Refresh", "Refresh", AllIcons.Actions.Refresh) { 20 | // @Override 21 | // public void actionPerformed(@NotNull AnActionEvent e) { 22 | // 23 | // } 24 | // }; 25 | // } 26 | // } 27 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Opt-out flag for bundling Kotlin standard library -> https://jb.gg/intellij-platform-kotlin-stdlib 2 | kotlin.stdlib.default.dependency=false 3 | # Enable Gradle Configuration Cache -> https://docs.gradle.org/current/userguide/configuration_cache.html 4 | org.gradle.configuration-cache=true 5 | # Enable Gradle Build Cache -> https://docs.gradle.org/current/userguide/build_cache.html 6 | org.gradle.caching=true 7 | # ???? 8 | pluginGroup=com.liuzhihang.toolkit 9 | pluginName=Doc View 10 | pluginVersion=1.3.11 11 | pluginSinceBuild=241 12 | pluginUntilBuild= 13 | pluginDescription=parts/description.html 14 | platformType=IU 15 | platformVersion=2024.1 16 | # ,-Dide.browser.jcef.log.level=verbose,-Duser.language=en-US 17 | runIdeJvmArgs=-Dfile.encoding=utf-8 18 | # Gradle Releases -> https://github.com/gradle/gradle/releases 19 | gradleVersion=8.5 -------------------------------------------------------------------------------- /src/main/resources/icons/copy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /src/main/resources/icons/copy_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/DocViewBundle.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. 2 | package com.liuzhihang.doc.view; 3 | 4 | import com.intellij.DynamicBundle; 5 | import org.jetbrains.annotations.NonNls; 6 | import org.jetbrains.annotations.NotNull; 7 | import org.jetbrains.annotations.PropertyKey; 8 | 9 | public final class DocViewBundle extends DynamicBundle { 10 | 11 | @NonNls 12 | private static final String BUNDLE = "messages.DocViewBundle"; 13 | private static final DocViewBundle INSTANCE = new DocViewBundle(); 14 | 15 | private DocViewBundle() { 16 | super(BUNDLE); 17 | } 18 | 19 | @NotNull 20 | public static String message(@NotNull @PropertyKey(resourceBundle = BUNDLE) String key, Object... params) { 21 | return INSTANCE.getMessage(key, params); 22 | } 23 | } -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/dto/Param.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.dto; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import lombok.Data; 5 | 6 | /** 7 | * @author liuzhihang 8 | * @date 2020/2/27 16:39 9 | */ 10 | @Data 11 | public class Param { 12 | 13 | 14 | /** 15 | * 参数的 psiElement 16 | */ 17 | private PsiElement psiElement; 18 | 19 | /** 20 | * 是否必须 21 | */ 22 | private Boolean required; 23 | /** 24 | * 参数名 25 | */ 26 | private String name; 27 | /** 28 | * 参数示例 29 | */ 30 | private String example; 31 | 32 | /** 33 | * 参数描述 34 | */ 35 | private String desc; 36 | 37 | /** 38 | * 类型 39 | */ 40 | private String type; 41 | 42 | 43 | /** 44 | * since 45 | */ 46 | private String since; 47 | 48 | /** 49 | * version 50 | */ 51 | private String version; 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/action/toolbar/window/catalog/CatalogUploadYApiAction.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.action.toolbar.window.catalog; 2 | 3 | import com.intellij.openapi.actionSystem.AnActionEvent; 4 | import com.intellij.openapi.application.ApplicationManager; 5 | import com.liuzhihang.doc.view.service.DocViewUploadService; 6 | import com.liuzhihang.doc.view.service.impl.YApiServiceImpl; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | /** 10 | * @author liuzhihang 11 | * @date 2021/10/23 19:55 12 | */ 13 | public class CatalogUploadYApiAction extends AbstractCatalogUploadAction { 14 | 15 | @Override 16 | public void actionPerformed(@NotNull AnActionEvent e) { 17 | super.actionPerformed(e); 18 | } 19 | 20 | @Override 21 | protected DocViewUploadService uploadService() { 22 | return ApplicationManager.getApplication().getService(YApiServiceImpl.class); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/resources/icons/export.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/action/toolbar/window/catalog/CatalogUploadYuQueAction.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.action.toolbar.window.catalog; 2 | 3 | import com.intellij.openapi.actionSystem.AnActionEvent; 4 | import com.intellij.openapi.application.ApplicationManager; 5 | import com.liuzhihang.doc.view.service.DocViewUploadService; 6 | import com.liuzhihang.doc.view.service.impl.YuQueServiceImpl; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | /** 10 | * @author liuzhihang 11 | * @date 2021/10/23 19:55 12 | */ 13 | public class CatalogUploadYuQueAction extends AbstractCatalogUploadAction { 14 | 15 | @Override 16 | public void actionPerformed(@NotNull AnActionEvent e) { 17 | super.actionPerformed(e); 18 | } 19 | 20 | @Override 21 | protected DocViewUploadService uploadService() { 22 | return ApplicationManager.getApplication().getService(YuQueServiceImpl.class); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/resources/icons/export_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/action/toolbar/window/catalog/CatalogUploadShowDocAction.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.action.toolbar.window.catalog; 2 | 3 | import com.intellij.openapi.actionSystem.AnActionEvent; 4 | import com.intellij.openapi.application.ApplicationManager; 5 | import com.liuzhihang.doc.view.service.DocViewUploadService; 6 | import com.liuzhihang.doc.view.service.impl.ShowDocServiceImpl; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | /** 10 | * @author liuzhihang 11 | * @date 2021/10/23 19:55 12 | */ 13 | public class CatalogUploadShowDocAction extends AbstractCatalogUploadAction { 14 | 15 | @Override 16 | public void actionPerformed(@NotNull AnActionEvent e) { 17 | super.actionPerformed(e); 18 | } 19 | 20 | @Override 21 | protected DocViewUploadService uploadService() { 22 | return ApplicationManager.getApplication().getService(ShowDocServiceImpl.class); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/resources/icons/httpAPI.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/exception/DocViewException.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.exception; 2 | 3 | /** 4 | * DocView 异常类 5 | * 6 | * @author liuzhihang 7 | * @since 2023/8/5 17:13 8 | */ 9 | public class DocViewException extends RuntimeException { 10 | 11 | /** 12 | * 空参构造 13 | */ 14 | public DocViewException() { 15 | } 16 | 17 | /** 18 | * 消息构造器 19 | * 20 | * @param message 消息 21 | */ 22 | public DocViewException(String message) { 23 | super(message); 24 | } 25 | 26 | /** 27 | * 堆栈 消息构造 28 | * 29 | * @param message 消息 30 | * @param cause 堆栈 31 | */ 32 | public DocViewException(String message, Throwable cause) { 33 | super(message, cause); 34 | } 35 | 36 | /** 37 | * 堆栈 38 | * 39 | * @param cause 堆栈 40 | */ 41 | public DocViewException(Throwable cause) { 42 | super(cause); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/resources/icons/download.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/main/resources/icons/download_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/ui/window/DocViewToolWindowFactory.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.ui.window; 2 | 3 | import com.intellij.openapi.project.DumbAware; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.openapi.wm.ToolWindow; 6 | import com.intellij.openapi.wm.ToolWindowFactory; 7 | import com.intellij.ui.content.Content; 8 | import com.intellij.ui.content.ContentFactory; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | /** 12 | * @author liuzhihang 13 | * @date 2021/10/22 16:10 14 | */ 15 | public class DocViewToolWindowFactory implements ToolWindowFactory, DumbAware { 16 | @Override 17 | public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) { 18 | 19 | ContentFactory contentFactory = ContentFactory.getInstance(); 20 | Content content = contentFactory.createContent(new DocViewWindowPanel(project, toolWindow), "", false); 21 | toolWindow.getContentManager().addContent(content); 22 | 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/action/upload/YApiUploadAction.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.action.upload; 2 | 3 | import com.intellij.openapi.actionSystem.AnActionEvent; 4 | import com.intellij.openapi.application.ApplicationManager; 5 | import com.liuzhihang.doc.view.service.DocViewUploadService; 6 | import com.liuzhihang.doc.view.service.impl.YApiServiceImpl; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | /** 10 | * YApi 上传 11 | * 12 | * @author liuzhihang 13 | * @date 2021/6/8 16:40 14 | */ 15 | public class YApiUploadAction extends AbstractUploadAction { 16 | 17 | @Override 18 | public void actionPerformed(@NotNull AnActionEvent e) { 19 | super.actionPerformed(e); 20 | } 21 | 22 | @Override 23 | protected DocViewUploadService uploadService() { 24 | return ApplicationManager.getApplication().getService(YApiServiceImpl.class); 25 | } 26 | 27 | @Override 28 | public void update(@NotNull AnActionEvent e) { 29 | super.update(e); 30 | } 31 | 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/resources/icons/upload.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/main/resources/icons/upload_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/utils/EditorUtils.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.utils; 2 | 3 | import com.intellij.lang.Language; 4 | import com.intellij.openapi.editor.EditorSettings; 5 | import com.intellij.openapi.editor.ex.EditorEx; 6 | 7 | /** 8 | * 编辑器工具 9 | * 10 | * @author liuzhihang 11 | * @since 2023/8/5 18:56 12 | */ 13 | public class EditorUtils { 14 | 15 | /** 16 | * 对 markdown 编辑器进行渲染 17 | * 18 | * @param editor 编辑器 19 | */ 20 | public static void renderMarkdownEditor(EditorEx editor) { 21 | 22 | EditorSettings editorSettings = editor.getSettings(); 23 | editorSettings.setAdditionalLinesCount(0); 24 | editorSettings.setAdditionalColumnsCount(0); 25 | editorSettings.setLineMarkerAreaShown(false); 26 | editorSettings.setLineNumbersShown(false); 27 | editorSettings.setVirtualSpace(false); 28 | editorSettings.setFoldingOutlineShown(false); 29 | 30 | editorSettings.setLanguageSupplier(() -> Language.findLanguageByID("Markdown")); 31 | 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/integration/YApiFacadeService.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.integration; 2 | 3 | import com.liuzhihang.doc.view.integration.dto.YApiCat; 4 | import com.liuzhihang.doc.view.integration.dto.YapiSave; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * YApi 包装 service 10 | *

11 | * https://hellosean1025.github.io/yapi/openapi.html 12 | * 13 | * @author liuzhihang 14 | * @date 2021/6/8 19:20 15 | */ 16 | public interface YApiFacadeService { 17 | 18 | /** 19 | * 新增接口 20 | * 21 | * @param dto 22 | */ 23 | void save(YapiSave dto) throws Exception; 24 | 25 | /** 26 | * 获取菜单列表 27 | * 28 | * @param yapiUrl 29 | * @param projectId 30 | * @param token 31 | * @return 32 | * @throws Exception 33 | */ 34 | List getCatMenu(String yapiUrl, Long projectId, String token) throws Exception; 35 | 36 | /** 37 | * 添加菜单 38 | * 39 | * @param cat 40 | * @return 41 | * @throws Exception 42 | */ 43 | YApiCat addCat(YApiCat cat) throws Exception; 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/action/PreviewAction.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.action; 2 | 3 | import com.intellij.openapi.actionSystem.ActionUpdateThread; 4 | import com.intellij.openapi.actionSystem.AnAction; 5 | import com.intellij.openapi.actionSystem.AnActionEvent; 6 | import com.liuzhihang.doc.view.ui.PreviewForm; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | /** 10 | * 单个类或者方法中操作 11 | * 12 | * @author liuzhihang 13 | * @date 2020/2/26 21:57 14 | */ 15 | public class PreviewAction extends AbstractAction { 16 | 17 | /** 18 | * @see AnAction#actionPerformed(AnActionEvent) 19 | */ 20 | @Override 21 | public void actionPerformed(AnActionEvent e) { 22 | // 先执行抽象类逻辑 23 | super.actionPerformed(e); 24 | 25 | // 预览文档 26 | PreviewForm.getInstance(targetClass, targetMethod).popup(); 27 | } 28 | 29 | /** 30 | * @see AnAction#update(AnActionEvent) 31 | */ 32 | @Override 33 | public @NotNull ActionUpdateThread getActionUpdateThread() { 34 | return ActionUpdateThread.BGT; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/dom/BeansDescription.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.dom; 2 | 3 | import com.intellij.openapi.module.Module; 4 | import com.intellij.psi.xml.XmlFile; 5 | import com.intellij.psi.xml.XmlTag; 6 | import com.intellij.util.xml.DomFileDescription; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | /** 11 | * @author liuzhihang 12 | * @link https://jetbrains.org/intellij/sdk/docs/reference_guide/frameworks_and_external_apis/xml_dom_api.html 13 | * @date 2022-04-11 23:49:37 14 | */ 15 | public class BeansDescription extends DomFileDescription { 16 | 17 | public BeansDescription() { 18 | super(BeansDomElement.class, "beans"); 19 | } 20 | 21 | @Override 22 | public boolean isMyFile(@NotNull XmlFile file, @Nullable Module module) { 23 | XmlTag rootTag = file.getRootTag(); 24 | return rootTag != null && rootTag.getName().equals(getRootTagName()); 25 | } 26 | 27 | @Override 28 | public boolean acceptsOtherRootTagNames() { 29 | return true; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/action/upload/YuQueUploadAction.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.action.upload; 2 | 3 | import com.intellij.openapi.actionSystem.AnActionEvent; 4 | import com.intellij.openapi.application.ApplicationManager; 5 | import com.liuzhihang.doc.view.service.DocViewUploadService; 6 | import com.liuzhihang.doc.view.service.impl.YuQueServiceImpl; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | /** 10 | * 语雀 上传 11 | * 12 | * @author liuzhihang 13 | * @date 2021/6/8 16:40 14 | */ 15 | public class YuQueUploadAction extends AbstractUploadAction { 16 | 17 | @Override 18 | public void actionPerformed(@NotNull AnActionEvent e) { 19 | super.actionPerformed(e); 20 | } 21 | 22 | @Override 23 | protected DocViewUploadService uploadService() { 24 | return ApplicationManager.getApplication().getService(YuQueServiceImpl.class); 25 | } 26 | 27 | /** 28 | * 设置右键菜单是否隐藏 上传按钮 29 | * 30 | * @param e 31 | */ 32 | @Override 33 | public void update(@NotNull AnActionEvent e) { 34 | super.update(e); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/action/upload/ShowDocUploadAction.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.action.upload; 2 | 3 | import com.intellij.openapi.actionSystem.AnActionEvent; 4 | import com.intellij.openapi.application.ApplicationManager; 5 | import com.liuzhihang.doc.view.service.DocViewUploadService; 6 | import com.liuzhihang.doc.view.service.impl.ShowDocServiceImpl; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | /** 10 | * YApi 上传 11 | * 12 | * @author liuzhihang 13 | * @date 2021/6/8 16:40 14 | */ 15 | public class ShowDocUploadAction extends AbstractUploadAction { 16 | 17 | @Override 18 | public void actionPerformed(@NotNull AnActionEvent e) { 19 | super.actionPerformed(e); 20 | } 21 | 22 | @Override 23 | protected DocViewUploadService uploadService() { 24 | return ApplicationManager.getApplication().getService(ShowDocServiceImpl.class); 25 | } 26 | 27 | /** 28 | * 设置右键菜单是否隐藏 上传按钮 29 | * 30 | * @param e 31 | */ 32 | @Override 33 | public void update(@NotNull AnActionEvent e) { 34 | super.update(e); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/pluginIcon.svg: -------------------------------------------------------------------------------- 1 | 3 | 5 | 6 | 7 | 8 | 10 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/utils/SpringHeaderUtils.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.utils; 2 | 3 | import com.liuzhihang.doc.view.constant.HeaderConstant; 4 | import com.liuzhihang.doc.view.dto.Header; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | /** 8 | * @author liuzhihang 9 | * @date 2020/3/6 13:24 10 | */ 11 | public class SpringHeaderUtils { 12 | 13 | @NotNull 14 | public static Header buildJsonHeader() { 15 | 16 | Header header = new Header(); 17 | header.setRequired(true); 18 | header.setName("Content-Type"); 19 | header.setValue(HeaderConstant.APPLICATION_JSON); 20 | header.setDesc(HeaderConstant.APPLICATION_JSON); 21 | 22 | 23 | return header; 24 | } 25 | 26 | @NotNull 27 | public static Header buildFormHeader() { 28 | 29 | Header header = new Header(); 30 | header.setRequired(true); 31 | header.setName("Content-Type"); 32 | header.setValue(HeaderConstant.APPLICATION_FORM); 33 | header.setDesc(HeaderConstant.APPLICATION_FORM); 34 | 35 | 36 | return header; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /parts/description.html: -------------------------------------------------------------------------------- 1 | 2 |

Generate Markdown documents based on the interface

3 |
4 | GitHub | 5 | Issues | 6 | Website | 7 | LICENSE 8 |
9 |
10 | 11 | 12 | English introduction 13 |
14 |
15 | 16 | The Markdown document is generated based on the interface and can be previewed, edited, and uploaded to YApi or ShowDoc. 17 |
18 |
19 | 20 | 23 |
24 | 25 | 26 | 中文介绍 27 |
28 |
29 | 30 | 基于接口生成 Markdown 文档, 支持预览、编辑以及上传到YApi、ShowDoc、语雀。 31 |
32 |
33 | 36 |
37 | 38 | Other 39 |
40 |
41 | 46 |
-------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 liuzhihang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/action/toolbar/window/WindowCollapseAction.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.action.toolbar.window; 2 | 3 | import com.intellij.openapi.actionSystem.AnAction; 4 | import com.intellij.openapi.actionSystem.AnActionEvent; 5 | import com.intellij.openapi.actionSystem.PlatformDataKeys; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.ui.TreeExpandCollapse; 8 | import com.intellij.ui.treeStructure.SimpleTree; 9 | import com.liuzhihang.doc.view.data.DocViewDataKeys; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | /** 13 | * @author liuzhihang 14 | * @date 2021/10/23 19:55 15 | */ 16 | public class WindowCollapseAction extends AnAction { 17 | 18 | @Override 19 | public void actionPerformed(@NotNull AnActionEvent e) { 20 | // 获取当前project对象 21 | Project project = e.getData(PlatformDataKeys.PROJECT); 22 | SimpleTree catalogTree = e.getData(DocViewDataKeys.WINDOW_CATALOG_TREE); 23 | 24 | if (catalogTree == null || project == null) { 25 | return; 26 | } 27 | TreeExpandCollapse.collapse(catalogTree); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/action/toolbar/window/WindowExpandAction.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.action.toolbar.window; 2 | 3 | import com.intellij.openapi.actionSystem.AnAction; 4 | import com.intellij.openapi.actionSystem.AnActionEvent; 5 | import com.intellij.openapi.actionSystem.PlatformDataKeys; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.ui.TreeExpandCollapse; 8 | import com.intellij.ui.treeStructure.SimpleTree; 9 | import com.liuzhihang.doc.view.data.DocViewDataKeys; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | /** 13 | * @author liuzhihang 14 | * @date 2021/10/23 19:55 15 | */ 16 | public class WindowExpandAction extends AnAction { 17 | 18 | @Override 19 | public void actionPerformed(@NotNull AnActionEvent e) { 20 | // 获取当前project对象 21 | Project project = e.getData(PlatformDataKeys.PROJECT); 22 | SimpleTree catalogTree = e.getData(DocViewDataKeys.WINDOW_CATALOG_TREE); 23 | 24 | if (catalogTree == null || project == null) { 25 | return; 26 | } 27 | TreeExpandCollapse.expandAll(catalogTree); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/utils/DialogUtil.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.utils; 2 | 3 | import com.intellij.openapi.ui.DialogWrapper; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | import javax.swing.*; 7 | import java.awt.*; 8 | 9 | /** 10 | * 弹出式提示工具类 11 | * 12 | * @author lvgorice@gmail.com 13 | * @version 1.0 14 | * @date 2020-11-20 15 | */ 16 | public class DialogUtil extends DialogWrapper { 17 | 18 | private final String message; 19 | 20 | private DialogUtil(String title, String message) { 21 | super(true); // use current window as parent 22 | this.message = message; 23 | init(); 24 | setTitle(title); 25 | } 26 | 27 | @Nullable 28 | @Override 29 | protected JComponent createCenterPanel() { 30 | JPanel dialogPanel = new JPanel(new BorderLayout()); 31 | 32 | JLabel label = new JLabel(message); 33 | dialogPanel.add(label, BorderLayout.CENTER); 34 | 35 | return dialogPanel; 36 | } 37 | 38 | public static boolean confirm(String title, String message) { 39 | return new DialogUtil(title, message).showAndGet(); 40 | } 41 | } -------------------------------------------------------------------------------- /src/main/resources/icons/httpAPI_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/constant/SwaggerConstant.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.constant; 2 | 3 | /** 4 | * @author liuzhihang 5 | * @date 2021/6/10 19:01 6 | */ 7 | public final class SwaggerConstant { 8 | 9 | private SwaggerConstant() { 10 | } 11 | 12 | /** 13 | * Swagger 2.x 配置 14 | */ 15 | public static final String API_MODEL = "io.swagger.annotations.ApiModel"; 16 | public static final String API_MODEL_PROPERTY = "io.swagger.annotations.ApiModelProperty"; 17 | public static final String API = "io.swagger.annotations.Api"; 18 | public static final String API_OPERATION = "io.swagger.annotations.ApiOperation"; 19 | public static final String API_PARAM = "io.swagger.annotations.ApiParam"; 20 | 21 | 22 | /** 23 | * Swagger 3 配置 24 | */ 25 | public static final String TAG = "io.swagger.v3.oas.annotations.tags.Tag"; 26 | public static final String OPERATION = "io.swagger.v3.oas.annotations.Operation"; 27 | public static final String PARAMETER = "io.swagger.v3.oas.annotations.Parameter"; 28 | public static final String PARAMETERS = "io.swagger.v3.oas.annotations.Parameters"; 29 | public static final String SCHEMA = "io.swagger.v3.oas.annotations.media.Schema"; 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/ui/window/DocViewNode.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.ui.window; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.ui.treeStructure.CachingSimpleNode; 5 | import com.intellij.ui.treeStructure.SimpleNode; 6 | import com.liuzhihang.doc.view.dto.DocView; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * @author liuzhihang 12 | * @date 2022/4/5 16:05 13 | */ 14 | public abstract class DocViewNode extends CachingSimpleNode { 15 | 16 | protected DocViewNode(SimpleNode parentNode) { 17 | super(parentNode); 18 | 19 | } 20 | 21 | @Override 22 | protected abstract SimpleNode[] buildChildren(); 23 | 24 | @Override 25 | public String getName() { 26 | return "Doc View"; 27 | } 28 | 29 | public abstract List docViewList(); 30 | 31 | public abstract void updateNode(Project project); 32 | 33 | /** 34 | * 生成的文档保存路径 35 | * 36 | * @param project project 37 | * @return path 38 | */ 39 | public abstract String docPath(Project project); 40 | 41 | /** 42 | * 生成的 .http 文件路径 43 | * 44 | * @param project project 45 | * @return path 46 | */ 47 | public abstract String httpPath(Project project); 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/resources/icons/pin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/resources/icons/pin_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/config/YApiSettings.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.config; 2 | 3 | import com.intellij.openapi.components.PersistentStateComponent; 4 | import com.intellij.openapi.components.State; 5 | import com.intellij.openapi.components.Storage; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.util.xmlb.XmlSerializerUtil; 8 | import lombok.Data; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | /** 13 | * YApi 的相关设置 14 | * 15 | * @author liuzhihang 16 | * @date 2020/11/22 13:51 17 | */ 18 | @Data 19 | @State(name = "DocViewYApiSettingsComment", storages = {@Storage("DocViewYApiSettings.xml")}) 20 | public class YApiSettings implements PersistentStateComponent { 21 | 22 | private String url; 23 | 24 | private Long projectId; 25 | 26 | private String token; 27 | 28 | public static YApiSettings getInstance(@NotNull Project project) { 29 | return project.getService(YApiSettings.class); 30 | } 31 | 32 | @Override 33 | public @Nullable 34 | YApiSettings getState() { 35 | return this; 36 | } 37 | 38 | @Override 39 | public void loadState(@NotNull YApiSettings state) { 40 | 41 | XmlSerializerUtil.copyBean(state, this); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/integration/dto/ShowDocUpdateResponse.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.integration.dto; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import lombok.Data; 5 | 6 | /** 7 | * @author liuzhihang 8 | * @date 2021/7/27 11:45 9 | */ 10 | @Data 11 | public class ShowDocUpdateResponse { 12 | 13 | @SerializedName("error_code") 14 | private String errorCode; 15 | private DataInner data; 16 | 17 | @Data 18 | public static class DataInner { 19 | 20 | @SerializedName("s_number") 21 | private String sNumber; 22 | 23 | @SerializedName("page_title") 24 | private String pageTitle; 25 | 26 | @SerializedName("page_id") 27 | private String pageId; 28 | 29 | @SerializedName("page_content") 30 | private String pageContent; 31 | 32 | @SerializedName("page_comments") 33 | private String pageComments; 34 | 35 | @SerializedName("item_id") 36 | private String itemId; 37 | 38 | @SerializedName("cat_id") 39 | private String catId; 40 | 41 | @SerializedName("author_username") 42 | private String authorUsername; 43 | 44 | @SerializedName("author_uid") 45 | private String authorUid; 46 | 47 | private String addtime; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/config/ShowDocSettings.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.config; 2 | 3 | import com.intellij.openapi.components.PersistentStateComponent; 4 | import com.intellij.openapi.components.State; 5 | import com.intellij.openapi.components.Storage; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.util.xmlb.XmlSerializerUtil; 8 | import lombok.Data; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | /** 13 | * ShowDoc 的相关设置 14 | * 15 | * @author liuzhihang 16 | * @date 2020/11/22 13:51 17 | */ 18 | @Data 19 | @State(name = "DocViewShowDocSettingsComment", storages = {@Storage("DocViewShowDocSettings.xml")}) 20 | public class ShowDocSettings implements PersistentStateComponent { 21 | 22 | private String url; 23 | 24 | private String apiKey; 25 | 26 | private String apiToken; 27 | 28 | 29 | public static ShowDocSettings getInstance(@NotNull Project project) { 30 | return project.getService(ShowDocSettings.class); 31 | } 32 | 33 | @Override 34 | public @Nullable 35 | ShowDocSettings getState() { 36 | return this; 37 | } 38 | 39 | @Override 40 | public void loadState(@NotNull ShowDocSettings state) { 41 | 42 | XmlSerializerUtil.copyBean(state, this); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/integration/dto/ShowDocUpdateRequest.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.integration.dto; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import lombok.Data; 5 | 6 | /** 7 | * @author liuzhihang 8 | * @date 2021/7/27 11:45 9 | */ 10 | @Data 11 | public class ShowDocUpdateRequest { 12 | 13 | /** 14 | * 请求地址 15 | */ 16 | private String showDocUrl; 17 | 18 | /** 19 | * api_key,认证凭证。登录showdoc,进入具体项目后,点击右上角的”项目设置”-“开放API”便可看到 20 | */ 21 | @SerializedName("api_key") 22 | private String apiKey; 23 | 24 | /** 25 | * 同上 26 | */ 27 | @SerializedName("api_token") 28 | private String apiToken; 29 | 30 | /** 31 | * 可选参数。当页面文档处于目录下时,请传递目录名。当目录名不存在时,showdoc会自动创建此目录。 32 | * 需要创建多层目录的时候请用斜杆隔开,例如 “一层/二层/三层” 33 | */ 34 | @SerializedName("cat_name") 35 | private String catName; 36 | 37 | /** 38 | * 页面标题。请保证其唯一。 39 | * (或者,当页面处于目录下时,请保证页面标题在该目录下唯一)。 40 | * 当页面标题不存在时,showdoc将会创建此页面。当页面标题存在时,将用page_content更新其内容 41 | */ 42 | @SerializedName("page_title") 43 | private String pageTitle; 44 | 45 | /** 46 | * 页面内容,可传递markdown格式的文本或者html源码 47 | */ 48 | @SerializedName("page_content") 49 | private String pageContent; 50 | 51 | /** 52 | * 可选,页面序号。默认是99。数字越小,该页面越靠前 53 | */ 54 | @SerializedName("s_number") 55 | private Long sNumber; 56 | 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/action/upload/AbstractUploadAction.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.action.upload; 2 | 3 | import com.intellij.openapi.actionSystem.ActionUpdateThread; 4 | import com.intellij.openapi.actionSystem.AnAction; 5 | import com.intellij.openapi.actionSystem.AnActionEvent; 6 | import com.liuzhihang.doc.view.action.AbstractAction; 7 | import com.liuzhihang.doc.view.dto.DocView; 8 | import com.liuzhihang.doc.view.service.DocViewService; 9 | import com.liuzhihang.doc.view.service.DocViewUploadService; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * @author liuzhihang 16 | * @date 2021/10/23 23:13 17 | */ 18 | public abstract class AbstractUploadAction extends AbstractAction { 19 | 20 | /** 21 | * @see AnAction#actionPerformed(AnActionEvent) 22 | */ 23 | public void actionPerformed(@NotNull AnActionEvent e) { 24 | // 先执行抽象类逻辑 25 | super.actionPerformed(e); 26 | 27 | List docViewList = DocViewService.getInstance(project, targetClass).buildDoc(targetClass, targetMethod); 28 | 29 | // 上传 30 | uploadService().upload(project, docViewList); 31 | 32 | } 33 | 34 | /** 35 | * 具体上传服务 36 | */ 37 | protected abstract DocViewUploadService uploadService(); 38 | 39 | @Override 40 | public @NotNull ActionUpdateThread getActionUpdateThread() { 41 | return ActionUpdateThread.BGT; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/action/toolbar/window/catalog/AbstractCatalogUploadAction.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.action.toolbar.window.catalog; 2 | 3 | import com.intellij.openapi.actionSystem.AnActionEvent; 4 | import com.intellij.openapi.actionSystem.PlatformDataKeys; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.ui.treeStructure.SimpleNode; 7 | import com.intellij.ui.treeStructure.SimpleTree; 8 | import com.liuzhihang.doc.view.action.upload.AbstractUploadAction; 9 | import com.liuzhihang.doc.view.data.DocViewDataKeys; 10 | import com.liuzhihang.doc.view.ui.window.DocViewNode; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | /** 14 | * @author liuzhihang 15 | * @date 2021/10/23 23:13 16 | */ 17 | public abstract class AbstractCatalogUploadAction extends AbstractUploadAction { 18 | 19 | protected Project project; 20 | 21 | @Override 22 | public void actionPerformed(@NotNull AnActionEvent e) { 23 | 24 | // 获取当前project对象 25 | project = e.getData(PlatformDataKeys.PROJECT); 26 | SimpleTree simpleTree = e.getData(DocViewDataKeys.WINDOW_CATALOG_TREE); 27 | 28 | if (simpleTree == null || project == null) { 29 | return; 30 | } 31 | 32 | SimpleNode selectedNode = simpleTree.getSelectedNode(); 33 | 34 | if (selectedNode instanceof DocViewNode docViewNode) { 35 | uploadService().upload(project, docViewNode.docViewList()); 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/dto/Body.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.dto; 2 | 3 | 4 | import com.intellij.psi.PsiElement; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | 8 | import java.util.LinkedList; 9 | import java.util.List; 10 | 11 | /** 12 | * @author liuzhihang 13 | * @date 2020/2/27 16:39 14 | */ 15 | @Getter 16 | @Setter 17 | public class Body { 18 | 19 | /** 20 | * 参数的 psiElement 21 | */ 22 | private PsiElement psiElement; 23 | 24 | /** 25 | * 是否必须 26 | */ 27 | private Boolean required; 28 | /** 29 | * 参数名 30 | */ 31 | private String name; 32 | /** 33 | * 参数示例 34 | */ 35 | private String example; 36 | 37 | /** 38 | * 参数描述 39 | */ 40 | private String desc; 41 | 42 | /** 43 | * 类型 44 | */ 45 | private String type; 46 | 47 | /** 48 | * since 49 | */ 50 | private String since; 51 | 52 | /** 53 | * version 54 | */ 55 | private String version; 56 | 57 | /** 58 | * 初始化时创建集合 59 | */ 60 | private List childList = new LinkedList<>(); 61 | 62 | private Body parent; 63 | 64 | /** 65 | * 当类型是类时, 全路径 66 | * 如果是 HashMap 则是内部泛型的类型 67 | */ 68 | private String qualifiedNameForClassType; 69 | 70 | /** 71 | * 是否是集合 72 | */ 73 | private boolean isCollection = false; 74 | 75 | /** 76 | * 是否是 map 77 | */ 78 | private boolean isMap = false; 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/config/ApplicationSettings.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.config; 2 | 3 | import com.intellij.openapi.components.PersistentStateComponent; 4 | import com.intellij.openapi.components.State; 5 | import com.intellij.openapi.components.Storage; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.util.xmlb.XmlSerializerUtil; 8 | import lombok.Data; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | /** 13 | * 项目级的持久配置 14 | *

15 | *

16 | * See IntelliJ Platform SDK DevGuide 17 | * 18 | * @author liuzhihang 19 | * @date 2020/2/27 19:02 20 | */ 21 | @Data 22 | @State(name = "DocViewApplicationSettingsComponent", storages = {@Storage("DocViewApplicationSettings.xml")}) 23 | public class ApplicationSettings implements PersistentStateComponent { 24 | 25 | private String pluginVersion = "0.0.1"; 26 | 27 | 28 | public static ApplicationSettings getInstance(@NotNull Project project) { 29 | return project.getService(ApplicationSettings.class); 30 | } 31 | 32 | 33 | @Nullable 34 | @Override 35 | public ApplicationSettings getState() { 36 | return this; 37 | } 38 | 39 | @Override 40 | public void loadState(@NotNull ApplicationSettings state) { 41 | XmlSerializerUtil.copyBean(state, this); 42 | } 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/action/toolbar/preview/PreviewRightExportAction.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.action.toolbar.preview; 2 | 3 | import com.intellij.openapi.actionSystem.AnAction; 4 | import com.intellij.openapi.actionSystem.AnActionEvent; 5 | import com.intellij.openapi.actionSystem.PlatformDataKeys; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.openapi.ui.popup.JBPopup; 8 | import com.liuzhihang.doc.view.data.DocViewDataKeys; 9 | import com.liuzhihang.doc.view.dto.DocView; 10 | import com.liuzhihang.doc.view.utils.ExportUtils; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | /** 14 | * Preview 右侧单个导出 15 | * 16 | * @author liuzhihang 17 | * @date 2021/10/23 22:31 18 | */ 19 | public class PreviewRightExportAction extends AnAction { 20 | @Override 21 | public void actionPerformed(@NotNull AnActionEvent e) { 22 | 23 | // 获取当前project对象 24 | Project project = e.getData(PlatformDataKeys.PROJECT); 25 | JBPopup popup = e.getData(DocViewDataKeys.PREVIEW_POPUP); 26 | DocView currentDocView = e.getData(DocViewDataKeys.PREVIEW_CURRENT_DOC_VIEW); 27 | String currentMarkdownText = e.getData(DocViewDataKeys.PREVIEW_MARKDOWN_TEXT); 28 | 29 | if (popup == null || project == null || currentDocView == null || currentMarkdownText == null) { 30 | return; 31 | } 32 | popup.cancel(); 33 | ExportUtils.exportMarkdown(project, currentDocView.getName(), currentMarkdownText); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/dto/DocViewParamData.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.dto; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import lombok.Data; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | /** 10 | * 参数 data 数据 11 | * 12 | * @author liuzhihang 13 | * @date 2020/2/27 16:39 14 | */ 15 | @Data 16 | public class DocViewParamData { 17 | 18 | /** 19 | * 参数的 psiElement 20 | */ 21 | private PsiElement psiElement; 22 | 23 | /** 24 | * 前缀 1 25 | */ 26 | private String prefixSymbol1 = ""; 27 | 28 | /** 29 | * 前缀 2 30 | */ 31 | private String prefixSymbol2 = ""; 32 | 33 | /** 34 | * 参数名 35 | */ 36 | private String name; 37 | 38 | /** 39 | * 类型 40 | */ 41 | private String type; 42 | 43 | /** 44 | * 是否必须 45 | */ 46 | private Boolean required; 47 | 48 | /** 49 | * 参数示例 50 | */ 51 | private String example; 52 | 53 | /** 54 | * 参数描述 55 | */ 56 | private String desc = ""; 57 | 58 | /** 59 | * since 60 | */ 61 | private String since; 62 | 63 | /** 64 | * version 65 | */ 66 | private String version; 67 | 68 | /** 69 | * 子 70 | */ 71 | private List childList = new ArrayList<>(); 72 | 73 | /** 74 | * 是否是集合 75 | */ 76 | private boolean isCollection = false; 77 | 78 | /** 79 | * 是否是 map 80 | */ 81 | private boolean isMap = false; 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/action/toolbar/window/catalog/CatalogClearAction.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.action.toolbar.window.catalog; 2 | 3 | import com.intellij.openapi.actionSystem.AnAction; 4 | import com.intellij.openapi.actionSystem.AnActionEvent; 5 | import com.intellij.openapi.actionSystem.PlatformDataKeys; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.ui.treeStructure.SimpleNode; 8 | import com.intellij.ui.treeStructure.SimpleTree; 9 | import com.liuzhihang.doc.view.data.DocViewDataKeys; 10 | import com.liuzhihang.doc.view.ui.window.DocViewNode; 11 | import com.liuzhihang.doc.view.utils.CustomFileUtils; 12 | import org.jetbrains.annotations.NotNull; 13 | 14 | /** 15 | * @author liuzhihang 16 | * @date 2021/10/23 19:55 17 | */ 18 | public class CatalogClearAction extends AnAction { 19 | 20 | @Override 21 | public void actionPerformed(@NotNull AnActionEvent e) { 22 | 23 | // 获取当前project对象 24 | Project project = e.getData(PlatformDataKeys.PROJECT); 25 | SimpleTree simpleTree = e.getData(DocViewDataKeys.WINDOW_CATALOG_TREE); 26 | 27 | if (simpleTree == null || project == null) { 28 | return; 29 | } 30 | 31 | SimpleNode selectedNode = simpleTree.getSelectedNode(); 32 | 33 | if (selectedNode instanceof DocViewNode) { 34 | DocViewNode docViewNode = (DocViewNode) selectedNode; 35 | CustomFileUtils.delete(project, docViewNode.docPath(project)); 36 | 37 | } 38 | 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/action/toolbar/preview/MenuExportAllAction.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.action.toolbar.preview; 2 | 3 | import com.intellij.openapi.actionSystem.AnAction; 4 | import com.intellij.openapi.actionSystem.AnActionEvent; 5 | import com.intellij.openapi.actionSystem.PlatformDataKeys; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.openapi.ui.popup.JBPopup; 8 | import com.liuzhihang.doc.view.data.DocViewDataKeys; 9 | import com.liuzhihang.doc.view.dto.DocView; 10 | import com.liuzhihang.doc.view.utils.ExportUtils; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | import java.util.List; 14 | 15 | /** 16 | * Preview 导出全部 17 | * 18 | * @author liuzhihang 19 | * @date 2021/10/23 22:31 20 | */ 21 | public class MenuExportAllAction extends AnAction { 22 | @Override 23 | public void actionPerformed(@NotNull AnActionEvent e) { 24 | 25 | // 获取当前project对象 26 | Project project = e.getData(PlatformDataKeys.PROJECT); 27 | JBPopup popup = e.getData(DocViewDataKeys.PREVIEW_POPUP); 28 | DocView currentDocView = e.getData(DocViewDataKeys.PREVIEW_CURRENT_DOC_VIEW); 29 | List docViewList = e.getData(DocViewDataKeys.PREVIEW_DOC_VIEW_LIST); 30 | 31 | if (popup == null || project == null || currentDocView == null || docViewList == null) { 32 | return; 33 | } 34 | 35 | popup.cancel(); 36 | ExportUtils.batchExportMarkdown(project, currentDocView.getPsiClass().getName(), docViewList); 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/integration/YuQueFacadeService.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.integration; 2 | 3 | import com.liuzhihang.doc.view.integration.dto.YuQueCreate; 4 | import com.liuzhihang.doc.view.integration.dto.YuQueResponse; 5 | import com.liuzhihang.doc.view.integration.dto.YuQueUpdate; 6 | 7 | /** 8 | * 语雀包装 service 9 | *

10 | * https://www.yuque.com/yuque/developer/doc#Response-1 11 | * 12 | * @author liuzhihang 13 | * @date 2022-03-31 23:01:56 14 | */ 15 | public interface YuQueFacadeService { 16 | 17 | /** 18 | * 获取单篇文章 19 | * 20 | * @param url 21 | * @param token 22 | * @param namespace 23 | * @param slug 24 | * @return 25 | * @throws Exception 26 | */ 27 | YuQueResponse getDoc(String url, String token, String namespace, String slug) throws Exception; 28 | 29 | /** 30 | * 创建文档 31 | * 32 | * @param url 33 | * @param token 34 | * @param namespace 35 | * @param yuQueCreate 36 | * @return 37 | * @throws Exception 38 | */ 39 | YuQueResponse create(String url, String token, String namespace, YuQueCreate yuQueCreate) throws Exception; 40 | 41 | /** 42 | * 更新文档 43 | * 44 | * @param url 45 | * @param token 46 | * @param namespace 47 | * @param id 文章的 id 48 | * @param yuQueUpdate 49 | * @return 50 | * @throws Exception 51 | */ 52 | YuQueResponse update(String url, String token, String namespace, Long id, YuQueUpdate yuQueUpdate) throws Exception; 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/config/YuQueSettings.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.config; 2 | 3 | import com.intellij.openapi.components.PersistentStateComponent; 4 | import com.intellij.openapi.components.State; 5 | import com.intellij.openapi.components.Storage; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.util.xmlb.XmlSerializerUtil; 8 | import lombok.Data; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | /** 13 | * ShowDoc 的相关设置 14 | * 15 | * @author liuzhihang 16 | * @date 2020/11/22 13:51 17 | */ 18 | @Data 19 | @State(name = "DocViewYuQueSettingsComment", storages = {@Storage("DocViewYuQueSettings.xml")}) 20 | public class YuQueSettings implements PersistentStateComponent { 21 | 22 | private String url = "https://www.yuque.com"; 23 | private String apiUrl = "https://www.yuque.com/api/v2"; 24 | 25 | private String token; 26 | 27 | private String login; 28 | 29 | private String repos; 30 | 31 | 32 | public static YuQueSettings getInstance(@NotNull Project project) { 33 | return project.getService(YuQueSettings.class); 34 | } 35 | 36 | @Override 37 | public @Nullable 38 | YuQueSettings getState() { 39 | return this; 40 | } 41 | 42 | @Override 43 | public void loadState(@NotNull YuQueSettings state) { 44 | 45 | XmlSerializerUtil.copyBean(state, this); 46 | } 47 | 48 | public String getNamespace() { 49 | return login + "/" + repos; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/config/WindowSettings.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.config; 2 | 3 | import com.intellij.openapi.components.PersistentStateComponent; 4 | import com.intellij.openapi.components.State; 5 | import com.intellij.openapi.components.Storage; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.util.xmlb.XmlSerializerUtil; 8 | import lombok.Data; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | /** 13 | * 配置持久保存 14 | *

15 | *

16 | * See IntelliJ Platform SDK DevGuide 17 | * 18 | * @author liuzhihang 19 | * @date 2020/2/27 19:02 20 | */ 21 | @Data 22 | @State(name = "DocViewWindowSettingsComponent", storages = {@Storage("DocViewWindowSettings.xml")}) 23 | public class WindowSettings implements PersistentStateComponent { 24 | 25 | /** 26 | * 范围 27 | */ 28 | private String scope; 29 | 30 | /** 31 | * 包含接口 32 | */ 33 | private boolean includeInterface = false; 34 | 35 | 36 | public static WindowSettings getInstance(@NotNull Project project) { 37 | return project.getService(WindowSettings.class); 38 | } 39 | 40 | 41 | @Nullable 42 | @Override 43 | public WindowSettings getState() { 44 | return this; 45 | } 46 | 47 | @Override 48 | public void loadState(@NotNull WindowSettings state) { 49 | XmlSerializerUtil.copyBean(state, this); 50 | } 51 | 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/constant/FieldTypeConstant.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.constant; 2 | 3 | import com.google.common.collect.Sets; 4 | import org.jetbrains.annotations.NonNls; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | import java.util.Set; 9 | 10 | /** 11 | * @author liuzhihang 12 | * @date 2020/3/4 20:35 13 | */ 14 | public final class FieldTypeConstant { 15 | 16 | private FieldTypeConstant() { 17 | } 18 | 19 | @NonNls 20 | public static final Map FIELD_TYPE = new HashMap<>(64); 21 | 22 | public static final Set BASE_TYPE_SET = Sets.newHashSet("byte", "short", "int", "long", "char", "float", 23 | "double", "boolean"); 24 | 25 | /** 26 | * 包装类型 & String 27 | */ 28 | public static final Set PACKAGE_TYPE_SET = Sets.newHashSet("Byte", "Short", "Integer", "Long", "Float", "Double", 29 | "Boolean", "String"); 30 | 31 | static { 32 | // 包装数据类型 33 | FIELD_TYPE.put("Byte", 0); 34 | FIELD_TYPE.put("Short", 0); 35 | FIELD_TYPE.put("Integer", 0); 36 | FIELD_TYPE.put("Long", 0L); 37 | FIELD_TYPE.put("Float", 0.0F); 38 | FIELD_TYPE.put("Double", 0.0D); 39 | FIELD_TYPE.put("Boolean", false); 40 | // 其他 41 | FIELD_TYPE.put("String", ""); 42 | FIELD_TYPE.put("BigDecimal", null); 43 | FIELD_TYPE.put("Date", null); 44 | FIELD_TYPE.put("LocalDate", null); 45 | FIELD_TYPE.put("LocalTime", null); 46 | FIELD_TYPE.put("LocalDateTime", null); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/action/toolbar/window/WindowRefreshAction.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.action.toolbar.window; 2 | 3 | import com.intellij.openapi.actionSystem.AnAction; 4 | import com.intellij.openapi.actionSystem.AnActionEvent; 5 | import com.intellij.openapi.actionSystem.PlatformDataKeys; 6 | import com.intellij.openapi.progress.ProgressIndicator; 7 | import com.intellij.openapi.progress.ProgressManager; 8 | import com.intellij.openapi.progress.Task; 9 | import com.intellij.openapi.project.Project; 10 | import com.liuzhihang.doc.view.data.DocViewDataKeys; 11 | import com.liuzhihang.doc.view.ui.window.DocViewWindowPanel; 12 | import org.jetbrains.annotations.NotNull; 13 | 14 | /** 15 | * @author liuzhihang 16 | * @date 2021/10/23 19:55 17 | */ 18 | public class WindowRefreshAction extends AnAction { 19 | 20 | @Override 21 | public void actionPerformed(@NotNull AnActionEvent e) { 22 | 23 | // 获取当前project对象 24 | Project project = e.getData(PlatformDataKeys.PROJECT); 25 | DocViewWindowPanel docViewWindowPanel = e.getData(DocViewDataKeys.WINDOW_PANE); 26 | 27 | if (docViewWindowPanel == null || project == null) { 28 | return; 29 | } 30 | 31 | ProgressManager.getInstance().run(new Task.Backgroundable(project, "Doc View", false) { 32 | @Override 33 | public void run(@NotNull ProgressIndicator progressIndicator) { 34 | 35 | progressIndicator.setText("Directory refreshing"); 36 | docViewWindowPanel.updateCatalogTree(); 37 | } 38 | }); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/http/YApiGetTest.http: -------------------------------------------------------------------------------- 1 | ### 2 | POST http://apidoc.xxx.com:3000/api/interface/save 3 | Content-Type: application/json 4 | 5 | { 6 | "yapiUrl": "http://apidoc.opaydev.com:3000", 7 | "token": "33622ea7067e12c0d632e1362a2eec5964175bc3bd601bb12d5696e8ee38126c", 8 | "projectId": 116, 9 | "id": null, 10 | "catid": 1649, 11 | "path": "/user/requestParamUserAc11", 12 | "method": "GET", 13 | "req_body_type": "form", 14 | "req_body_other": null, 15 | "req_body_is_json_schema": true, 16 | "req_body_form": [ 17 | { 18 | "name": "userId", 19 | "type": "String", 20 | "example": null, 21 | "desc": "", 22 | "required": "0" 23 | } 24 | ], 25 | "req_params": [], 26 | "req_headers": [ 27 | { 28 | "name": "Content-Type", 29 | "example": null, 30 | "desc": "application/x-www-form-urlencoded", 31 | "value": "application/x-www-form-urlencoded", 32 | "required": "1" 33 | } 34 | ], 35 | "req_query": [], 36 | "res_body_type": "json", 37 | "res_body": "{\"description\":\" \",\"type\":\"object\",\"title\":\" \",\"required\":[],\"properties\":{\"code\":{\"type\":{},\"description\":\"响应码\"},\"msg\":{\"type\":{},\"description\":\"响应信息\"},\"data\":{\"type\":{},\"description\":\"响应数据\"}}}", 38 | "desc": "", 39 | "title": "requestParamUser111", 40 | "switch_notice": false, 41 | "status": "undone", 42 | "res_body_is_json_schema": true, 43 | "markdown": "**接口名称:**\n\nrequestParamUser111\n\n**接口描述:**\n\n\n\n**请求示例:**\n\nnull\n\n**返回示例:**\n\n{\n \"code\": \"\",\n \"msg\": \"\",\n \"data\": {\n \"userId\": \"\"\n }\n}" 44 | } -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/action/toolbar/window/catalog/CatalogOpenAction.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.action.toolbar.window.catalog; 2 | 3 | import com.intellij.openapi.actionSystem.AnAction; 4 | import com.intellij.openapi.actionSystem.AnActionEvent; 5 | import com.intellij.openapi.actionSystem.PlatformDataKeys; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.ui.TreeExpandCollapse; 8 | import com.intellij.ui.treeStructure.SimpleNode; 9 | import com.intellij.ui.treeStructure.SimpleTree; 10 | import com.liuzhihang.doc.view.data.DocViewDataKeys; 11 | import com.liuzhihang.doc.view.ui.window.MethodNode; 12 | import com.liuzhihang.doc.view.utils.CustomFileUtils; 13 | import org.jetbrains.annotations.NotNull; 14 | 15 | /** 16 | * @author liuzhihang 17 | * @date 2021/10/23 19:55 18 | */ 19 | public class CatalogOpenAction extends AnAction { 20 | 21 | @Override 22 | public void actionPerformed(@NotNull AnActionEvent e) { 23 | 24 | // 获取当前project对象 25 | Project project = e.getData(PlatformDataKeys.PROJECT); 26 | SimpleTree simpleTree = e.getData(DocViewDataKeys.WINDOW_CATALOG_TREE); 27 | 28 | if (simpleTree == null || project == null) { 29 | return; 30 | } 31 | 32 | SimpleNode selectedNode = simpleTree.getSelectedNode(); 33 | 34 | if (selectedNode instanceof MethodNode) { 35 | MethodNode methodNode = (MethodNode) selectedNode; 36 | CustomFileUtils.openMd(project, methodNode); 37 | } else { 38 | TreeExpandCollapse.expandAll(simpleTree); 39 | } 40 | 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/integration/impl/ShowDocFacadeServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.integration.impl; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import com.google.gson.JsonObject; 6 | import com.intellij.openapi.diagnostic.Logger; 7 | import com.liuzhihang.doc.view.integration.ShowDocFacadeService; 8 | import com.liuzhihang.doc.view.integration.dto.ShowDocUpdateRequest; 9 | import com.liuzhihang.doc.view.integration.dto.ShowDocUpdateResponse; 10 | import com.liuzhihang.doc.view.utils.HttpUtils; 11 | import org.apache.commons.lang3.StringUtils; 12 | 13 | /** 14 | * @author liuzhihang 15 | * @date 2021/7/27 11:53 16 | */ 17 | public class ShowDocFacadeServiceImpl implements ShowDocFacadeService { 18 | 19 | private static final Logger LOGGER = Logger.getInstance(ShowDocFacadeServiceImpl.class); 20 | 21 | private static final Gson gson = new GsonBuilder().serializeNulls().create(); 22 | 23 | @Override 24 | public ShowDocUpdateResponse updateByApi(ShowDocUpdateRequest request) throws Exception { 25 | 26 | String resp = HttpUtils.post(request.getShowDocUrl() + "/api/item/updateByApi", gson.toJson(request)); 27 | 28 | if (StringUtils.isBlank(resp)) { 29 | throw new Exception("ShowDoc 接口返回为空"); 30 | } 31 | 32 | JsonObject jsonObject = gson.fromJson(resp, JsonObject.class); 33 | 34 | if (!jsonObject.get("error_code").getAsString().equals("0")) { 35 | throw new Exception("ShowDoc 接口返回失败:" + resp); 36 | } 37 | return gson.fromJson(jsonObject, ShowDocUpdateResponse.class); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/action/toolbar/window/catalog/CatalogHttpClientAction.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.action.toolbar.window.catalog; 2 | 3 | import com.intellij.openapi.actionSystem.AnAction; 4 | import com.intellij.openapi.actionSystem.AnActionEvent; 5 | import com.intellij.openapi.actionSystem.PlatformDataKeys; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.ui.TreeExpandCollapse; 8 | import com.intellij.ui.treeStructure.SimpleNode; 9 | import com.intellij.ui.treeStructure.SimpleTree; 10 | import com.liuzhihang.doc.view.data.DocViewDataKeys; 11 | import com.liuzhihang.doc.view.ui.window.MethodNode; 12 | import com.liuzhihang.doc.view.utils.CustomFileUtils; 13 | 14 | /** 15 | * window 窗口目录树, 右键生成 HttpClient 功能 16 | * 17 | * @author liuzhihang 18 | * @version CatalogHttpClientAction.java, v 0.1 2022/6/16 17:53 liuzhihang 19 | */ 20 | public class CatalogHttpClientAction extends AnAction { 21 | 22 | @Override 23 | public void actionPerformed(AnActionEvent e) { 24 | 25 | // 获取当前project对象 26 | Project project = e.getData(PlatformDataKeys.PROJECT); 27 | SimpleTree simpleTree = e.getData(DocViewDataKeys.WINDOW_CATALOG_TREE); 28 | 29 | if (simpleTree == null || project == null) { 30 | return; 31 | } 32 | 33 | SimpleNode selectedNode = simpleTree.getSelectedNode(); 34 | 35 | if (selectedNode instanceof MethodNode) { 36 | MethodNode methodNode = (MethodNode) selectedNode; 37 | CustomFileUtils.openHttp(project, methodNode); 38 | } else { 39 | TreeExpandCollapse.expandAll(simpleTree); 40 | } 41 | 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/resources/icons/clear.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/action/toolbar/window/catalog/CatalogExportAction.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.action.toolbar.window.catalog; 2 | 3 | import com.intellij.openapi.actionSystem.AnAction; 4 | import com.intellij.openapi.actionSystem.AnActionEvent; 5 | import com.intellij.openapi.actionSystem.PlatformDataKeys; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.ui.treeStructure.SimpleNode; 8 | import com.intellij.ui.treeStructure.SimpleTree; 9 | import com.liuzhihang.doc.view.data.DocViewDataKeys; 10 | import com.liuzhihang.doc.view.dto.DocView; 11 | import com.liuzhihang.doc.view.ui.window.DocViewNode; 12 | import com.liuzhihang.doc.view.utils.ExportUtils; 13 | import org.jetbrains.annotations.NotNull; 14 | 15 | import java.util.List; 16 | 17 | /** 18 | * @author liuzhihang 19 | * @date 2021/10/23 19:55 20 | */ 21 | public class CatalogExportAction extends AnAction { 22 | 23 | @Override 24 | public void actionPerformed(@NotNull AnActionEvent e) { 25 | 26 | // 获取当前project对象 27 | Project project = e.getData(PlatformDataKeys.PROJECT); 28 | SimpleTree simpleTree = e.getData(DocViewDataKeys.WINDOW_CATALOG_TREE); 29 | 30 | if (simpleTree == null || project == null) { 31 | return; 32 | } 33 | 34 | SimpleNode selectedNode = simpleTree.getSelectedNode(); 35 | 36 | if (selectedNode instanceof DocViewNode) { 37 | DocViewNode docViewNode = (DocViewNode) selectedNode; 38 | List docViews = docViewNode.docViewList(); 39 | ExportUtils.batchExportMarkdown(project, docViewNode.getName(), docViews); 40 | } 41 | 42 | } 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/resources/icons/clear_dark.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/config/SettingsConfigurable.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.config; 2 | 3 | import com.intellij.openapi.options.ConfigurationException; 4 | import com.intellij.openapi.options.SearchableConfigurable; 5 | import com.intellij.openapi.project.Project; 6 | import com.liuzhihang.doc.view.ui.SettingsForm; 7 | import org.jetbrains.annotations.Nls; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | import javax.swing.*; 12 | 13 | /** 14 | * @author liuzhihang 15 | * @date 2020/3/4 13:05 16 | */ 17 | public class SettingsConfigurable implements SearchableConfigurable { 18 | 19 | 20 | private final Project project; 21 | 22 | private SettingsForm settingsForm; 23 | 24 | public SettingsConfigurable(@NotNull Project project) { 25 | this.project = project; 26 | } 27 | 28 | @NotNull 29 | @Override 30 | public String getId() { 31 | return "liuzhihang.api.doc.SettingsConfigurable"; 32 | } 33 | 34 | @Nls(capitalization = Nls.Capitalization.Title) 35 | @Override 36 | public String getDisplayName() { 37 | return "Doc View"; 38 | } 39 | 40 | @Nullable 41 | @Override 42 | public JComponent createComponent() { 43 | 44 | settingsForm = new SettingsForm(project); 45 | 46 | return settingsForm.getRootPanel(); 47 | } 48 | 49 | @Override 50 | public boolean isModified() { 51 | return settingsForm.isModified(); 52 | } 53 | 54 | @Override 55 | public void apply() throws ConfigurationException { 56 | settingsForm.apply(); 57 | } 58 | 59 | @Override 60 | public void reset() { 61 | settingsForm.reset(); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/utils/VelocityUtils.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.utils; 2 | 3 | import com.liuzhihang.doc.view.dto.DocViewData; 4 | import org.apache.velocity.VelocityContext; 5 | import org.apache.velocity.app.VelocityEngine; 6 | import org.apache.velocity.runtime.RuntimeConstants; 7 | 8 | import java.io.StringWriter; 9 | import java.util.Properties; 10 | 11 | /** 12 | * 根据模版生成对应的内容 13 | * 14 | * @author liuzhihang 15 | * @date 2020/11/21 15:38 16 | */ 17 | public class VelocityUtils { 18 | 19 | private static VelocityEngine engine; 20 | private static String VM_LOG_TAG = "DocView VelocityUtils"; 21 | 22 | static { 23 | engine = new VelocityEngine(); 24 | engine.setProperty(RuntimeConstants.PARSER_POOL_SIZE, 20); 25 | engine.setProperty(RuntimeConstants.INPUT_ENCODING, "UTF-8"); 26 | // engine.setProperty(RuntimeConstants.OUTPUT_ENCODING, "UTF-8"); 27 | 28 | Properties props = new Properties(); 29 | props.put("runtime.log.logsystem.class", "org.apache.velocity.runtime.log.SimpleLog4JLogSystem"); 30 | props.put("runtime.log.logsystem.log4j.category", "velocity"); 31 | props.put("runtime.log.logsystem.log4j.logger", "velocity"); 32 | engine.init(props); 33 | } 34 | 35 | public static String convert(String template, DocViewData data) { 36 | 37 | 38 | StringWriter writer = new StringWriter(); 39 | VelocityContext velocityContext = new VelocityContext(); 40 | velocityContext.put("DocView", data); 41 | boolean isSuccess = engine.evaluate(velocityContext, writer, VM_LOG_TAG, template); 42 | if (!isSuccess) { 43 | return "ERROR"; 44 | } 45 | 46 | 47 | return writer.toString(); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/data/DocViewDataKeys.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.data; 2 | 3 | import com.intellij.openapi.actionSystem.DataKey; 4 | import com.intellij.openapi.ui.popup.JBPopup; 5 | import com.intellij.ui.treeStructure.SimpleTree; 6 | import com.liuzhihang.doc.view.dto.DocView; 7 | import com.liuzhihang.doc.view.ui.PreviewForm; 8 | import com.liuzhihang.doc.view.ui.window.DocViewWindowPanel; 9 | import com.liuzhihang.doc.view.ui.window.RootNode; 10 | 11 | import javax.swing.*; 12 | import java.util.List; 13 | 14 | /** 15 | * @author liuzhihang 16 | * @date 2021/10/23 21:04 17 | */ 18 | public class DocViewDataKeys { 19 | 20 | /** 21 | * 目录树 22 | */ 23 | public static final DataKey WINDOW_PANE = DataKey.create("DocViewWindowPane"); 24 | public static final DataKey WINDOW_ROOT_NODE = DataKey.create("DocViewWindowRootNode"); 25 | public static final DataKey WINDOW_CATALOG_TREE = DataKey.create("DocViewWindowTree"); 26 | public static final DataKey WINDOW_TOOLBAR = DataKey.create("DocViewWindowToolbar"); 27 | 28 | 29 | /** 30 | * Preview 界面 31 | */ 32 | public static final DataKey PREVIEW_FORM = DataKey.create("PreviewForm"); 33 | public static final DataKey PREVIEW_POPUP = DataKey.create("PreviewPop"); 34 | public static final DataKey PREVIEW_CURRENT_DOC_VIEW = DataKey.create("PreviewCurrentDocView"); 35 | public static final DataKey PREVIEW_MARKDOWN_TEXT = DataKey.create("PreviewMarkdownText"); 36 | public static final DataKey> PREVIEW_DOC_VIEW_LIST = DataKey.create("PreviewDocViewList"); 37 | public static final DataKey PREVIEW_TOOLBAR_PANEL = DataKey.create("PreviewToolbarPanel"); 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/notification/DocViewStartupNotification.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.notification; 2 | 3 | import com.intellij.ide.plugins.IdeaPluginDescriptor; 4 | import com.intellij.ide.plugins.PluginManagerCore; 5 | import com.intellij.openapi.application.ApplicationManager; 6 | import com.intellij.openapi.extensions.PluginId; 7 | import com.intellij.openapi.project.DumbAware; 8 | import com.intellij.openapi.project.Project; 9 | import com.intellij.openapi.startup.StartupActivity; 10 | import com.intellij.util.text.VersionComparatorUtil; 11 | import com.liuzhihang.doc.view.config.ApplicationSettings; 12 | import org.jetbrains.annotations.NotNull; 13 | 14 | /** 15 | * 安装新版本或者更新时提示 16 | * 17 | * @author liuzhihang 18 | * @date 2021/10/22 14:09 19 | */ 20 | public class DocViewStartupNotification implements StartupActivity, DumbAware { 21 | 22 | @Override 23 | public void runActivity(@NotNull Project project) { 24 | 25 | if (ApplicationManager.getApplication().isUnitTestMode()) { 26 | return; 27 | } 28 | ApplicationSettings applicationSettings = ApplicationManager.getApplication().getService(ApplicationSettings.class); 29 | 30 | // 上次安装的版本 31 | String lastVersion = applicationSettings.getPluginVersion(); 32 | 33 | IdeaPluginDescriptor plugin = PluginManagerCore.getPlugin(PluginId.getId("com.liuzhihang.doc-view")); 34 | 35 | // 一个版本只通知一次 36 | if (lastVersion != null && plugin != null) { 37 | final int compare = VersionComparatorUtil.compare(lastVersion, plugin.getVersion()); 38 | if (compare < 0) { 39 | DocViewNotification.startupNotification(project); 40 | applicationSettings.setPluginVersion(plugin.getVersion()); 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/config/YApiSettingsConfigurable.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.config; 2 | 3 | import com.intellij.openapi.options.ConfigurationException; 4 | import com.intellij.openapi.options.SearchableConfigurable; 5 | import com.intellij.openapi.project.Project; 6 | import com.liuzhihang.doc.view.ui.YApiSettingForm; 7 | import org.jetbrains.annotations.Nls; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | import javax.swing.*; 12 | 13 | /** 14 | * YApi 配置 15 | * 16 | * @author liuzhihang 17 | * @date 2021/6/8 17:10 18 | */ 19 | public class YApiSettingsConfigurable implements SearchableConfigurable { 20 | 21 | private YApiSettingForm yApiSettingForm; 22 | 23 | private final Project project; 24 | 25 | public YApiSettingsConfigurable(@NotNull Project project) { 26 | this.project = project; 27 | } 28 | 29 | @NotNull 30 | @Override 31 | public String getId() { 32 | return "liuzhihang.api.doc.YApiSettingsConfigurable"; 33 | } 34 | 35 | @Nls(capitalization = Nls.Capitalization.Title) 36 | @Override 37 | public String getDisplayName() { 38 | return "YApi Settings"; 39 | } 40 | 41 | @Nullable 42 | @Override 43 | public JComponent createComponent() { 44 | 45 | yApiSettingForm = new YApiSettingForm(project); 46 | 47 | return yApiSettingForm.getRootPanel(); 48 | } 49 | 50 | 51 | @Override 52 | public boolean isModified() { 53 | 54 | return yApiSettingForm.isModified(); 55 | } 56 | 57 | @Override 58 | public void apply() throws ConfigurationException { 59 | 60 | yApiSettingForm.apply(); 61 | } 62 | 63 | @Override 64 | public void reset() { 65 | 66 | yApiSettingForm.reset(); 67 | } 68 | 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/config/TemplateConfigurable.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.config; 2 | 3 | import com.intellij.openapi.options.ConfigurationException; 4 | import com.intellij.openapi.options.SearchableConfigurable; 5 | import com.intellij.openapi.project.Project; 6 | import com.liuzhihang.doc.view.ui.TemplateSettingForm; 7 | import org.jetbrains.annotations.Nls; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | import javax.swing.*; 12 | 13 | /** 14 | * 设置模版 15 | * 16 | * @author liuzhihang 17 | * @date 2020/3/4 13:05 18 | */ 19 | public class TemplateConfigurable implements SearchableConfigurable { 20 | 21 | private TemplateSettingForm templateSettingForm; 22 | 23 | private final Project project; 24 | 25 | public TemplateConfigurable(@NotNull Project project) { 26 | this.project = project; 27 | } 28 | 29 | 30 | @NotNull 31 | @Override 32 | public String getId() { 33 | return "liuzhihang.api.doc.TemplateConfigurable"; 34 | } 35 | 36 | @Nls(capitalization = Nls.Capitalization.Title) 37 | @Override 38 | public String getDisplayName() { 39 | return "Markdown Template"; 40 | } 41 | 42 | @Nullable 43 | @Override 44 | public JComponent createComponent() { 45 | 46 | templateSettingForm = new TemplateSettingForm(project); 47 | 48 | return templateSettingForm.createCenterPanel(); 49 | } 50 | 51 | @Override 52 | public boolean isModified() { 53 | 54 | return templateSettingForm.isModified(); 55 | } 56 | 57 | @Override 58 | public void apply() throws ConfigurationException { 59 | 60 | templateSettingForm.apply(); 61 | } 62 | 63 | @Override 64 | public void reset() { 65 | 66 | templateSettingForm.reset(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/config/YuQueSettingsConfigurable.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.config; 2 | 3 | import com.intellij.openapi.options.ConfigurationException; 4 | import com.intellij.openapi.options.SearchableConfigurable; 5 | import com.intellij.openapi.project.Project; 6 | import com.liuzhihang.doc.view.ui.YuQueSettingForm; 7 | import org.jetbrains.annotations.Nls; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | import javax.swing.*; 12 | 13 | /** 14 | * YApi 配置 15 | * 16 | * @author liuzhihang 17 | * @date 2021/6/8 17:10 18 | */ 19 | public class YuQueSettingsConfigurable implements SearchableConfigurable { 20 | 21 | private YuQueSettingForm yuQueSettingForm; 22 | 23 | private final Project project; 24 | 25 | public YuQueSettingsConfigurable(@NotNull Project project) { 26 | this.project = project; 27 | } 28 | 29 | @NotNull 30 | @Override 31 | public String getId() { 32 | return "liuzhihang.api.doc.YuQueSettingsConfigurable"; 33 | } 34 | 35 | @Nls(capitalization = Nls.Capitalization.Title) 36 | @Override 37 | public String getDisplayName() { 38 | return "YuQue Settings"; 39 | } 40 | 41 | @Nullable 42 | @Override 43 | public JComponent createComponent() { 44 | 45 | yuQueSettingForm = new YuQueSettingForm(project); 46 | 47 | return yuQueSettingForm.getRootPanel(); 48 | } 49 | 50 | 51 | @Override 52 | public boolean isModified() { 53 | 54 | return yuQueSettingForm.isModified(); 55 | } 56 | 57 | @Override 58 | public void apply() throws ConfigurationException { 59 | 60 | yuQueSettingForm.apply(); 61 | } 62 | 63 | @Override 64 | public void reset() { 65 | 66 | yuQueSettingForm.reset(); 67 | } 68 | 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/config/ShowDocSettingsConfigurable.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.config; 2 | 3 | import com.intellij.openapi.options.ConfigurationException; 4 | import com.intellij.openapi.options.SearchableConfigurable; 5 | import com.intellij.openapi.project.Project; 6 | import com.liuzhihang.doc.view.ui.ShowDocSettingForm; 7 | import org.jetbrains.annotations.Nls; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | import javax.swing.*; 12 | 13 | /** 14 | * YApi 配置 15 | * 16 | * @author liuzhihang 17 | * @date 2021/6/8 17:10 18 | */ 19 | public class ShowDocSettingsConfigurable implements SearchableConfigurable { 20 | 21 | private ShowDocSettingForm showDocSettingForm; 22 | 23 | private final Project project; 24 | 25 | public ShowDocSettingsConfigurable(@NotNull Project project) { 26 | this.project = project; 27 | } 28 | 29 | @NotNull 30 | @Override 31 | public String getId() { 32 | return "liuzhihang.api.doc.ShowDocSettingsConfigurable"; 33 | } 34 | 35 | @Nls(capitalization = Nls.Capitalization.Title) 36 | @Override 37 | public String getDisplayName() { 38 | return "ShowDoc Settings"; 39 | } 40 | 41 | @Nullable 42 | @Override 43 | public JComponent createComponent() { 44 | 45 | showDocSettingForm = new ShowDocSettingForm(project); 46 | 47 | return showDocSettingForm.getRootPanel(); 48 | } 49 | 50 | 51 | @Override 52 | public boolean isModified() { 53 | 54 | return showDocSettingForm.isModified(); 55 | } 56 | 57 | @Override 58 | public void apply() throws ConfigurationException { 59 | 60 | showDocSettingForm.apply(); 61 | } 62 | 63 | @Override 64 | public void reset() { 65 | 66 | showDocSettingForm.reset(); 67 | } 68 | 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/config/TemplateSettings.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.config; 2 | 3 | import com.intellij.openapi.components.PersistentStateComponent; 4 | import com.intellij.openapi.components.State; 5 | import com.intellij.openapi.components.Storage; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.util.xmlb.XmlSerializerUtil; 8 | import com.liuzhihang.doc.view.DocViewBundle; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | /** 13 | * DocView 模版设置 14 | * 15 | * @author liuzhihang 16 | * @date 2020/11/22 13:51 17 | */ 18 | @State(name = "TemplateSettingsComponent", storages = {@Storage("DocViewTemplateSettings.xml")}) 19 | public class TemplateSettings implements PersistentStateComponent { 20 | 21 | /** 22 | * spring 的模版 23 | */ 24 | private String springTemplate = DocViewBundle.message("template.spring.init"); 25 | private String dubboTemplate = DocViewBundle.message("template.dubbo.init"); 26 | 27 | public static TemplateSettings getInstance(@NotNull Project project) { 28 | return project.getService(TemplateSettings.class); 29 | } 30 | 31 | 32 | @Nullable 33 | @Override 34 | public TemplateSettings getState() { 35 | return this; 36 | } 37 | 38 | @Override 39 | public void loadState(@NotNull TemplateSettings state) { 40 | XmlSerializerUtil.copyBean(state, this); 41 | } 42 | 43 | 44 | public String getSpringTemplate() { 45 | return springTemplate; 46 | } 47 | 48 | public void setSpringTemplate(String springTemplate) { 49 | this.springTemplate = springTemplate; 50 | } 51 | 52 | public String getDubboTemplate() { 53 | return dubboTemplate; 54 | } 55 | 56 | public void setDubboTemplate(String dubboTemplate) { 57 | this.dubboTemplate = dubboTemplate; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/action/toolbar/preview/PreviewRightCopyAction.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.action.toolbar.preview; 2 | 3 | import com.intellij.openapi.actionSystem.AnAction; 4 | import com.intellij.openapi.actionSystem.AnActionEvent; 5 | import com.intellij.openapi.actionSystem.PlatformDataKeys; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.openapi.ui.popup.JBPopup; 8 | import com.liuzhihang.doc.view.DocViewBundle; 9 | import com.liuzhihang.doc.view.data.DocViewDataKeys; 10 | import com.liuzhihang.doc.view.dto.DocView; 11 | import com.liuzhihang.doc.view.notification.DocViewNotification; 12 | import org.jetbrains.annotations.NotNull; 13 | 14 | import java.awt.*; 15 | import java.awt.datatransfer.Clipboard; 16 | import java.awt.datatransfer.StringSelection; 17 | 18 | /** 19 | * Preview 右侧复制 20 | * 21 | * @author liuzhihang 22 | * @date 2021/10/23 22:31 23 | */ 24 | public class PreviewRightCopyAction extends AnAction { 25 | @Override 26 | public void actionPerformed(@NotNull AnActionEvent e) { 27 | 28 | // 获取当前project对象 29 | Project project = e.getData(PlatformDataKeys.PROJECT); 30 | JBPopup popup = e.getData(DocViewDataKeys.PREVIEW_POPUP); 31 | DocView currentDocView = e.getData(DocViewDataKeys.PREVIEW_CURRENT_DOC_VIEW); 32 | String currentMarkdownText = e.getData(DocViewDataKeys.PREVIEW_MARKDOWN_TEXT); 33 | 34 | if (popup == null || project == null || currentDocView == null || currentMarkdownText == null) { 35 | return; 36 | } 37 | 38 | StringSelection selection = new StringSelection(currentMarkdownText); 39 | Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); 40 | clipboard.setContents(selection, selection); 41 | 42 | DocViewNotification.notifyInfo(project, DocViewBundle.message("notify.copy.success", currentDocView.getName())); 43 | 44 | } 45 | 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/ui/SupportForm.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.ui; 2 | 3 | import com.intellij.ide.BrowserUtil; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.openapi.ui.DialogWrapper; 6 | import com.intellij.ui.components.labels.LinkLabel; 7 | import com.intellij.util.ui.JBUI; 8 | import com.liuzhihang.doc.view.DocViewBundle; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | import javax.swing.*; 13 | 14 | /** 15 | * @author liuzhihang 16 | * @date 2020/11/11 20:27 17 | */ 18 | public class SupportForm extends DialogWrapper { 19 | 20 | private JPanel rootPanel; 21 | private LinkLabel starLinkLabel; 22 | private LinkLabel reportLinkLabel; 23 | private LinkLabel discussionsLinkLabel; 24 | private LinkLabel websiteLinkLabel; 25 | 26 | public SupportForm(@NotNull Project project) { 27 | super(project, true); 28 | 29 | init(); 30 | 31 | starLinkLabel.setIcon(null); 32 | reportLinkLabel.setIcon(null); 33 | discussionsLinkLabel.setIcon(null); 34 | websiteLinkLabel.setIcon(null); 35 | 36 | rootPanel.setBorder(JBUI.Borders.empty(12, 15)); 37 | rootPanel.setBackground(UIManager.getColor("TextArea.background")); 38 | starLinkLabel.setListener((source, data) -> BrowserUtil.browse(data), DocViewBundle.message("github")); 39 | reportLinkLabel.setListener((source, data) -> BrowserUtil.browse(data), DocViewBundle.message("issues")); 40 | discussionsLinkLabel.setListener((source, data) -> BrowserUtil.browse(data), DocViewBundle.message("discussions")); 41 | websiteLinkLabel.setListener((source, data) -> BrowserUtil.browse(data), DocViewBundle.message("website")); 42 | 43 | } 44 | 45 | @Nullable 46 | @Override 47 | public JComponent createCenterPanel() { 48 | return rootPanel; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/utils/FeignPsiUtil.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.utils; 2 | 3 | import com.intellij.codeInsight.AnnotationUtil; 4 | import com.intellij.openapi.module.Module; 5 | import com.intellij.psi.PsiAnnotation; 6 | import com.intellij.psi.PsiClass; 7 | import com.intellij.psi.PsiElement; 8 | import com.intellij.psi.PsiModifierList; 9 | import com.intellij.psi.impl.java.stubs.index.JavaAnnotationIndex; 10 | import com.intellij.psi.search.GlobalSearchScope; 11 | import com.liuzhihang.doc.view.constant.SpringConstant; 12 | import org.jetbrains.annotations.NotNull; 13 | 14 | import java.util.Collection; 15 | import java.util.LinkedList; 16 | import java.util.List; 17 | 18 | /** 19 | * @author liuzhihang 20 | * @date 2021/10/23 17:38 21 | */ 22 | public class FeignPsiUtil { 23 | 24 | /** 25 | * 检查类或者接口是否是 Feign 接口 26 | * 27 | * @param psiClass 28 | * @return 29 | */ 30 | public static boolean isFeignClass(@NotNull PsiClass psiClass) { 31 | 32 | // 必须匹配是 Feign 注解才可以 33 | return psiClass.isInterface() && AnnotationUtil.isAnnotated(psiClass, SpringConstant.FEIGN_CLIENT, 0); 34 | } 35 | 36 | public static List findDocViewFromModule(Module module) { 37 | 38 | Collection psiAnnotations = JavaAnnotationIndex.getInstance().get("FeignClient", module.getProject(), GlobalSearchScope.moduleScope(module)); 39 | 40 | List psiClasses = new LinkedList<>(); 41 | 42 | for (PsiAnnotation psiAnnotation : psiAnnotations) { 43 | PsiModifierList psiModifierList = (PsiModifierList) psiAnnotation.getParent(); 44 | PsiElement psiElement = psiModifierList.getParent(); 45 | 46 | if (psiElement instanceof PsiClass && isFeignClass((PsiClass) psiElement)) { 47 | psiClasses.add((PsiClass) psiElement); 48 | } 49 | } 50 | return psiClasses; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/ui/ParamDocEditor.form: -------------------------------------------------------------------------------- 1 | 2 |

3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
41 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/ui/window/RootNode.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.ui.window; 2 | 3 | import com.intellij.icons.AllIcons; 4 | import com.intellij.openapi.module.Module; 5 | import com.intellij.openapi.module.ModuleManager; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.ui.treeStructure.SimpleNode; 8 | import com.liuzhihang.doc.view.dto.DocView; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Collection; 12 | import java.util.List; 13 | import java.util.stream.Collectors; 14 | 15 | /** 16 | * 根目录 17 | * 18 | * @author liuzhihang 19 | * @date 2022/4/4 16:56 20 | */ 21 | public class RootNode extends DocViewNode { 22 | 23 | private final List moduleNodes = new ArrayList<>(); 24 | 25 | public RootNode() { 26 | super(null); 27 | getTemplatePresentation().setIcon(AllIcons.Nodes.ModuleGroup); 28 | getTemplatePresentation().setPresentableText(getName()); 29 | } 30 | 31 | @Override 32 | public void updateNode(Project project) { 33 | cleanUpCache(); 34 | moduleNodes.clear(); 35 | 36 | Module[] modules = ModuleManager.getInstance(project).getModules(); 37 | for (Module module : modules) { 38 | ModuleNode moduleNode = new ModuleNode(this, module); 39 | if (moduleNode.getChildCount() > 0) { 40 | moduleNodes.add(moduleNode); 41 | } 42 | } 43 | update(); 44 | } 45 | 46 | @Override 47 | public String docPath(Project project) { 48 | return "Doc View"; 49 | } 50 | 51 | @Override 52 | public String httpPath(Project project) { 53 | return "Http"; 54 | } 55 | 56 | @Override 57 | protected SimpleNode[] buildChildren() { 58 | return moduleNodes.toArray(new SimpleNode[0]); 59 | } 60 | 61 | @Override 62 | public String getName() { 63 | return "Doc View"; 64 | } 65 | 66 | public List docViewList() { 67 | 68 | return moduleNodes.stream().map(ModuleNode::docViewList).flatMap(Collection::stream).collect(Collectors.toList()); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/ui/Preview.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 |
43 | -------------------------------------------------------------------------------- /src/main/resources/icons/json.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/resources/icons/json_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/ui/WindowFilterForm.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.ui; 2 | 3 | import com.intellij.openapi.module.Module; 4 | import com.intellij.openapi.module.ModuleManager; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.ui.IdeBorderFactory; 7 | import com.intellij.util.ui.JBUI; 8 | import com.liuzhihang.doc.view.config.WindowSettings; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | import javax.swing.*; 12 | import javax.swing.border.TitledBorder; 13 | 14 | /** 15 | * @author liuzhihang 16 | * @date 2021/10/25 12:11 17 | */ 18 | public class WindowFilterForm { 19 | 20 | private static final TitledBorder scope = IdeBorderFactory.createTitledBorder("读取范围"); 21 | private static final TitledBorder otherSettings = IdeBorderFactory.createTitledBorder("其他设置"); 22 | 23 | private final Project project; 24 | private final WindowSettings windowSettings; 25 | 26 | private JCheckBox interfaceCheckBox; 27 | private JComboBox scopeComboBox; 28 | private JPanel rootPane; 29 | private JPanel scopePanel; 30 | private JPanel otherSettingsPanel; 31 | 32 | public WindowFilterForm(@NotNull Project project) { 33 | this.project = project; 34 | this.windowSettings = WindowSettings.getInstance(project); 35 | 36 | initUI(); 37 | initFilterPane(); 38 | initFilterListener(); 39 | } 40 | 41 | private void initUI() { 42 | 43 | scopePanel.setBorder(scope); 44 | otherSettingsPanel.setBorder(otherSettings); 45 | rootPane.setBorder(JBUI.Borders.empty(5)); 46 | 47 | 48 | } 49 | 50 | private void initFilterPane() { 51 | 52 | interfaceCheckBox.setSelected(windowSettings.isIncludeInterface()); 53 | 54 | Module[] modules = ModuleManager.getInstance(project).getModules(); 55 | 56 | for (Module module : modules) { 57 | scopeComboBox.addItem(module.getName()); 58 | } 59 | 60 | } 61 | 62 | private void initFilterListener() { 63 | interfaceCheckBox.addChangeListener(e -> windowSettings.setIncludeInterface(interfaceCheckBox.isSelected())); 64 | scopeComboBox.addItemListener(e -> windowSettings.setScope((String) scopeComboBox.getSelectedItem())); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/icons/DocViewIcons.java: -------------------------------------------------------------------------------- 1 | package icons; 2 | 3 | import com.intellij.openapi.util.IconLoader; 4 | 5 | import javax.swing.*; 6 | 7 | /** 8 | * 图标管理: 图标地址 9 | * 10 | * @author liuzhihang 11 | * @date 2021/2/28 14:49 12 | */ 13 | public interface DocViewIcons { 14 | 15 | /** 16 | * 插件图标:在侧栏树勇 17 | */ 18 | Icon DOC_VIEW = IconLoader.getIcon("/icons/markdown.svg", DocViewIcons.class); 19 | 20 | /** 21 | * 清理缓存 22 | */ 23 | Icon CLEAR = IconLoader.getIcon("/icons/clearCash.svg", DocViewIcons.class); 24 | 25 | /** 26 | * 上传 27 | */ 28 | Icon UPLOAD = IconLoader.getIcon("/icons/upload.svg", DocViewIcons.class); 29 | 30 | /** 31 | * 下载 32 | */ 33 | Icon DOWNLOAD = IconLoader.getIcon("/icons/download.svg", DocViewIcons.class); 34 | 35 | /** 36 | * pin 37 | */ 38 | Icon PIN = IconLoader.getIcon("/icons/pin.svg", DocViewIcons.class); 39 | 40 | /** 41 | * editorPreview 42 | */ 43 | Icon EDITOR_PREVIEW = IconLoader.getIcon("/icons/editorPreview.svg", DocViewIcons.class); 44 | 45 | /** 46 | * copy 47 | */ 48 | Icon COPY = IconLoader.getIcon("/icons/copy.svg", DocViewIcons.class); 49 | 50 | /** 51 | * settings 52 | */ 53 | Icon SETTINGS = IconLoader.getIcon("/icons/settings.svg", DocViewIcons.class); 54 | 55 | /** 56 | * checked 57 | */ 58 | Icon CHECKED = IconLoader.getIcon("/icons/checked.svg", DocViewIcons.class); 59 | 60 | /** 61 | * json 62 | */ 63 | Icon JSON = IconLoader.getIcon("/icons/json.svg", DocViewIcons.class); 64 | 65 | /** 66 | * refresh 67 | */ 68 | Icon REFRESH = IconLoader.getIcon("/icons/refresh.svg", DocViewIcons.class); 69 | 70 | /** 71 | * export 72 | */ 73 | Icon EXPORT = IconLoader.getIcon("/icons/export.svg", DocViewIcons.class); 74 | 75 | /** 76 | * expand ALL 77 | */ 78 | Icon EXPAND_ALL = IconLoader.getIcon("/icons/expandAll.svg", DocViewIcons.class); 79 | 80 | /** 81 | * collapseAll 82 | */ 83 | Icon COLLAPSE_ALL = IconLoader.getIcon("/icons/collapseAll.svg", DocViewIcons.class); 84 | 85 | /** 86 | * httpAPI 87 | */ 88 | Icon HTTP_API = IconLoader.getIcon("/icons/httpAPI.svg", DocViewIcons.class); 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/constant/ValidationConstant.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.constant; 2 | 3 | /** 4 | * @author liuzhihang 5 | * @date 2020/3/4 22:34 6 | */ 7 | public final class ValidationConstant { 8 | 9 | private ValidationConstant() { 10 | } 11 | 12 | public static final String VALID = "javax.validation.Valid"; 13 | 14 | public static final String CONSTRAINTS = "javax.validation.constraints"; 15 | public static final String ASSERT_FALSE = "javax.validation.constraints.AssertFalse"; 16 | public static final String ASSERT_TRUE = "javax.validation.constraints.AssertTrue"; 17 | public static final String DECIMAL_MAX = "javax.validation.constraints.DecimalMax"; 18 | public static final String DECIMAL_MIN = "javax.validation.constraints.DecimalMin"; 19 | public static final String DIGITS = "javax.validation.constraints.Digits"; 20 | public static final String EMAIL = "javax.validation.constraints.Email"; 21 | public static final String FUTURE = "javax.validation.constraints.Future"; 22 | public static final String FUTURE_OR_PRESENT = "javax.validation.constraints.FutureOrPresent"; 23 | public static final String MAX = "javax.validation.constraints.Max"; 24 | public static final String MIN = "javax.validation.constraints.Min"; 25 | public static final String NEGATIVE = "javax.validation.constraints.Negative"; 26 | public static final String NEGATIVE_OR_ZERO = "javax.validation.constraints.NegativeOrZero"; 27 | public static final String NOT_BLANK = "javax.validation.constraints.NotBlank"; 28 | public static final String NOT_EMPTY = "javax.validation.constraints.NotEmpty"; 29 | public static final String NOT_NULL = "javax.validation.constraints.NotNull"; 30 | public static final String NULL = "javax.validation.constraints.Null"; 31 | public static final String PAST = "javax.validation.constraints.Past"; 32 | public static final String PAST_OR_PRESENT = "javax.validation.constraints.PastOrPresent"; 33 | public static final String PATTERN = "javax.validation.constraints.Pattern"; 34 | public static final String POSITIVE = "javax.validation.constraints.Positive"; 35 | public static final String POSITIVE_OR_ZERO = "javax.validation.constraints.PositiveOrZero"; 36 | public static final String SIZE = "javax.validation.constraints.Size"; 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/utils/GsonFormatUtil.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.utils; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import com.google.gson.JsonElement; 6 | import com.google.gson.JsonNull; 7 | import com.google.gson.internal.Streams; 8 | import com.google.gson.stream.JsonWriter; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | import java.io.IOException; 12 | import java.io.StringWriter; 13 | import java.io.Writer; 14 | import java.util.Collection; 15 | 16 | /** 17 | * 对 Gson 进行修改主要是修改 newJsonWriter中的缩进 18 | * 19 | * @author liuzhihang 20 | * @date 2019/5/9 11:07 21 | */ 22 | public class GsonFormatUtil { 23 | 24 | @NotNull 25 | public static String gsonFormat(Gson gson, JsonElement jsonElement) throws IOException { 26 | StringWriter writer = new StringWriter(); 27 | JsonWriter jsonWriter = newJsonWriter(Streams.writerForAppendable(Streams.writerForAppendable(writer))); 28 | gson.toJson(jsonElement, jsonWriter); 29 | return writer.toString(); 30 | } 31 | 32 | public static String gsonFormat(Object src) { 33 | 34 | Gson gson = new GsonBuilder().serializeNulls().create(); 35 | 36 | return gsonFormat(gson, src); 37 | } 38 | 39 | @NotNull 40 | public static String gsonFormat(Gson gson, Object src) { 41 | if (src == null) { 42 | return gson.toJson(JsonNull.INSTANCE); 43 | } 44 | try { 45 | StringWriter writer = new StringWriter(); 46 | JsonWriter jsonWriter = newJsonWriter(Streams.writerForAppendable(Streams.writerForAppendable(writer))); 47 | gson.toJson(src, src.getClass(), jsonWriter); 48 | return writer.toString(); 49 | } catch (IOException e) { 50 | if (src instanceof Collection) { 51 | return "[]"; 52 | } 53 | return "{}"; 54 | } 55 | } 56 | 57 | /** 58 | * 重新JsonWriter的缩进 59 | * 具体请查看相关源码 60 | * 61 | * @param writer 62 | * @return 63 | * @throws IOException 64 | * @see Gson#newJsonWriter(Writer) 65 | */ 66 | private static JsonWriter newJsonWriter(Writer writer) throws IOException { 67 | JsonWriter jsonWriter = new JsonWriter(writer); 68 | // 修改此处缩进为四个空格 69 | jsonWriter.setIndent(" "); 70 | jsonWriter.setSerializeNulls(false); 71 | return jsonWriter; 72 | } 73 | 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/action/toolbar/window/WindowUploadAction.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.action.toolbar.window; 2 | 3 | import com.intellij.openapi.actionSystem.AnActionEvent; 4 | import com.intellij.openapi.actionSystem.PlatformDataKeys; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.openapi.ui.popup.JBPopupFactory; 7 | import com.intellij.openapi.ui.popup.PopupStep; 8 | import com.intellij.openapi.ui.popup.util.BaseListPopupStep; 9 | import com.liuzhihang.doc.view.DocViewBundle; 10 | import com.liuzhihang.doc.view.action.toolbar.AbstractToolbarUploadAction; 11 | import com.liuzhihang.doc.view.data.DocViewDataKeys; 12 | import com.liuzhihang.doc.view.notification.DocViewNotification; 13 | import com.liuzhihang.doc.view.service.DocViewUploadService; 14 | import com.liuzhihang.doc.view.ui.window.RootNode; 15 | import org.jetbrains.annotations.NotNull; 16 | import org.jetbrains.annotations.Nullable; 17 | 18 | import javax.swing.*; 19 | 20 | /** 21 | * @author liuzhihang 22 | * @date 2021/10/23 19:55 23 | */ 24 | public class WindowUploadAction extends AbstractToolbarUploadAction { 25 | 26 | @Override 27 | public void actionPerformed(@NotNull AnActionEvent e) { 28 | 29 | // 获取当前project对象 30 | Project project = e.getData(PlatformDataKeys.PROJECT); 31 | JComponent toolbar = e.getData(DocViewDataKeys.WINDOW_TOOLBAR); 32 | RootNode rootNode = e.getData(DocViewDataKeys.WINDOW_ROOT_NODE); 33 | 34 | if (project == null || toolbar == null || rootNode == null || rootNode.getChildCount() == 0) { 35 | DocViewNotification.notifyError(project, DocViewBundle.message("notify.window.upload.empty")); 36 | return; 37 | } 38 | 39 | JBPopupFactory.getInstance() 40 | .createListPopup(new BaseListPopupStep<>(null, DocViewUploadService.UPLOAD_OPTIONS) { 41 | @Override 42 | public @NotNull String getTextFor(String value) { 43 | return "Upload to " + value; 44 | } 45 | 46 | @Override 47 | public @Nullable PopupStep onChosen(String selectedValue, boolean finalChoice) { 48 | 49 | DocViewUploadService.getInstance(selectedValue).upload(project, rootNode.docViewList()); 50 | 51 | return FINAL_CHOICE; 52 | } 53 | }).showUnderneathOf(toolbar); 54 | 55 | 56 | } 57 | 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/ui/ShowDocSettingForm.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.ui; 2 | 3 | import com.intellij.openapi.options.ConfigurationException; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.ui.IdeBorderFactory; 6 | import com.intellij.ui.components.JBTextField; 7 | import com.liuzhihang.doc.view.DocViewBundle; 8 | import com.liuzhihang.doc.view.config.ShowDocSettings; 9 | 10 | import javax.swing.*; 11 | 12 | public class ShowDocSettingForm { 13 | private JPanel rootPanel; 14 | 15 | private JPanel showDocProjectPanel; 16 | private JBTextField urlTextField; 17 | private JBTextField apiKeyTextField; 18 | private JBTextField apiTokenTextField; 19 | 20 | 21 | private final Project project; 22 | 23 | public ShowDocSettingForm(Project project) { 24 | this.project = project; 25 | showDocProjectPanel.setBorder(IdeBorderFactory.createTitledBorder(DocViewBundle.message("showdoc.project.panel"))); 26 | } 27 | 28 | public JPanel getRootPanel() { 29 | return rootPanel; 30 | } 31 | 32 | 33 | public boolean isModified() { 34 | 35 | ShowDocSettings settings = ShowDocSettings.getInstance(project); 36 | 37 | if (!urlTextField.getText().equals(settings.getUrl())) { 38 | return true; 39 | } 40 | if (!apiKeyTextField.getText().equals(settings.getApiKey())) { 41 | return true; 42 | } 43 | 44 | if (!apiTokenTextField.getText().equals(settings.getApiToken())) { 45 | return true; 46 | } 47 | 48 | return false; 49 | } 50 | 51 | public void apply() throws ConfigurationException { 52 | ShowDocSettings settings = ShowDocSettings.getInstance(project); 53 | String urlTextFieldText = urlTextField.getText(); 54 | 55 | if (urlTextFieldText.endsWith("/")) { 56 | settings.setUrl(urlTextFieldText.substring(0, urlTextFieldText.length() - 1)); 57 | } else { 58 | settings.setUrl(urlTextFieldText); 59 | } 60 | settings.setApiKey(apiKeyTextField.getText()); 61 | settings.setApiToken(apiTokenTextField.getText()); 62 | 63 | } 64 | 65 | public void reset() { 66 | 67 | ShowDocSettings settings = ShowDocSettings.getInstance(project); 68 | 69 | urlTextField.setText(settings.getUrl()); 70 | apiKeyTextField.setText(settings.getApiKey()); 71 | apiTokenTextField.setText(settings.getApiToken()); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Example user template template 3 | ### Example user template 4 | 5 | # IntelliJ project files 6 | .idea 7 | *.iml 8 | out 9 | gen 10 | ### Gradle template 11 | .gradle 12 | **/build/ 13 | !src/**/build/ 14 | 15 | # Ignore Gradle GUI config 16 | gradle-app.setting 17 | 18 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 19 | !gradle-wrapper.jar 20 | 21 | # Cache of project 22 | .gradletasknamecache 23 | 24 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 25 | # gradle/wrapper/gradle-wrapper.properties 26 | 27 | ### JetBrains template 28 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 29 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 30 | 31 | # User-specific stuff 32 | .idea/**/workspace.xml 33 | .idea/**/tasks.xml 34 | .idea/**/usage.statistics.xml 35 | .idea/**/dictionaries 36 | .idea/**/shelf 37 | 38 | # Generated files 39 | .idea/**/contentModel.xml 40 | 41 | # Sensitive or high-churn files 42 | .idea/**/dataSources/ 43 | .idea/**/dataSources.ids 44 | .idea/**/dataSources.local.xml 45 | .idea/**/sqlDataSources.xml 46 | .idea/**/dynamic.xml 47 | .idea/**/uiDesigner.xml 48 | .idea/**/dbnavigator.xml 49 | 50 | # Gradle 51 | .idea/**/gradle.xml 52 | .idea/**/libraries 53 | 54 | # Gradle and Maven with auto-import 55 | # When using Gradle or Maven with auto-import, you should exclude module files, 56 | # since they will be recreated, and may cause churn. Uncomment if using 57 | # auto-import. 58 | # .idea/artifacts 59 | # .idea/compiler.xml 60 | # .idea/jarRepositories.xml 61 | # .idea/modules.xml 62 | # .idea/*.iml 63 | # .idea/modules 64 | # *.iml 65 | # *.ipr 66 | 67 | # CMake 68 | cmake-build-*/ 69 | 70 | # Mongo Explorer plugin 71 | .idea/**/mongoSettings.xml 72 | 73 | # File-based project format 74 | *.iws 75 | 76 | # IntelliJ 77 | out/ 78 | 79 | # mpeltonen/sbt-idea plugin 80 | .idea_modules/ 81 | 82 | # JIRA plugin 83 | atlassian-ide-plugin.xml 84 | 85 | # Cursive Clojure plugin 86 | .idea/replstate.xml 87 | 88 | # Crashlytics plugin (for Android Studio and IntelliJ) 89 | com_crashlytics_export_strings.xml 90 | crashlytics.properties 91 | crashlytics-build.properties 92 | fabric.properties 93 | 94 | # Editor-based Rest Client 95 | .idea/httpRequests 96 | 97 | # Android studio 3.1+ serialized cache file 98 | .idea/caches/build_file_checksums.ser 99 | 100 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Doc View 2 | ======= 3 | 4 | [![JetBrains Plugins](https://img.shields.io/jetbrains/plugin/v/15305-doc-view.svg)](https://plugins.jetbrains.com/plugin/15305-doc-view) 5 | [![Version](http://phpstorm.espend.de/badge/15305/version)](https://plugins.jetbrains.com/plugin/15305-doc-view/versions) 6 | [![Downloads](https://img.shields.io/jetbrains/plugin/d/15305-doc-view.svg)](https://plugins.jetbrains.com/plugin/15305-doc-view) 7 | [![License](https://img.shields.io/badge/license-MIT-red.svg)](https://github.com/liuzhihang/toolkit/blob/master/LICENSE) 8 | 9 | 特征 10 | ---- 11 | 12 | - [x] Controller/Dubbo 接口文档生成 13 | - [x] 支持 validation、swagger 等注解 14 | - [x] Markdown 接口查看、预览、复制、导出 15 | - [x] 支持自定义生成接口的 Markdown 模版 16 | - [x] 支持界面编辑文档、注释、并同步保存到代码注释或 Swagger 注解中 17 | - [x] 支持在编辑实体界面, 将实体复制为 Json 字符串 18 | - [x] 支持上传文档到 YApi 19 | - [x] 支持上传文档到 ShowDoc 20 | - [x] 支持自定义配置 21 | 22 | 演示 23 | ---- 24 | 25 | ![](https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/122-zbZ9ps.gif) 26 | ![](https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/0YyY3m-CQdmD6.gif) 27 | 28 | [更多截图演示](https://github.com/liuzhihang/doc-view/discussions/17) 29 | 30 | 安装 31 | ---- 32 | 33 | - **在线安装:** 34 | - `File` -> `Setting` -> `Plugins` -> 搜索 `Doc View` 35 | 36 | - **手动安装:** 37 | - [下载插件](https://github.com/liuzhihang/doc-view/releases) -> `File` -> `Setting` -> `Plugins` 38 | -> `Install Plugin from Disk...` 39 | 40 | 使用 41 | ---- 42 | 43 | - 右键菜单选择 `Doc View` 44 | 45 | 更新 46 | ---- 47 | 48 | [查看历史更新记录](https://github.com/liuzhihang/doc-view/releases) 49 | 50 | 关于我 51 | ---- 52 | 53 | 欢迎关注公众号:『 程序员小航 』 54 | 55 | ![wechat-vxgNsq](https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/wechat-vxgNsq.png) 56 | 57 | 58 | 小伙伴们 59 | ---- 60 | 61 | 感谢以下小伙伴的参与: 62 | 63 | [lvgo](https://github.com/lvgocc) 64 | [知一](https://github.com/zh-d-d) 65 | [大斌](https://github.com/dabinaa) 66 | [ayang0422](https://github.com/ayang0422) 67 | 68 | 69 | 70 | 其他插件 71 | ---- 72 | 73 |  Toolkit: [https://github.com/liuzhihang/toolkit](https://github.com/liuzhihang/toolkit) 74 | 75 | 76 |  copy-as-json: [https://github.com/liuzhihang/copy-as-json](https://github.com/liuzhihang/copy-as-json) 77 | 78 | 本工具使用 JetBrains IDEA 进行开发 79 | ---- 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/integration/impl/YuQueFacadeServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.integration.impl; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import com.liuzhihang.doc.view.integration.YuQueFacadeService; 6 | import com.liuzhihang.doc.view.integration.dto.YuQueCreate; 7 | import com.liuzhihang.doc.view.integration.dto.YuQueResponse; 8 | import com.liuzhihang.doc.view.integration.dto.YuQueUpdate; 9 | import com.liuzhihang.doc.view.utils.HttpUtils; 10 | import org.apache.commons.lang3.StringUtils; 11 | import org.apache.http.Header; 12 | import org.apache.http.message.BasicHeader; 13 | 14 | import java.util.List; 15 | 16 | /** 17 | * @author liuzhihang 18 | * @date 2022/3/31 23:16 19 | */ 20 | public class YuQueFacadeServiceImpl implements YuQueFacadeService { 21 | 22 | private static final Gson gson = new GsonBuilder().serializeNulls().create(); 23 | 24 | @Override 25 | public YuQueResponse getDoc(String url, String token, String namespace, String slug) throws Exception { 26 | 27 | Header header = new BasicHeader("X-Auth-Token", token); 28 | 29 | 30 | String resp = null; 31 | try { 32 | resp = HttpUtils.get(url + "/repos/" + namespace + "/docs/" + slug, null, List.of(header)); 33 | } catch (RuntimeException e) { 34 | String message = e.getMessage(); 35 | if (message.contains("404") && message.contains("Not Found")) { 36 | return null; 37 | } 38 | } 39 | 40 | return gson.fromJson(resp, YuQueResponse.class); 41 | } 42 | 43 | @Override 44 | public YuQueResponse create(String url, String token, String namespace, YuQueCreate yuQueCreate) throws Exception { 45 | 46 | Header header = new BasicHeader("X-Auth-Token", token); 47 | 48 | String resp = HttpUtils.post(url + "/repos/" + namespace + "/docs/", gson.toJson(yuQueCreate), List.of(header)); 49 | 50 | if (StringUtils.isBlank(resp)) { 51 | throw new Exception("语雀接口返回为空"); 52 | } 53 | return gson.fromJson(resp, YuQueResponse.class); 54 | } 55 | 56 | @Override 57 | public YuQueResponse update(String url, String token, String namespace, Long id, YuQueUpdate yuQueUpdate) throws Exception { 58 | 59 | Header header = new BasicHeader("X-Auth-Token", token); 60 | 61 | String resp = HttpUtils.put(url + "/repos/" + namespace + "/docs/" + id, gson.toJson(yuQueUpdate), List.of(header)); 62 | 63 | if (StringUtils.isBlank(resp)) { 64 | throw new Exception("语雀接口返回为空"); 65 | } 66 | return gson.fromJson(resp, YuQueResponse.class); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/dto/DocView.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.dto; 2 | 3 | import com.intellij.psi.PsiClass; 4 | import com.intellij.psi.PsiMethod; 5 | import com.liuzhihang.doc.view.enums.ContentTypeEnum; 6 | import com.liuzhihang.doc.view.enums.FrameworkEnum; 7 | import lombok.Data; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * 文档参数 13 | * 14 | * @author liuzhihang 15 | * @date 2020/2/28 10:32 16 | */ 17 | @Data 18 | public class DocView { 19 | 20 | /** 21 | * 当前接口所在的类 22 | */ 23 | private PsiClass psiClass; 24 | 25 | /** 26 | * 当前接口的方法 27 | */ 28 | private PsiMethod psiMethod; 29 | 30 | /** 31 | * 文档标题, 方法所属的类 32 | */ 33 | private String docTitle; 34 | 35 | /** 36 | * 接口方法所属类的标签 37 | *

38 | * swagger @Api(tags = {"用户接口相关", "用户11"}) 39 | * swagger3 @Tags 40 | */ 41 | private String[] classTags; 42 | 43 | /** 44 | * 接口方法的标签 45 | *

46 | * swagger @ApiOperation(value = "查询用户", tags = "用户接口 xxx") 47 | * swagger3 @Operation(summary = "用户接口 1", tags = "测试") 48 | */ 49 | private String[] tags; 50 | 51 | /** 52 | * 文档名称 53 | */ 54 | private String name; 55 | 56 | /** 57 | * 文档描述 58 | */ 59 | private String desc; 60 | 61 | /** 62 | * 环境地址 63 | */ 64 | private List domain; 65 | 66 | /** 67 | * 接口地址 68 | */ 69 | private String path; 70 | 71 | /** 72 | * 请求方式 GET POST PUT DELETE HEAD OPTIONS PATCH 73 | */ 74 | private String method; 75 | 76 | /** 77 | * 变动说明 78 | */ 79 | private String changeLog; 80 | 81 | /** 82 | * headers 83 | */ 84 | private List

headerList; 85 | 86 | /** 87 | * 请求参数 88 | */ 89 | private Body reqBody = new Body(); 90 | 91 | /** 92 | * 返回参数 93 | */ 94 | private Body respBody = new Body(); 95 | 96 | /** 97 | * 请求参数 98 | */ 99 | private List reqParamList; 100 | 101 | /** 102 | * body 参数 103 | */ 104 | private String reqBodyExample; 105 | 106 | /** 107 | * form 参数 108 | */ 109 | private String reqFormExample; 110 | 111 | /** 112 | * 请求参数类型 json/form 113 | */ 114 | private ContentTypeEnum contentType; 115 | 116 | /** 117 | * 返回参数 118 | */ 119 | private String respExample; 120 | 121 | /** 122 | * 备注 123 | */ 124 | private String remark; 125 | 126 | private FrameworkEnum type; 127 | 128 | public DocView(String name) { 129 | this.name = name; 130 | } 131 | 132 | public DocView() { 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/action/toolbar/preview/PreviewRightUploadAction.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.action.toolbar.preview; 2 | 3 | import com.intellij.openapi.actionSystem.AnActionEvent; 4 | import com.intellij.openapi.actionSystem.PlatformDataKeys; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.openapi.ui.popup.JBPopup; 7 | import com.intellij.openapi.ui.popup.JBPopupFactory; 8 | import com.intellij.openapi.ui.popup.PopupStep; 9 | import com.intellij.openapi.ui.popup.util.BaseListPopupStep; 10 | import com.liuzhihang.doc.view.action.toolbar.AbstractToolbarUploadAction; 11 | import com.liuzhihang.doc.view.data.DocViewDataKeys; 12 | import com.liuzhihang.doc.view.dto.DocView; 13 | import com.liuzhihang.doc.view.service.DocViewUploadService; 14 | import com.liuzhihang.doc.view.ui.PreviewForm; 15 | import org.jetbrains.annotations.NotNull; 16 | import org.jetbrains.annotations.Nullable; 17 | 18 | import javax.swing.*; 19 | import java.awt.*; 20 | 21 | /** 22 | * Preview 右侧单个上传 23 | * 24 | * @author liuzhihang 25 | * @date 2021/10/23 22:31 26 | */ 27 | public class PreviewRightUploadAction extends AbstractToolbarUploadAction { 28 | @Override 29 | public void actionPerformed(@NotNull AnActionEvent e) { 30 | 31 | // 获取当前project对象 32 | Project project = e.getData(PlatformDataKeys.PROJECT); 33 | JBPopup popup = e.getData(DocViewDataKeys.PREVIEW_POPUP); 34 | DocView currentDocView = e.getData(DocViewDataKeys.PREVIEW_CURRENT_DOC_VIEW); 35 | JPanel previewToolbarPanel = e.getData(DocViewDataKeys.PREVIEW_TOOLBAR_PANEL); 36 | 37 | 38 | if (popup == null || project == null || currentDocView == null || previewToolbarPanel == null) { 39 | return; 40 | } 41 | 42 | Point location = previewToolbarPanel.getLocationOnScreen(); 43 | location.x = MouseInfo.getPointerInfo().getLocation().x; 44 | location.y += previewToolbarPanel.getHeight(); 45 | 46 | PreviewForm.myIsPinned.set(true); 47 | 48 | JBPopupFactory.getInstance() 49 | .createListPopup(new BaseListPopupStep<>(null, DocViewUploadService.UPLOAD_OPTIONS) { 50 | 51 | @Override 52 | public @NotNull String getTextFor(String value) { 53 | return "Upload to " + value; 54 | } 55 | 56 | @Override 57 | public @Nullable PopupStep onChosen(String selectedValue, boolean finalChoice) { 58 | 59 | DocViewUploadService.getInstance(selectedValue).doUpload(project, currentDocView); 60 | 61 | return FINAL_CHOICE; 62 | } 63 | }).showInScreenCoordinates(previewToolbarPanel, location); 64 | 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/dom/DubboDefinitionSearch.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.dom; 2 | 3 | import com.intellij.openapi.application.QueryExecutorBase; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.psi.PsiClass; 6 | import com.intellij.psi.PsiElement; 7 | import com.intellij.psi.PsiTypeParameterListOwner; 8 | import com.intellij.psi.search.GlobalSearchScope; 9 | import com.intellij.psi.search.searches.DefinitionsScopedSearch; 10 | import com.intellij.psi.xml.XmlElement; 11 | import com.intellij.util.Processor; 12 | import com.intellij.util.xml.DomElement; 13 | import com.intellij.util.xml.DomFileElement; 14 | import com.intellij.util.xml.DomService; 15 | import org.jetbrains.annotations.NotNull; 16 | 17 | import java.util.List; 18 | import java.util.Objects; 19 | 20 | /** 21 | * 加载所有 xml 中的 dubbo 接口 22 | * 23 | * @author liuzhihang 24 | * @date 2022-04-12 00:05:11 25 | */ 26 | public class DubboDefinitionSearch extends QueryExecutorBase { 27 | 28 | public DubboDefinitionSearch() { 29 | super(true); 30 | } 31 | 32 | @Override 33 | public void processQuery(@NotNull DefinitionsScopedSearch.SearchParameters parameters, 34 | @NotNull Processor consumer) { 35 | 36 | PsiElement element = parameters.getElement(); 37 | 38 | if (element instanceof PsiTypeParameterListOwner) { 39 | Processor processor = domElement -> consumer.process(domElement.getXmlElement()); 40 | if (element instanceof PsiClass) { 41 | PsiClass psiClass = (PsiClass) element; 42 | Project project = psiClass.getProject(); 43 | // 当前项目的所有元素 beans 44 | List> fileElements = DomService.getInstance() 45 | .getFileElements(BeansDomElement.class, project, GlobalSearchScope.allScope(project)); 46 | // 只需要判断 interface 47 | String qualifiedName = psiClass.getQualifiedName(); 48 | 49 | for (DomFileElement beansDomFileElement : fileElements) { 50 | BeansDomElement rootElement = beansDomFileElement.getRootElement(); 51 | 52 | for (DubboServiceDomElement dubboServiceDomElement : rootElement.getDubboServiceDomElements()) { 53 | String interfaceQualifiedName = dubboServiceDomElement.getInterface().getStringValue(); 54 | if (Objects.equals(qualifiedName, interfaceQualifiedName)) { 55 | processor.process(dubboServiceDomElement); 56 | } 57 | } 58 | } 59 | } 60 | } 61 | 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/resources/icons/settings.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/icons/settings_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/integration/impl/YApiFacadeServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.integration.impl; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import com.google.gson.JsonObject; 6 | import com.google.gson.reflect.TypeToken; 7 | import com.liuzhihang.doc.view.integration.YApiFacadeService; 8 | import com.liuzhihang.doc.view.integration.dto.YApiCat; 9 | import com.liuzhihang.doc.view.integration.dto.YApiResponse; 10 | import com.liuzhihang.doc.view.integration.dto.YapiSave; 11 | import com.liuzhihang.doc.view.utils.HttpUtils; 12 | import lombok.extern.slf4j.Slf4j; 13 | import org.apache.commons.lang3.StringUtils; 14 | 15 | import java.lang.reflect.Type; 16 | import java.util.List; 17 | 18 | /** 19 | * @author liuzhihang 20 | * @date 2021/6/8 19:20 21 | */ 22 | @Slf4j 23 | public class YApiFacadeServiceImpl implements YApiFacadeService { 24 | 25 | private static final Gson gson = new GsonBuilder().serializeNulls().create(); 26 | 27 | @Override 28 | public void save(YapiSave save) throws Exception { 29 | 30 | String resp = HttpUtils.post(save.getYapiUrl() + "/api/interface/save", gson.toJson(save)); 31 | 32 | if (StringUtils.isBlank(resp)) { 33 | throw new Exception("YApi 接口返回为空"); 34 | } 35 | 36 | JsonObject jsonObject = gson.fromJson(resp, JsonObject.class); 37 | 38 | if (jsonObject.get("errcode").getAsInt() != 0) { 39 | throw new Exception("YApi 接口返回失败:" + resp); 40 | } 41 | } 42 | 43 | @Override 44 | public List getCatMenu(String yapiUrl, Long projectId, String token) throws Exception { 45 | 46 | String url = yapiUrl + "/api/interface/getCatMenu" + 47 | "?project_id=" + projectId + 48 | "&token=" + token; 49 | 50 | String resp = HttpUtils.get(url); 51 | 52 | Type jsonType = new TypeToken>>() { 53 | }.getType(); 54 | 55 | YApiResponse> response = gson.fromJson(resp, jsonType); 56 | 57 | if (response.getErrcode() != 0) { 58 | throw new Exception("YApi 接口返回失败:" + resp); 59 | } 60 | return response.getData(); 61 | } 62 | 63 | @Override 64 | public YApiCat addCat(YApiCat cat) throws Exception { 65 | 66 | String resp = HttpUtils.post(cat.getYapiUrl() + "/api/interface/add_cat", gson.toJson(cat)); 67 | 68 | if (StringUtils.isBlank(resp)) { 69 | throw new Exception("YApi 接口返回为空"); 70 | } 71 | 72 | Type jsonType = new TypeToken>() { 73 | }.getType(); 74 | 75 | YApiResponse response = gson.fromJson(resp, jsonType); 76 | 77 | if (response == null || response.getErrcode() != 0) { 78 | throw new Exception("YApi 接口返回失败:" + resp); 79 | } 80 | return response.getData(); 81 | 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/ui/window/MethodNode.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.ui.window; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.psi.PsiClass; 5 | import com.intellij.psi.PsiMethod; 6 | import com.intellij.ui.treeStructure.SimpleNode; 7 | import com.intellij.ui.treeStructure.SimpleTree; 8 | import com.liuzhihang.doc.view.dto.DocView; 9 | import com.liuzhihang.doc.view.service.DocViewService; 10 | import com.liuzhihang.doc.view.utils.CustomFileUtils; 11 | import com.liuzhihang.doc.view.utils.DocViewUtils; 12 | 13 | import java.awt.event.InputEvent; 14 | import java.util.Collections; 15 | import java.util.List; 16 | 17 | /** 18 | * 目录树上的一个节点 19 | * 20 | * @author liuzhihang 21 | * @date 2022/4/4 22 | */ 23 | public class MethodNode extends DocViewNode { 24 | 25 | private final PsiMethod psiMethod; 26 | private final PsiClass psiClass; 27 | 28 | protected MethodNode(SimpleNode aParent, PsiClass psiClass, PsiMethod psiMethod) { 29 | super(aParent); 30 | this.psiMethod = psiMethod; 31 | this.psiClass = psiClass; 32 | 33 | getTemplatePresentation().setIcon(null); 34 | getTemplatePresentation().setTooltip(DocViewUtils.getMethodDesc(psiMethod)); 35 | } 36 | 37 | 38 | @Override 39 | protected SimpleNode[] buildChildren() { 40 | return new SimpleNode[0]; 41 | } 42 | 43 | @Override 44 | public String getName() { 45 | return DocViewUtils.getName(psiMethod); 46 | } 47 | 48 | @Override 49 | public List docViewList() { 50 | DocViewService service = DocViewService.getInstance(psiClass.getProject(), psiClass); 51 | return Collections.singletonList(service.buildClassMethodDoc(psiClass, psiMethod)); 52 | 53 | } 54 | 55 | @Override 56 | public void updateNode(Project project) { 57 | 58 | } 59 | 60 | @Override 61 | public String docPath(Project project) { 62 | 63 | ClassNode classNode = (ClassNode) getParent(); 64 | 65 | return classNode.docPath(project) + "/" + DocViewUtils.getName(psiMethod) + ".md"; 66 | 67 | } 68 | 69 | @Override 70 | public String httpPath(Project project) { 71 | 72 | ClassNode classNode = (ClassNode) getParent(); 73 | 74 | return classNode.httpPath(project) + "/" + DocViewUtils.getName(psiMethod) + ".http"; 75 | } 76 | 77 | @Override 78 | public void handleSelection(SimpleTree tree) { 79 | super.handleSelection(tree); 80 | } 81 | 82 | /** 83 | * 被双击或者 Enter 时, 双击打开文档 84 | * 85 | * @param tree 86 | * @param inputEvent 87 | */ 88 | @Override 89 | public void handleDoubleClickOrEnter(SimpleTree tree, InputEvent inputEvent) { 90 | CustomFileUtils.openMd(psiClass.getProject(), this); 91 | } 92 | 93 | public PsiMethod getPsiMethod() { 94 | return psiMethod; 95 | } 96 | 97 | public PsiClass getPsiClass() { 98 | return psiClass; 99 | } 100 | 101 | } -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/integration/dto/YapiSave.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.integration.dto; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import lombok.Data; 5 | 6 | import java.io.Serializable; 7 | import java.util.List; 8 | 9 | /** 10 | * 新增或上传对象 11 | *

12 | * https://hellosean1025.github.io/yapi/openapi.html 13 | * 14 | * @author liuzhihang 15 | * @date 2021/6/8 20:11 16 | */ 17 | @Data 18 | public class YapiSave implements Serializable { 19 | 20 | /** 21 | * 项目信息 22 | */ 23 | private String yapiUrl; 24 | 25 | /** 26 | * 项目 token 27 | */ 28 | private String token; 29 | 30 | /** 31 | * 项目 id 32 | */ 33 | private Long projectId; 34 | 35 | /** 36 | * 接口 id 37 | */ 38 | private String id; 39 | 40 | /** 41 | * 品类id 42 | */ 43 | @SerializedName("catid") 44 | private Long catId; 45 | 46 | /** 47 | * 请求路径 48 | */ 49 | private String path; 50 | /** 51 | * 请求方式 52 | */ 53 | private String method; 54 | 55 | /** 56 | * 请求数据类型 57 | * 枚举: raw,form,json 58 | */ 59 | @SerializedName("req_body_type") 60 | private String reqBodyType; 61 | 62 | /** 63 | * 请求数据body 64 | */ 65 | @SerializedName("req_body_other") 66 | private String reqBodyOther; 67 | /** 68 | * 请求参数body 是否为json_schema 69 | */ 70 | @SerializedName("req_body_is_json_schema") 71 | private boolean reqBodyIsJsonSchema; 72 | 73 | 74 | /** 75 | * 请求参数 form 类型 76 | */ 77 | @SerializedName("req_body_form") 78 | private List reqBodyForm; 79 | 80 | /** 81 | * 请求参数 82 | */ 83 | @SerializedName("req_params") 84 | private List reqParams; 85 | 86 | /** 87 | * 请求参数 88 | */ 89 | @SerializedName("req_headers") 90 | private List reqHeaders; 91 | 92 | /** 93 | * 请求参数 94 | */ 95 | @SerializedName("req_query") 96 | private List reqQuery; 97 | 98 | /** 99 | * 返回参数类型 json 100 | * 枚举: raw,json 101 | */ 102 | @SerializedName("res_body_type") 103 | private String resBodyType = "json"; 104 | 105 | /** 106 | * 返回参数 107 | */ 108 | @SerializedName("res_body") 109 | private String resBody; 110 | 111 | 112 | /** 113 | * 文档描述 114 | */ 115 | private String desc = ""; 116 | 117 | /** 118 | * 标题 119 | */ 120 | private String title; 121 | /** 122 | * 邮件开关 123 | */ 124 | @SerializedName("switch_notice") 125 | private Boolean switchNotice = false; 126 | /** 127 | * 状态 undone,默认done 128 | */ 129 | private String status = "undone"; 130 | 131 | 132 | /** 133 | * 返回参数是否为json_schema 134 | */ 135 | @SerializedName("res_body_is_json_schema") 136 | private boolean resBodyIsJsonSchema = true; 137 | 138 | /** 139 | * 备注信息 140 | */ 141 | private String markdown; 142 | 143 | 144 | } 145 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/ui/window/ClassNode.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.ui.window; 2 | 3 | import com.intellij.icons.AllIcons; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.psi.PsiClass; 6 | import com.intellij.psi.PsiMethod; 7 | import com.intellij.ui.treeStructure.SimpleNode; 8 | import com.intellij.ui.treeStructure.SimpleTree; 9 | import com.liuzhihang.doc.view.dto.DocView; 10 | import com.liuzhihang.doc.view.utils.DocViewUtils; 11 | 12 | import java.awt.event.InputEvent; 13 | import java.util.ArrayList; 14 | import java.util.Collection; 15 | import java.util.List; 16 | import java.util.stream.Collectors; 17 | 18 | /** 19 | * 目录树上的一个节点 20 | * 21 | * @author liuzhihang 22 | * @date 2022/4/4 23 | */ 24 | public class ClassNode extends DocViewNode { 25 | 26 | private final List methodNodes = new ArrayList<>(); 27 | private final PsiClass psiClass; 28 | 29 | protected ClassNode(SimpleNode aParent, PsiClass psiClass) { 30 | super(aParent); 31 | this.psiClass = psiClass; 32 | 33 | getTemplatePresentation().setIcon(psiClass.isInterface() ? AllIcons.Nodes.Interface : AllIcons.Nodes.Class); 34 | getTemplatePresentation().setTooltip(DocViewUtils.getTitle(psiClass)); 35 | updateNode(psiClass.getProject()); 36 | } 37 | 38 | public void updateNode(Project project) { 39 | 40 | PsiMethod[] methods = psiClass.getMethods(); 41 | 42 | for (PsiMethod psiMethod : methods) { 43 | if (DocViewUtils.isDocViewMethod(psiMethod)) { 44 | MethodNode methodNode = new MethodNode(this, psiClass, psiMethod); 45 | methodNodes.add(methodNode); 46 | } 47 | } 48 | update(); 49 | } 50 | 51 | @Override 52 | public String docPath(Project project) { 53 | 54 | ModuleNode moduleNode = (ModuleNode) getParent(); 55 | 56 | return moduleNode.docPath(project) + "/" + DocViewUtils.getTitle(psiClass); 57 | } 58 | 59 | @Override 60 | public String httpPath(Project project) { 61 | 62 | ModuleNode moduleNode = (ModuleNode) getParent(); 63 | 64 | return moduleNode.httpPath(project) + "/" + DocViewUtils.getTitle(psiClass); 65 | } 66 | 67 | @Override 68 | protected SimpleNode[] buildChildren() { 69 | return methodNodes.toArray(new SimpleNode[0]); 70 | } 71 | 72 | @Override 73 | public String getName() { 74 | return DocViewUtils.getTitle(psiClass); 75 | } 76 | 77 | @Override 78 | public List docViewList() { 79 | return methodNodes.stream().map(MethodNode::docViewList).flatMap(Collection::stream).collect(Collectors.toList()); 80 | } 81 | 82 | @Override 83 | public void handleSelection(SimpleTree tree) { 84 | super.handleSelection(tree); 85 | } 86 | 87 | /** 88 | * 被双击或者 Enter 时 89 | * 90 | * @param tree 91 | * @param inputEvent 92 | */ 93 | @Override 94 | public void handleDoubleClickOrEnter(SimpleTree tree, InputEvent inputEvent) { 95 | super.handleDoubleClickOrEnter(tree, inputEvent); 96 | } 97 | } -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/action/EditorAction.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.action; 2 | 3 | import com.intellij.openapi.actionSystem.*; 4 | import com.intellij.openapi.editor.Editor; 5 | import com.intellij.openapi.project.DumbService; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.psi.PsiClass; 8 | import com.intellij.psi.PsiFile; 9 | import com.liuzhihang.doc.view.DocViewBundle; 10 | import com.liuzhihang.doc.view.notification.DocViewNotification; 11 | import com.liuzhihang.doc.view.ui.ParamDocEditorForm; 12 | import com.liuzhihang.doc.view.utils.CustomPsiUtils; 13 | import com.liuzhihang.doc.view.utils.DocViewUtils; 14 | import org.jetbrains.annotations.NotNull; 15 | 16 | /** 17 | * 编辑实体类字段注释 18 | * 19 | * @author liuzhihang 20 | * @date 2020/2/26 21:57 21 | */ 22 | public class EditorAction extends AbstractAction { 23 | 24 | /** 25 | * @see AnAction#actionPerformed(AnActionEvent) 26 | */ 27 | @Override 28 | public void actionPerformed(AnActionEvent e) { 29 | 30 | // 获取当前project对象 31 | Project project = e.getData(PlatformDataKeys.PROJECT); 32 | // 获取当前编辑的文件, 可以进而获取 PsiClass, PsiField 对象 33 | PsiFile psiFile = e.getData(CommonDataKeys.PSI_FILE); 34 | Editor editor = e.getData(CommonDataKeys.EDITOR); 35 | 36 | if (editor == null || project == null || psiFile == null || DumbService.isDumb(project)) { 37 | return; 38 | } 39 | 40 | // 获取Java类或者接口 41 | PsiClass targetClass = CustomPsiUtils.getTargetClass(editor, psiFile); 42 | 43 | if (targetClass == null || targetClass.isAnnotationType() || targetClass.isEnum()) { 44 | DocViewNotification.notifyError(project, DocViewBundle.message("notify.error.class")); 45 | return; 46 | } 47 | 48 | ParamDocEditorForm.getInstance(project, psiFile, editor, targetClass).popup(); 49 | } 50 | 51 | /** 52 | * 设置右键菜单是否隐藏 Doc View 53 | * 54 | * @param e 55 | */ 56 | @Override 57 | public void update(@NotNull AnActionEvent e) { 58 | 59 | Project project = e.getData(PlatformDataKeys.PROJECT); 60 | PsiFile psiFile = e.getData(CommonDataKeys.PSI_FILE); 61 | Editor editor = e.getData(CommonDataKeys.EDITOR); 62 | 63 | Presentation presentation = e.getPresentation(); 64 | 65 | if (editor == null || project == null || psiFile == null || DumbService.isDumb(project)) { 66 | presentation.setEnabledAndVisible(false); 67 | return; 68 | } 69 | 70 | PsiClass targetClass = CustomPsiUtils.getTargetClass(editor, psiFile); 71 | 72 | if (targetClass == null || targetClass.isAnnotationType() || targetClass.isInterface() || targetClass.isEnum()) { 73 | presentation.setEnabledAndVisible(false); 74 | return; 75 | } 76 | 77 | // 判断是否是 Doc View 类,是的话 隐藏 78 | if (DocViewUtils.isDocViewClass(targetClass)) { 79 | presentation.setEnabledAndVisible(false); 80 | return; 81 | } 82 | 83 | presentation.setEnabledAndVisible(true); 84 | } 85 | 86 | @Override 87 | public @NotNull ActionUpdateThread getActionUpdateThread() { 88 | return ActionUpdateThread.BGT; 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/service/impl/WriterService.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.service.impl; 2 | 3 | import com.intellij.openapi.command.WriteCommandAction; 4 | import com.intellij.openapi.diagnostic.Logger; 5 | import com.intellij.openapi.editor.Editor; 6 | import com.intellij.openapi.editor.EditorModificationUtil; 7 | import com.intellij.openapi.project.Project; 8 | import com.intellij.psi.PsiElement; 9 | import com.intellij.psi.PsiJavaDocumentedElement; 10 | import com.intellij.psi.codeStyle.CodeStyleManager; 11 | import com.intellij.psi.javadoc.PsiDocComment; 12 | import com.intellij.util.ThrowableRunnable; 13 | import org.apache.commons.lang3.StringUtils; 14 | 15 | /** 16 | * @author wangchao 17 | * @date 2019/08/25 18 | */ 19 | public class WriterService { 20 | private static final Logger LOGGER = Logger.getInstance(WriterService.class); 21 | 22 | public void write(Project project, PsiElement psiElement, PsiDocComment comment) { 23 | try { 24 | WriteCommandAction.writeCommandAction(project).run( 25 | (ThrowableRunnable) () -> { 26 | if (psiElement.getContainingFile() == null) { 27 | return; 28 | } 29 | 30 | // 写入文档注释 31 | if (psiElement instanceof PsiJavaDocumentedElement) { 32 | PsiDocComment psiDocComment = ((PsiJavaDocumentedElement) psiElement).getDocComment(); 33 | if (psiDocComment == null) { 34 | psiElement.getNode().addChild(comment.getNode(), psiElement.getFirstChild().getNode()); 35 | } else { 36 | psiDocComment.replace(comment); 37 | } 38 | } 39 | 40 | // 格式化文档注释 41 | CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(psiElement.getProject()); 42 | PsiElement javadocElement = psiElement.getFirstChild(); 43 | int startOffset = javadocElement.getTextOffset(); 44 | int endOffset = javadocElement.getTextOffset() + javadocElement.getText().length(); 45 | codeStyleManager.reformatText(psiElement.getContainingFile(), startOffset, endOffset + 1); 46 | }); 47 | } catch (Throwable throwable) { 48 | LOGGER.error("写入错误", throwable); 49 | } 50 | } 51 | 52 | public void write(Project project, Editor editor, String text) { 53 | if (project == null || editor == null || StringUtils.isBlank(text)) { 54 | return; 55 | } 56 | try { 57 | WriteCommandAction.writeCommandAction(project).run( 58 | (ThrowableRunnable) () -> { 59 | int start = editor.getSelectionModel().getSelectionStart(); 60 | EditorModificationUtil.insertStringAtCaret(editor, text); 61 | editor.getSelectionModel().setSelection(start, start + text.length()); 62 | }); 63 | } catch (Throwable throwable) { 64 | LOGGER.error("写入错误", throwable); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/ui/YApiSettingForm.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.ui; 2 | 3 | import com.intellij.openapi.options.ConfigurationException; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.ui.IdeBorderFactory; 6 | import com.intellij.ui.components.JBTextField; 7 | import com.liuzhihang.doc.view.DocViewBundle; 8 | import com.liuzhihang.doc.view.config.YApiSettings; 9 | import com.liuzhihang.doc.view.notification.DocViewNotification; 10 | 11 | import javax.swing.*; 12 | import java.util.regex.Pattern; 13 | 14 | public class YApiSettingForm { 15 | private JPanel rootPanel; 16 | 17 | private JPanel yapiProjectPanel; 18 | private JBTextField urlTextField; 19 | private JBTextField projectIdTextField; 20 | private JBTextField tokenTextField; 21 | 22 | 23 | private final Project project; 24 | 25 | public YApiSettingForm(Project project) { 26 | this.project = project; 27 | yapiProjectPanel.setBorder(IdeBorderFactory.createTitledBorder(DocViewBundle.message("yapi.project.panel"))); 28 | } 29 | 30 | public JPanel getRootPanel() { 31 | return rootPanel; 32 | } 33 | 34 | 35 | public boolean isModified() { 36 | 37 | YApiSettings settings = YApiSettings.getInstance(project); 38 | 39 | if (!urlTextField.getText().equals(settings.getUrl())) { 40 | return true; 41 | } 42 | 43 | String projectId = projectIdTextField.getText().trim(); 44 | 45 | 46 | if (!Pattern.compile("^?[0-9]+").matcher(projectId).matches()) { 47 | return false; 48 | } 49 | 50 | if (Long.parseLong(projectId) != settings.getProjectId()) { 51 | return true; 52 | } 53 | 54 | if (!tokenTextField.getText().equals(settings.getToken())) { 55 | return true; 56 | } 57 | 58 | return false; 59 | } 60 | 61 | public void apply() throws ConfigurationException { 62 | try { 63 | YApiSettings settings = YApiSettings.getInstance(project); 64 | String urlTextFieldText = urlTextField.getText(); 65 | 66 | if (urlTextFieldText.endsWith("/")) { 67 | settings.setUrl(urlTextFieldText.substring(0, urlTextFieldText.length() - 1)); 68 | } else { 69 | settings.setUrl(urlTextFieldText); 70 | } 71 | 72 | String projectId = projectIdTextField.getText().trim(); 73 | if (Pattern.compile("^?[0-9]+").matcher(projectId).matches()) { 74 | settings.setProjectId(Long.parseLong(projectId)); 75 | } else { 76 | DocViewNotification.notifyError(project, DocViewBundle.message("notify.yapi.project.id")); 77 | } 78 | 79 | settings.setToken(tokenTextField.getText()); 80 | } catch (NumberFormatException e) { 81 | throw new ConfigurationException(DocViewBundle.message("notify.yapi.project.id")); 82 | } 83 | } 84 | 85 | public void reset() { 86 | YApiSettings settings = YApiSettings.getInstance(project); 87 | urlTextField.setText(settings.getUrl()); 88 | if (settings.getProjectId() != null) { 89 | projectIdTextField.setText(settings.getProjectId().toString()); 90 | } 91 | tokenTextField.setText(settings.getToken()); 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/ui/YuQueSettingForm.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.ui; 2 | 3 | import com.intellij.openapi.options.ConfigurationException; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.ui.IdeBorderFactory; 6 | import com.intellij.ui.components.JBTextField; 7 | import com.liuzhihang.doc.view.DocViewBundle; 8 | import com.liuzhihang.doc.view.config.YuQueSettings; 9 | 10 | import javax.swing.*; 11 | 12 | /** 13 | * @author liuzhihang 14 | * @date 2022/4/3 11:25 15 | */ 16 | public class YuQueSettingForm { 17 | private JPanel rootPanel; 18 | private JPanel yuQueProjectPanel; 19 | private JBTextField urlTextField; 20 | private JBTextField loginTextField; 21 | private JBTextField reposTextField; 22 | private JBTextField tokenTextField; 23 | private JBTextField apiUrlTextField; 24 | 25 | 26 | private final Project project; 27 | 28 | public YuQueSettingForm(Project project) { 29 | this.project = project; 30 | yuQueProjectPanel.setBorder(IdeBorderFactory.createTitledBorder(DocViewBundle.message("yapi.project.panel"))); 31 | } 32 | 33 | public JPanel getRootPanel() { 34 | return rootPanel; 35 | } 36 | 37 | 38 | public boolean isModified() { 39 | 40 | YuQueSettings settings = YuQueSettings.getInstance(project); 41 | 42 | if (!urlTextField.getText().trim().equals(settings.getUrl())) { 43 | return true; 44 | } 45 | if (!apiUrlTextField.getText().trim().equals(settings.getApiUrl())) { 46 | return true; 47 | } 48 | 49 | if (!loginTextField.getText().trim().equals(settings.getLogin())) { 50 | return true; 51 | } 52 | 53 | if (!reposTextField.getText().trim().equals(settings.getRepos())) { 54 | return true; 55 | } 56 | 57 | if (!tokenTextField.getText().trim().equals(settings.getToken())) { 58 | return true; 59 | } 60 | 61 | return false; 62 | } 63 | 64 | public void apply() throws ConfigurationException { 65 | YuQueSettings settings = YuQueSettings.getInstance(project); 66 | String urlTextFieldText = urlTextField.getText().trim(); 67 | 68 | if (urlTextFieldText.endsWith("/")) { 69 | settings.setUrl(urlTextFieldText.substring(0, urlTextFieldText.length() - 1)); 70 | } else { 71 | settings.setUrl(urlTextFieldText); 72 | } 73 | String apiUrlTextFieldText = apiUrlTextField.getText().trim(); 74 | 75 | if (apiUrlTextFieldText.endsWith("/")) { 76 | settings.setApiUrl(apiUrlTextFieldText.substring(0, apiUrlTextFieldText.length() - 1)); 77 | } else { 78 | settings.setApiUrl(apiUrlTextFieldText); 79 | } 80 | 81 | settings.setLogin(loginTextField.getText().trim()); 82 | settings.setRepos(reposTextField.getText().trim()); 83 | settings.setToken(tokenTextField.getText().trim()); 84 | 85 | } 86 | 87 | public void reset() { 88 | YuQueSettings settings = YuQueSettings.getInstance(project); 89 | urlTextField.setText(settings.getUrl()); 90 | apiUrlTextField.setText(settings.getApiUrl()); 91 | tokenTextField.setText(settings.getToken()); 92 | loginTextField.setText(settings.getLogin()); 93 | reposTextField.setText(settings.getRepos()); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/ui/TemplateSettingForm.form: -------------------------------------------------------------------------------- 1 | 2 |

3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 |
69 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/service/impl/ShowDocServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.service.impl; 2 | 3 | import com.intellij.openapi.application.ApplicationManager; 4 | import com.intellij.openapi.components.Service; 5 | import com.intellij.openapi.options.ShowSettingsUtil; 6 | import com.intellij.openapi.project.Project; 7 | import com.liuzhihang.doc.view.DocViewBundle; 8 | import com.liuzhihang.doc.view.config.ShowDocSettings; 9 | import com.liuzhihang.doc.view.config.ShowDocSettingsConfigurable; 10 | import com.liuzhihang.doc.view.dto.DocView; 11 | import com.liuzhihang.doc.view.dto.DocViewData; 12 | import com.liuzhihang.doc.view.integration.ShowDocFacadeService; 13 | import com.liuzhihang.doc.view.integration.dto.ShowDocUpdateRequest; 14 | import com.liuzhihang.doc.view.integration.dto.ShowDocUpdateResponse; 15 | import com.liuzhihang.doc.view.integration.impl.ShowDocFacadeServiceImpl; 16 | import com.liuzhihang.doc.view.notification.DocViewNotification; 17 | import com.liuzhihang.doc.view.service.DocViewUploadService; 18 | import lombok.extern.slf4j.Slf4j; 19 | import org.apache.commons.lang3.StringUtils; 20 | import org.jetbrains.annotations.NotNull; 21 | 22 | /** 23 | * @author liuzhihang 24 | * @date 2021/7/27 12:00 25 | */ 26 | @Slf4j 27 | @Service 28 | public final class ShowDocServiceImpl implements DocViewUploadService { 29 | 30 | 31 | @Override 32 | public boolean checkSettings(@NotNull Project project) { 33 | ShowDocSettings apiSettings = ShowDocSettings.getInstance(project); 34 | 35 | if (StringUtils.isBlank(apiSettings.getUrl()) 36 | || StringUtils.isBlank(apiSettings.getApiKey()) 37 | || StringUtils.isBlank(apiSettings.getApiToken())) { 38 | // 说明没有配置 ShowDoc 上传地址, 跳转到配置页面 39 | DocViewNotification.notifyError(project, DocViewBundle.message("notify.showdoc.info.settings")); 40 | ShowSettingsUtil.getInstance().showSettingsDialog(project, ShowDocSettingsConfigurable.class); 41 | return false; 42 | } 43 | return true; 44 | } 45 | 46 | @Override 47 | public void doUpload(@NotNull Project project, @NotNull DocView docView) { 48 | 49 | try { 50 | ShowDocSettings settings = ShowDocSettings.getInstance(project); 51 | 52 | ShowDocUpdateRequest request = new ShowDocUpdateRequest(); 53 | request.setShowDocUrl(settings.getUrl()); 54 | request.setApiKey(settings.getApiKey()); 55 | request.setApiToken(settings.getApiToken()); 56 | request.setCatName(docView.getDocTitle()); 57 | request.setPageTitle(docView.getName()); 58 | request.setPageContent(DocViewData.markdownText(project, docView)); 59 | 60 | ShowDocFacadeService facadeService = ApplicationManager.getApplication().getService(ShowDocFacadeServiceImpl.class); 61 | ShowDocUpdateResponse response = facadeService.updateByApi(request); 62 | 63 | ShowDocUpdateResponse.DataInner data = response.getData(); 64 | 65 | String showDocInterfaceUrl = settings.getUrl() + "/" + data.getItemId() + "/" + data.getPageId(); 66 | 67 | DocViewNotification.uploadSuccess(project, "ShowDoc", showDocInterfaceUrl); 68 | } catch (Exception e) { 69 | DocViewNotification.notifyError(project, DocViewBundle.message("notify.showdoc.upload.error")); 70 | log.error("上传单个文档失败:{}", docView, e); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/service/impl/DubboDocViewServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.service.impl; 2 | 3 | import com.intellij.psi.PsiClass; 4 | import com.intellij.psi.PsiMethod; 5 | import com.intellij.psi.PsiType; 6 | import com.liuzhihang.doc.view.dto.DocView; 7 | import com.liuzhihang.doc.view.enums.ContentTypeEnum; 8 | import com.liuzhihang.doc.view.enums.FrameworkEnum; 9 | import com.liuzhihang.doc.view.service.DocViewService; 10 | import com.liuzhihang.doc.view.utils.DocViewUtils; 11 | import com.liuzhihang.doc.view.utils.DubboPsiUtils; 12 | import com.liuzhihang.doc.view.utils.ParamPsiUtils; 13 | import org.jetbrains.annotations.NotNull; 14 | 15 | import java.util.LinkedList; 16 | import java.util.List; 17 | 18 | /** 19 | * Dubbo 处理服务 20 | * 21 | * @author liuzhihang 22 | * @date 2020/11/16 18:28 23 | */ 24 | public class DubboDocViewServiceImpl implements DocViewService { 25 | 26 | 27 | /** 28 | * 校验方法是否为符合条件 29 | * 30 | * @param targetMethod 当前方法 31 | * @return 是否 32 | */ 33 | @Override 34 | public boolean checkMethod(@NotNull PsiMethod targetMethod) { 35 | return DubboPsiUtils.isDubboMethod(targetMethod); 36 | } 37 | 38 | /** 39 | * 创建类的文档 40 | *

41 | * 可能会存在重载方法,所以这里需要对重载进行处理 42 | * 43 | * @param psiClass 当前类 44 | * @return 类的所有接口文档 45 | */ 46 | @Override 47 | public List buildClassDoc(@NotNull PsiClass psiClass) { 48 | 49 | List docViewList = new LinkedList<>(); 50 | 51 | for (PsiMethod method : psiClass.getMethods()) { 52 | if (!DubboPsiUtils.isDubboMethod(method)) { 53 | continue; 54 | } 55 | 56 | DocView docView = buildClassMethodDoc(psiClass, method); 57 | docViewList.add(docView); 58 | } 59 | 60 | return docViewList; 61 | 62 | } 63 | 64 | /** 65 | * 构造当前类方法的文档 66 | * 67 | * @param psiClass 当前类 68 | * @param psiMethod 当前方法 69 | * @return 70 | */ 71 | @NotNull 72 | @Override 73 | public DocView buildClassMethodDoc(@NotNull PsiClass psiClass, @NotNull PsiMethod psiMethod) { 74 | DocView docView = new DocView(); 75 | docView.setPsiClass(psiClass); 76 | docView.setPsiMethod(psiMethod); 77 | docView.setDocTitle(DocViewUtils.getTitle(psiClass)); 78 | docView.setName(DocViewUtils.getName(psiMethod)); 79 | docView.setDesc(DocViewUtils.getMethodDesc(psiMethod)); 80 | docView.setPath(psiClass.getName() + "#" + psiMethod.getName()); 81 | docView.setMethod("Dubbo"); 82 | // docView.setDomain(); 83 | docView.setType(FrameworkEnum.DUBBO); 84 | 85 | // 有参数 86 | if (psiMethod.hasParameters()) { 87 | docView.setReqBody(DubboPsiUtils.buildBody(psiMethod)); 88 | docView.setContentType(ContentTypeEnum.JSON); 89 | docView.setReqBodyExample(DubboPsiUtils.getReqBodyJson(psiMethod)); 90 | } 91 | 92 | PsiType returnType = psiMethod.getReturnType(); 93 | // 返回代码相同 94 | if (returnType != null && returnType.isValid() && !returnType.equalsToText("void")) { 95 | docView.setRespBody(ParamPsiUtils.buildRespBody(returnType)); 96 | docView.setRespExample(ParamPsiUtils.getRespBodyJson(returnType)); 97 | } 98 | return docView; 99 | 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/ui/window/ModuleNode.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.ui.window; 2 | 3 | import com.intellij.icons.AllIcons; 4 | import com.intellij.openapi.module.Module; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.openapi.util.NlsSafe; 7 | import com.intellij.psi.PsiClass; 8 | import com.intellij.psi.search.GlobalSearchScope; 9 | import com.intellij.psi.search.searches.AllClassesSearch; 10 | import com.intellij.ui.treeStructure.SimpleNode; 11 | import com.liuzhihang.doc.view.config.Settings; 12 | import com.liuzhihang.doc.view.dto.DocView; 13 | import com.liuzhihang.doc.view.utils.DubboPsiUtils; 14 | import com.liuzhihang.doc.view.utils.FeignPsiUtil; 15 | import com.liuzhihang.doc.view.utils.SpringPsiUtils; 16 | 17 | import java.util.ArrayList; 18 | import java.util.Collection; 19 | import java.util.LinkedList; 20 | import java.util.List; 21 | import java.util.stream.Collectors; 22 | 23 | /** 24 | * @author liuzhihang 25 | * @date 2022/4/4 17:06 26 | */ 27 | public class ModuleNode extends DocViewNode { 28 | 29 | private final List classNodes = new ArrayList<>(); 30 | private final Module module; 31 | 32 | protected ModuleNode(SimpleNode aParent, Module module) { 33 | super(aParent); 34 | this.module = module; 35 | 36 | getTemplatePresentation().setIcon(AllIcons.Nodes.Module); 37 | getTemplatePresentation().setPresentableText(getName()); 38 | updateNode(module.getProject()); 39 | } 40 | 41 | public void updateNode(Project project) { 42 | cleanUpCache(); 43 | classNodes.clear(); 44 | 45 | List psiClasses = new LinkedList<>(); 46 | 47 | if (Settings.getInstance(project).getIncludeNormalInterface()) { 48 | // 包含普通接口则扫描所有接口 49 | List interfaceList = AllClassesSearch.search(GlobalSearchScope.moduleScope(module), project).findAll() 50 | .stream() 51 | .filter(PsiClass::isInterface) 52 | .collect(Collectors.toList()); 53 | psiClasses.addAll(interfaceList); 54 | } else { 55 | psiClasses.addAll(DubboPsiUtils.findDocViewFromModule(module)); 56 | psiClasses.addAll(FeignPsiUtil.findDocViewFromModule(module)); 57 | } 58 | 59 | psiClasses.addAll(SpringPsiUtils.findDocViewFromModule(module)); 60 | 61 | for (PsiClass psiClass : psiClasses) { 62 | ClassNode classNode = new ClassNode(this, psiClass); 63 | classNodes.add(classNode); 64 | } 65 | update(); 66 | } 67 | 68 | @Override 69 | public String docPath(Project project) { 70 | 71 | RootNode rootNode = (RootNode) getParent(); 72 | 73 | return rootNode.docPath(project) + "/" + module.getName(); 74 | } 75 | 76 | @Override 77 | public String httpPath(Project project) { 78 | 79 | RootNode rootNode = (RootNode) getParent(); 80 | 81 | return rootNode.httpPath(project) + "/" + module.getName(); 82 | } 83 | 84 | @Override 85 | protected SimpleNode[] buildChildren() { 86 | return classNodes.toArray(new SimpleNode[0]); 87 | } 88 | 89 | @Override 90 | public @NlsSafe String getName() { 91 | return module.getName(); 92 | } 93 | 94 | @Override 95 | public List docViewList() { 96 | return classNodes.stream().map(ClassNode::docViewList).flatMap(Collection::stream).collect(Collectors.toList()); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/com/liuzhihang/doc/view/action/toolbar/window/WindowExportAction.java: -------------------------------------------------------------------------------- 1 | package com.liuzhihang.doc.view.action.toolbar.window; 2 | 3 | import com.intellij.openapi.actionSystem.AnAction; 4 | import com.intellij.openapi.actionSystem.AnActionEvent; 5 | import com.intellij.openapi.actionSystem.PlatformDataKeys; 6 | import com.intellij.openapi.application.ApplicationManager; 7 | import com.intellij.openapi.fileChooser.FileChooser; 8 | import com.intellij.openapi.fileChooser.FileChooserDescriptor; 9 | import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; 10 | import com.intellij.openapi.progress.ProgressIndicator; 11 | import com.intellij.openapi.progress.ProgressManager; 12 | import com.intellij.openapi.progress.Task; 13 | import com.intellij.openapi.project.Project; 14 | import com.intellij.openapi.util.io.FileUtil; 15 | import com.intellij.openapi.vfs.VirtualFile; 16 | import com.liuzhihang.doc.view.DocViewBundle; 17 | import com.liuzhihang.doc.view.data.DocViewDataKeys; 18 | import com.liuzhihang.doc.view.dto.DocView; 19 | import com.liuzhihang.doc.view.dto.DocViewData; 20 | import com.liuzhihang.doc.view.notification.DocViewNotification; 21 | import com.liuzhihang.doc.view.ui.window.RootNode; 22 | import com.liuzhihang.doc.view.utils.DialogUtil; 23 | import org.jetbrains.annotations.NotNull; 24 | 25 | import javax.swing.*; 26 | import java.io.File; 27 | 28 | /** 29 | * @author liuzhihang 30 | * @date 2021/10/23 19:55 31 | */ 32 | public class WindowExportAction extends AnAction { 33 | 34 | @Override 35 | public void actionPerformed(@NotNull AnActionEvent e) { 36 | 37 | // 获取当前project对象 38 | Project project = e.getData(PlatformDataKeys.PROJECT); 39 | JComponent toolbar = e.getData(DocViewDataKeys.WINDOW_TOOLBAR); 40 | RootNode rootNode = e.getData(DocViewDataKeys.WINDOW_ROOT_NODE); 41 | 42 | if (project == null || toolbar == null || rootNode == null || rootNode.getChildCount() == 0) { 43 | DocViewNotification.notifyError(project, DocViewBundle.message("notify.window.export.empty")); 44 | return; 45 | } 46 | // 选择路径 47 | FileChooserDescriptor fileChooserDescriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor(); 48 | fileChooserDescriptor.setForcedToUseIdeaFileChooser(true); 49 | VirtualFile chooser = FileChooser.chooseFile(fileChooserDescriptor, project, null); 50 | if (chooser == null) { 51 | return; 52 | } 53 | String path = chooser.getPath(); 54 | 55 | // 导出到一个文件中 56 | File file = new File(path + "/DocView.md"); 57 | 58 | // 文件已存在,选择是否覆盖导出。 59 | if (file.exists() && !DialogUtil.confirm( 60 | DocViewBundle.message("notify.export.file.exists"), 61 | DocViewBundle.message("notify.export.file.cover"))) { 62 | return; 63 | } 64 | ProgressManager.getInstance().run(new Task.Backgroundable(project, "Doc View export", true) { 65 | @Override 66 | public void run(@NotNull ProgressIndicator progressIndicator) { 67 | 68 | ApplicationManager.getApplication().executeOnPooledThread(() -> ApplicationManager.getApplication().runReadAction(() -> { 69 | 70 | for (DocView docView : rootNode.docViewList()) { 71 | try { 72 | FileUtil.writeToFile(file, DocViewData.markdownText(project, docView), true); 73 | } catch (Exception ignored) { 74 | } 75 | } 76 | 77 | })); 78 | 79 | 80 | } 81 | }); 82 | } 83 | } 84 | --------------------------------------------------------------------------------