Leetcode-runner Leetcode-runner
首页
  • 简 介
  • 安 装
  • 开 始

    • 快速上手
    • 使用配置
    • 辅助功能
  • 更新日志

    • v3.7.0
  • 开发文档-1
  • 开发文档-2
  • 算法

    • 前言
    • API入门
    • 滑动窗口
问答
GitHub (opens new window)
首页
  • 简 介
  • 安 装
  • 开 始

    • 快速上手
    • 使用配置
    • 辅助功能
  • 更新日志

    • v3.7.0
  • 开发文档-1
  • 开发文档-2
  • 算法

    • 前言
    • API入门
    • 滑动窗口
问答
GitHub (opens new window)
  • 源码讲解

    • 开发文档-1
      • 整体架构
      • Icon图标
      • Setting
      • IO-File
      • IO-HTTP
        • 1.总体介绍
        • 2.细节描述
        • 3. 使用Demo
      • IO-Console
      • UI-Action
      • UI-ToolWindow
      • UI-Editor
      • Service-LoginService
      • Service-QuestionService
      • Service-CodeService
      • LoginService-CookieLoginWindow
      • UI-Editor
        • 1. SplitTextEditorProvider 和SplitTextEditorWithPreview
        • 2.PsiAwareTextEditorImpl和FocusTextEditor
        • 3.MarkDownEditor、SolutionEditor、SubmissionEditor
        • i.MarkDownEditor
        • ii.SolutionEditor、SubmissionEditor
      • IO-HTTP
      • Bus
    • 开发文档-2
  • 算法

    • 算法模板
    • API入门
    • 算法模板
  • 教程
  • 源码讲解
文艺倾年
2025-04-02
目录

开发文档-1

# 开发环境

feigebuge推荐环境

  • JDK: JDK 11.0.25
  • Gradle: Gradle 8.0
  • IDE: IntelliJ IDEA 2023.2.3
  • 项目启动的IDE: IntelliJ IDEA 2022.2.3

# HELPER v1.0

# 整体架构

structual

项目分为三层,分别是

  • UI层:负责和idea交互,编写显示界面

    • ToolWindow:工具窗口,插件项目数据显示的主题部位
    • Editor:提供特定内容的显示编辑器
    • Action:与UI进行交互
  • Service层:处理业务逻辑,负责数据与UI的交互

  • Basic层:提供支持环境,目前包括setting和IO模块

    • setting:项目设置模块,可以在File->Settings部位进行项目设置

    • IO

      • file:处理文件IO

      • console:负责控制台IO

      • http:负责网络IO,处理与leetcode平台的请求

# Icon图标

一律采用svg作为项目图标, 且svg大小尽量为16x16, 如果偏小, 可选择24x24, 32x32. 最大尽量不要超过32x32

图标命名统一为xxx.svg, xxx_dark.svg

其中, xxx_dark.svg表示IDE如果是dark主题, 则会使用此svg作为图标

关于svg颜色, xxx.svg尽量一律选择fill="", 也就是在IDE主题为light时, 默认的黑色; xxx_dark.svg尽量选择fill="#dbdbdb", 也就是IDE主题为dark时, 使用浅银色

# Setting

该模块用三各类维护

  • AppSettings:包含需要idea持久的配置数据,目前需要持久化的数据有三,分别是langType, filePath, coreFilePath
    • langType:表示当前选择的语言,当选定langType后,插件会自动创建与langType匹配的代码文件
    • filePath:指定代码文件创建目录
    • coreFilePath:指定缓存文件创建目录,该值会在用户第一次设置·filePath`时确定,项目只允许创建一次
  • AppSettingsComponent:提供设置的UI界面
  • AppSettingsConfigurable:配置类,注册到idea当中,提供持久化AppSettings数据的能力

# IO-File

提供与disk,memory交互的能力

架构图

IO-File

StoreService目前提供缓存 + 持久化能力

其中缓存能力分为非持久化缓存(cache),持久化缓存(durableCache)。StoreService采用K-V结构存储数据,并在内部通过StoreContent对V进行封装。每一个缓存的数据都会序列化为Json

StoreService提供缓存时间检测功能,但目前该功能并未在项目中真正启用

此外,StoreService通过@Service注册为idea Project级别服务,有idea管理类的生命周期。在插件项目关闭之前,会调用StoreService的persistCahce()方法,将durableCache内的数据写入磁盘,存储为app.properties,磁盘路径由Settings模块的coreFilePath决定,通过AppSetting对外暴露的getCoreFilePath提供存储路径

# IO-HTTP

# 1.总体介绍

IO-HTTP-2

使用apache的httpclient框架作为底层请求支持,在此基础上根据lc-runner业务进行封装

总体来说分为两层,第一层HttpClient,封装httpclient提供的基础能力,对外暴露发送get和post请求方法,此外支持cookie的相关操作

第二层是LeetcodeClient,封装HttpClient提供能力,对业务提供各种业务接口,诸如登录判断,题目获取,代码提交等功能,该模块直接对接service模块

另外,针对http的请求和响应,项目也做了一定封装——HttpRequest,HttpResponse

# 2.细节描述

leetcode接口采用graphql技术,按需分配资源,关于该技术的详细介绍,可自行搜索

关于该技术对于本项目的影响,体现在Request Body的构建上

在没运用graphql技术前,不同资源是按照url请求路径区分,但应用该技术后,对于国内leetcode来说,大部分资源请求url都是https://leetcode.cn/graphql,而区分资源是依赖请求体

以题目内容查询、用户状态查询接口为例

题目内容查询

url = https://leetcode.cn/graphql

body =

{
 "query": "\n    query problemsetQuestionList($categorySlug: String, $limit: Int, $skip: Int, $filters: QuestionListFilterInput) {\n  problemsetQuestionList(\n    categorySlug: $categorySlug\n    limit: $limit\n    skip: $skip\n    filters: $filters\n  ) {\n    hasMore\n    total\n    questions {\n      acRate\n      difficulty\n      freqBar\n      frontendQuestionId\n      isFavor\n      paidOnly\n      solutionNum\n      status\n      title\n      titleCn\n      titleSlug\n      topicTags {\n        name\n        nameTranslated\n        id\n        slug\n      }\n      extra {\n        hasVideoSolution\n        topCompanyTags {\n          imgUrl\n          slug\n          numSubscribed\n        }\n      }\n    }\n  }\n}\n    ",
 "variables": {
     "categorySlug": "all-code-essentials",
     "skip": 0,
     "limit": 1,
     "filters": {
         "searchKeywords": "3254"
     }
 },
 "operationName": "problemsetQuestionList"
}
1
2
3
4
5
6
7
8
9
10
11
12

用户状态查询

url = https://leetcode.cn/graphql

body =

{
 "query": "\n    query globalData {\n  userStatus {\n    isSignedIn\n    isPremium\n    username\n    realName\n    avatar\n    userSlug\n    isAdmin\n    useTranslation\n    premiumExpiredAt\n    isTranslator\n    isSuperuser\n    isPhoneVerified\n    isVerified\n  }\n  jobsMyCompany {\n    nameSlug\n  }\n  commonNojPermissionTypes\n}\n    ",
 "variables": {}
}
1
2
3
4

通过上述两个demo可以发现,运用了graphql技术的接口,请求体的构建相对麻烦

基于此,封装如下三个类,帮助创建请求体

image-20211206152427409
  • GraphqlReqBody,负责构建请求体
  • SearchParams,是GraphqlReqBody的内部类,负责构建variables变量
  • ParamsBuilder,是SearchParams的内部类,通过建造者模式辅助构建SearchParams

# 3. 使用Demo

// 辅助构建varablies
GraphqlReqBody.SearchParams params = new GraphqlReqBody.SearchParams.ParamsBuilder()
    .setCategorySlug("all-code-essentials")
    .setLimit(limit)
    .setSkip(skip)
    .build();
// 构建graphql请求体
GraphqlReqBody body = new GraphqlReqBody(LeetcodeApiUtils.PROBLEM_SET_QUERY);
body.setBySearchParams(params);
// 构建httpRequest
HttpRequest httpRequest = new HttpRequest.RequestBuilder(url)
    .setBody(body.toJsonStr())
    .setContentType("application/json")
    .addBasicHeader()
    .build();
// send request
HttpResponse httpResponse = httpClient.executePost(httpRequest);
String resp = httpResponse.getBody();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# IO-Console

IO-Console

console模块负责在插件自带的控制台上输出信息

# UI-Action

官方定义:The IntelliJ Platform calls the actions of plugins in response to user interactions with the IDE

Action,通过继承AnAction类来提供用户与IDE的交互能力,在编写完代码后,需要在plugin.xml中进行注册

image-20241127221627476

在本项目中,通过继承AbstractAction类来规范化每一个Action的行为。在AbstractAction中,会检查登录状态和设置状态,如果有@LoginPass或者@SettingPass注解,那么在对应检测环节则会被放行,否则进行拦截检测

ActionGroup,本质上也是Action,只不过他是一组Action的集合,可以在xml中用<group>标签注册

        <!--注册Action-->
		<action id="leetcode.plugin.LoginAction" class="com.xhf.leetcode.plugin.actions.loginAction"
                text="Sign In" icon="/icons/login.png">

		<!--注册Group-->
        <group id="leetcode.plugin.lcActionsToolbar">
            <reference id="leetcode.plugin.LoginAction"/>
            <separator/>
        </group>
1
2
3
4
5
6
7
8
9

# UI-ToolWindow

image-20241127222429401

工具窗口,拓展idea功能的窗口,本项目关于这部分的模块图如下

image-20241127222429401

ToolWindowFactory负责为ToolWindow创建内容,Panel则是ToolWindow显示内容的载体

ToolWindow由idea自身提供,ToolWIndowFactory负责将Panel添加到ToolWindow内

在本项目中,Panel所在位置如下所示

image-20241127222932790
  • LCPanel:包含两部分数据内容,分别是Toolbar工具栏,JBList列表。LCPanel通过继承DataProvider提供对外暴露JBList的能力
  • LCConsolePanel:只包含ConsoleView,LCConsolePanel同样通过继承DataProvider提供对外暴露consoleView的能力

# UI-Editor

Editor,提供特定文件的编辑显示能力。比如说普通的文本编辑器,只会显示html代码。但自定义的Html编辑器,则会按照html的语法,显示内容

在idea中,提供自定义文本编辑器依赖于两个类

  • FileEditorProvider:负责提供FileEditor,同时检测特定文件是否支持用特定Editor打开
  • FileEditor:负责处理文件内容,提供显示的JComponent

在本项目中,为了支持分屏显示代码和题目内容,使用SpliterTextEditorProvider和SplitTextEditorWithPreview提供相关能力

  • SpliterTextEditorProvider:在提供分屏Editor的同时,检测当前文件是否支持分屏Editor预览。该类通过查询StoreService中是否缓存相关文件信息,来判断当前文件是否支持SplitTextEditor
  • SplitTextEditorWithPreview:提供分屏预览能力,在本项目中,第一块分屏是PsiAwareTextEditorImpl,第二块分屏是MarkDownEditor

MarkdownEditor,在第一个版本中,并不支持markdown内容的显示,只支持HTML代码的显示。其显示原理是,读取自定义的HTML模板代码,同时将HTML代码注入其中。然后通过JCEF技术,启动CefBrowser,通过模拟浏览器的方式打开处理后的HTML内容

# Service-LoginService

loginService,负责处理登录有关的逻辑,利用JCEF技术,模拟启动浏览器,通过加载leetcode登录界面来提供登录功能

需要注意的是,当前leetcode并不支持通过uesrname和password的方式用本地代码模拟登录。leetcode官网存在参数校验的机制,在发出登录请求的同时会携带该参数。该参数只有在官网登录的时候才会携带,单纯的代码模拟无法正确登录

每当JBCefClient加载状态改变,都会执行监控逻辑,检测当前CefCookieManager中是否存在name为LEETCODE_SESSION的Cookie,如果存在,缓存到HTTP模块,同时通过StoreService进行持久化,登录完成后关闭当前登录窗口,加载题目信息,更新LCPanel

为了判断当前项目是否登录,LoginService对外暴露isLogin接口,该接口原理是通过LoginService内部的loginFlag参数和LeetcodeClient提供的接口共同判断,前者判断插件是否执行成功过登录逻辑,后者判断当前cookie是否能够代表客户端成功登录

# Service-QuestionService

questionService,负责处理question相关逻辑。

loadAllQuestionData,该方法会在后台查询所有leetcode问题,查询完成后更新LCPanel暴露的JBList

其余业务内容注释中都有解释,没啥好说的

# Service-CodeService

上图表示CodeService与其他模块的沟通信息

首先介绍LeetcodeEditor,该类用于存储围绕题目内容,创建editor所需要的相关信息

未命名文件 (8)
  • questionId:当前题目的id
  • lang:编写当前题目用当的语言类型,比如java或者python
  • exampleTestcases:当前题目运行的测试样例【后续可以通过TestcaseAction更改】
  • markdownContent:题目内容【MarkDownEditor渲染所需的内容来源】
  • titleSlug:题目的slug标签,用于唯一标识题目,该字段是http模块发送请求时的必要字段
  • defaultTestcases:官方提供的测试用例

LeetcodeEditor会在openCode()方法中被创建,然后存储到storeService中

openCode()通过点击LCPanel显示的JBList触发,该方法的大致逻辑是

  1. 填充用户选定的question,丰富其中的信息
  2. 创建code文件,获取文件路径
  3. 创建并存储leetcodeEditor,维护额外信息。存贮的key是code文件的文件路径
  4. 打开文件,并用SplitTextWithPreview显示

runCode(),通过当前打开的FileEditor获取显示的code文件路径,以此作为key,从cache中获取LeetcodeEditor维护的信息,然后执行运行逻辑,将代码通过LeetcodeClient提交到Leetcode平台。运行的结果在通过ConsoleUtils输出到控制台

TestCase(),与runcode类似,通过code 路径获取leetcodeEditor信息。创建Dialog与用户交互,将用户提供的测试数据存入LeetcodeEditor内,更新测试用例

Tip:在runCode()方法中,通过AbstractResultBuilder将运行结果转化为String,然后再将String交由ConsoleUtils输出

有关于AbstractResultBuilder类的详细内容,请参考源码

# v2.0

新增内容

  • 登录模块,新增cookie登录
  • editor模块,支持同时阅览题目内容,题解, 提交记录。支持html于markdown的内容阅览
  • 新增http处理器
  • 新增事件总线,解耦模块

# LoginService-CookieLoginWindow

未命名文件

Cookie登录和Jcef登录存在通用逻辑,因此向上抽取抽象类

BasicWindow通过start()方法启动,getFrameTItle(), loadConponent()方法下放到子类实现。前者要求子类设置启动window的title,后者要求子类填充window的内容

此外,CookieLoginWindow用JTextArea要求用户输入名为LEETCODE_SESSTION的Cookie信息

# UI-Editor

Editor模块稍显复杂,文档将会按照等级依次介绍

# 1. SplitTextEditorProvider 和SplitTextEditorWithPreview

SplitTextEditorProvider 提供SplitTextEditorWithPreview,SplitTextEditorWithPreview提供第一层的分屏预览能力,左侧是编码区域,右侧是内容预览区域

# 2.PsiAwareTextEditorImpl和FocusTextEditor

PsiAwareTextEditorImpl作为SplitTextEditorWithPreview左侧的编辑区域,提供代码编辑能力

FocusTextEditor作为右侧编辑区域,提供tabs显示能力,包含三个tab,每个tab的显示内容由3个FileEditor维护

image-20241128112729808

# 3.MarkDownEditor、SolutionEditor、SubmissionEditor

# i.MarkDownEditor

MarkDownEditor,提供HTML、Markdown内容的显示能力,其原理是通过JCEF加载处理后的HTML文件,通过模拟浏览器选软HTML内容

Markdown内容渲染,使用前端vditor框架渲染。因为该框架在项目首次启动渲染Markdown耗时很长,因此在渲染HTML的时候会提前加载需要的js、css文件

# ii.SolutionEditor、SubmissionEditor

SolutionEditor、SubmissionEditor分别提供题解、提交记录显示

image-20241128113655951 image-20241128113718533

上述两种FileEditor都需要分屏显示功能,因此向上抽取AbstractSplitTextEditor抽象类,提供分窗能力

AbstractSplitTextEditor

未命名文件 (2)
  • AbstractSplitTextEditor核心组件jbSplitter,提供分屏显示的能力

  • createToolbarWrapper()为component创建工具栏,提供关闭右侧分屏的能力

  • initFirstComp()的实现下放到子类,初始化分屏左侧内容

  • openSecond(String)的实现下放到子类,创建右侧分屏内容

image-20241128114712906

# IO-HTTP

当前http模块通过web方式,提供resource目录下的资源内容。当前模块服务于markdown内容显示

本项目中,采用JCEF方式模拟浏览器,使用前端框架vditor渲染markdown内容。markdown的内容显示依赖于框架js、css代码,因此需要提供web能力,加载文件内容

LocalHttpRequestHandler负责拦截idea发出的http请求,如果请求前缀是"/leetcode-project/",则交由LocalResourceController负责处理,读取resource目录下的js、css文件内容

# Bus

总线模块,提供注册和发送消息的能力。订阅方注册到特定的Topic内,接受投放到Topic中的消息内容

LCSubscriber,订阅者,当特定Topic内存在消息,订阅者的onEvent()方法将会被触发

LCEventBus,提供消息投递和订阅者注册功能。LCEventBus允许将订阅者注册到内部的订阅池,并按照不同的Topic进行存储,每个Topic是LCEvent的子类的class。当有观察者通过LCEventBus投递消息,LCEventBus将会通知订阅相关Topic的所有订阅者

帮助我们改善此页面! (opens new window)
上次更新: 2025/04/02, 14:09:35
开发文档-2

开发文档-2→

Theme by Vdoing | Copyright © 2025-2025 文艺倾年 | 飞哥不鸽
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式