自定义IDEA代码补全插件
目标:
对于项目中的静态方法(主要是各种工具类里的静态方法),可以在输入方法名时直接提示相关的静态方法,选中后自动补全代码,并导入静态类。
设计:
初步构想,用户选择要导入的文件夹,遍历文件夹下面文件的静态方法并存储,当用户输入时使用弹窗显示候选方法,选中后补全代码。
分解步骤为:
- 在设置页加入视图化操作,让用户选择文件夹路径;
- 通过持久化数据将选择的文件夹路径保存到本地;
- IDE打开时遍历本地保存的文件夹路径下的所有文件,得到所有静态方法;
- 用户输入时弹窗显示联想方法;
- 选中后自动补全;
开发:
1.搭建开发环境
JetBrains已经提供了纯样板模板,我们下载提供的插件模板 ,使用Android Studio (或IntelliJ IDEA )打开后,可以在gradle.properties中修改项目的属性,gradle.gradle.properties里各属性表示的意义如下
gradle.properties配置
-
pluginGroup、pluginName_、pluginVersion:插件名称与版本
-
pluginSinceBuild、pluginUntilBuild:插件适用的IDE版本,从since到until,各种IDE的版本号可以在这个地方查阅内部编号范围
Android Studio对应的IntelliJ 平台版本可以查阅Android Studio
-
pluginVerifierIdeVersions:用来检查IDE版本和插件之间兼容性
-
**platformType:**插件适用的IDE类型,IC指社区版,Android Studio基于社区版修改
-
platformPlugins: 声明插件依赖项
更多的属性可以查阅此链接
https://github.com/JetBrains/intellij-platform-plugin-template
https://github.com/JetBrains/gradle-intellij-plugin/blob/master/README.md#intellij-platform-properties
plugin.xml
文件位于src\main\resources\META-INF下
-
id:gradle.properties里的pluginName_
-
name: gradle.properties里的pluginName_
-
vendor:开发者的名字
添加依赖
build.gradle.kts
在intellij节点下加入一句intellij
alternativeIdePath = "H:\Android\Android Studio"
路径设置为本地Android Studio位置,这样在运行时会直接使用本地的AS调试,避免重新下载Android Studio。
settings.gradle.kts
修改项目名称
rootProject.name = "Plugin Template Hint"
配置完成后,点击右边的gradle的runide即可运行插件,如果开发过程中想进行调试可以右键选择debug模式。
2.设置页添加视图化操作
在IDE的设置页添加新UI,需要使用applicationConfigurable Extension Points
。 先在plugin.xml里注册applicationConfigurable
,并且新建类继承Configurable
。插件的UI模块是在java的swing组件基础上直接包装了一层,可以直接使用。
<extensions defaultExtensionNs="com.intellij">......<applicationConfigurable instance="com.plugin.hint.other.UtilsImportUI" /></extensions>
class UtilsImportUI : Configurable {private val persistentState: UtilsFolderSetting = ApplicationManager.getApplication().getComponent(UtilsFolderSetting::class.java)private var isModify = false//绘制界面,使用Swing组件override fun createComponent(): JComponent? {//......绘制代码此处省略}//控制按钮“Apply”是否可点击override fun isModified(): Boolean {return isModify}//"Apply"按钮点击事件override fun apply() {......persistentState!!.list = pathListpersistentState.loadState(persistentState)//持久化数据isModify = false }//配置面板左边窗口的显示名称override fun getDisplayName(): String {return "Import Utils Files"}//调用IDE的文件管理器选择文件private fun dir(jPanel: JPanel): String {if (project == null) {project = guessCurrentProject(jPanel)}val fcDial = FileChooserFactory.getInstance().createFileChooser(fcDesc, project, null)val files = fcDial.choose(project)return if (files.isNotEmpty()) {files[0].path} else ""}
}
上面省略了部分代码,主要是绘制界面、持久化数据、保存用户选中的文件位置,并进行相关的去重。
效果如下:
3.持久化数据
为了保存用户选择的文件夹路径,我们需要对数据进行持久化。
在plugin.xml里注册implementation-class
,并且新建类继承PersistentStateComponent
,其中,name为XML中根标记的名称,storages 为保存的文件的名称,默认位置是配置文件地址的options目录下(默认位置可以点击File -> Mange IDE Settings -> Export Settings 查看)。
我们将路径通过list保存,读取时
<application-components><component><implementation-class>com.plugin.hint.other.UtilsFolderSetting</implementation-class></component></application-components>
@State(name = "searchUtilsPath", storages = [Storage(value = "searchUtilsPath.xml")])
class UtilsFolderSetting : PersistentStateComponent<UtilsFolderSetting?> {var list: MutableList<String> = ArrayList()override fun getState(): UtilsFolderSetting {return this}override fun loadState(state: UtilsFolderSetting) {XmlSerializerUtil.copyBean(state, this)}
}
4.启动时遍历文件,保存静态方法
工程模板service下有两个类MyApplicationService
和MyProjectService
,分别是 application 级别的service和 project 级别的service,其实还有一个module 级别的service,但是并不推荐(性能原因)。其中MyApplicationService
为全局单例,而MyProjectService
会在对应范围的每个实例创建一个单独的服务实例。这里我们在MyProjectService里遍历文件夹路径,对所有文件进行解析,并保存静态方法。
class MyProjectService(project: Project) {init {if (project.workspaceFile != null) {val persistentState = ApplicationManager.getApplication().getComponent(UtilsFolderSetting::class.java)val pathList = persistentState.list//得到持久化数据for (s in pathList) {//遍历文件夹路径UtilMethodsHandle.addPsiMethodByPath(s, project)}}}
}
persistentState 为得到的持久化数据,然后再对文件路径进行解析。
addPsiMethodByPath
方法如下,逻辑可以看注释
var globalPsiMethods = HashMap<String, List<PsiMethod>>()//遍历文件夹,解析文件,存储方法fun addPsiMethodByPath(path: String, project: Project) {val virtualFile = project.workspaceFile!!.fileSystem.findFileByPath(path) ?: returnif (virtualFile.isDirectory) {//如果是文件夹,递归遍历val virtualFiles = virtualFile.childrenfor (file in virtualFiles) {addPsiMethodByPath(file.path, project)}} else {//如果是文件,解析val psiFile = PsiManager.getInstance(project).findFile(virtualFile)//判断是否是java文件,后面看是否支持kotlin文件if (psiFile is PsiJavaFile) {val classes = psiFile.classes //遍历文件里的类,因为可能会有内部类for (aClass in classes) {val tempMethods = aClass.methodsval list: MutableList<PsiMethod> = ArrayList()//遍历类里面的方法for (method in tempMethods) {//判断是静态并且不是私有的方法if (method.hasModifierProperty(PsiModifier.STATIC)&& !method.hasModifierProperty(PsiModifier.PRIVATE)) {list.add(method)}}globalPsiMethods[path] = list}}}}
解释上面的代码,需要先了解IntelliJ平台的一些名称概念。
PSI 程序结构接口(Program Structure Interface),是IntelliJ平台中的一个层,负责解析文件并创建支持平台许多功能的语法和语义代码模型。
PSI File ,IDEA将文件结构抽象为接口,叫程序结构接口文件(PSI File),不同类型的文件解析后生成不同的PsiFile接口的实现类实例,这也是IDEA能够扩展支持多语言的基础。PsiFile类是所有PSI文件的公共基类,而在特定的语言文件通常是由它的子类来表示。例如,PsiJavaFile类表示Java文件,XmlFile类表示XML文件。
VirtualFileSystem 虚拟文件系统(VFS)是IntelliJ平台的组件,该组件封装了用于处理以Virtual File表示的文件的大部分活动。
它具有以下主要目的:
提供一个通用API来处理文件,而不管文件的实际位置如何(在磁盘上,在归档中,在HTTP服务器等上)
当检测到更改时,跟踪文件修改并提供文件内容的旧版本和新版本。
提供了将其他持久性数据与VFS中的文件相关联的可能性。
Virtual File System
上面的代码通过project
得到VirtualFile
,判断如果是文件夹,递归调用方法,否则返回相对应的PsiFile
,接着判断如果是PsiJavaFile
(因为项目有可能包含kotlin文件),则遍历PsiClass
(有可能包含内部类)得到所有PsiMethod
,最后判断method是否是静态的(method.hasModifierProperty(PsiModifier.STATIC)
)并且不是私有的(!method.hasModifierProperty(PsiModifier.PRIVATE)
),最后加入列表。
5.用户输入时自动弹窗显示联想方法
这里的两种方案,其实最开始使用的是第一种方法,在IDE自带的代码补全弹窗里插入我们保存的方法,但是这种方案没有解决方法显示排序的问题,提供的 order="first"
属性并没有生效,最后使用了第二种方案。这里记录一下,可能以后在写其他插件时会用到。
第一种方案:
我们在plugin.xml里注册CompletionContributor
, language
为JAVA
<extensions defaultExtensionNs="com.intellij">......<completion.contributorimplementationClass="com.plugin.hint.other.UtilsCompletionContributor" language="JAVA"order="first" /></extensions>
CompletionContributor
,实现extend
函数,有三个参数
- CompletionType:代码完成的类型,基本完成(BASIC)、智能类型(SMART)匹配完成,
Settings/Preferences | Editor | General | Code Completion
里可选.
- ElementPattern:匹配类型,可以对返回的元素进行过滤
- CompletionProvider:内容提供者,我们在这里返回待选择的
class UtilsCompletionContributor : CompletionContributor() {//查找可以自动补全的代码init {extend(CompletionType.BASIC, PlatformPatterns.psiElement(), UtilsCompletionProvider())}
}
UtilsCompletionProvider
类,继承CompletionProvider,重写addCompletions
方法,将元素加入到CompletionResultSet
。
class UtilsCompletionProvider : CompletionProvider<CompletionParameters>() {//添加自动补全代码override fun addCompletions(parameters: CompletionParameters, context: ProcessingContext, result: CompletionResultSet) {val prefix = result.prefixMatcher.prefixif (prefix.isEmpty()) {return}for (methodList in UtilMethodsHandle.globalPsiMethods.values) {for (method in methodList) {var s: String? = ""if (method.containingClass != null) {s = method.containingClass!!.qualifiedName//类名称}val element: LookupElement = LookupElementBuilder.create(method).withTypeText(s)//右边文字//.withIcon(MethodIcon).withIcon(AllIcons.Nodes.MethodReference)//左边图标.withBoldness(true)//是否加粗//选中后的处理事件.withInsertHandler { context1: InsertionContext, lookupElement: LookupElement? ->context1.document.insertString(context1.startOffset, ".")context1.document.insertString(context1.tailOffset, "();")//导入所引用的类JavaCompletionUtil.insertClassReference(method.containingClass!!, context1.file, context1.startOffset)//移动光标到代码尾部 context1.editor.caretModel.moveToOffset(context1.tailOffset - 2)}//添加element到代码补全弹窗result.addElement(PrioritizedLookupElement.withPriority(element, Int.MAX_VALUE.toDouble()))}}
上面代码,先检测是否有匹配的,否则返回。然后循环创建LookupElement。InsertHandler
为选中后的操作,在这里补全代码,引入当前方法所在类。
如上图,在自带的代码补全弹窗里添加了2条我们的方法。
第二种方案:
在用户输入后使用快捷键呼出代码补全弹窗,使用Action完成。IntelliJ 平台中的Action需要代码实现并且必须注册。注册决定了Action在 IDE UI 中出现的位置。实现并注册后,Action会接收来自 IntelliJ 平台的回调以响应用户。
1.创建UtilsAction
类,继承 Action
类。当使用键盘快捷键或从菜单、工具栏操作时,就会回调 Action
类的 actionPerformed
方法。
先在plugin.xml里注册Action
,这里默认的快捷键是"control shift X"
<actions><action class="com.plugin.hint.other.UtilsAction" description="方法提示" id="plugin.hint" text="hint"><add-to-group anchor="first" group-id="CodeCompletionGroup" /><keyboard-shortcut first-keystroke="control shift X" keymap="$default" /></action></actions>
效果如图,Code Completion组下添加了我们新建的Action,在这里也可以更改快捷键。
在UtilsAction
类里,我们在actionPerformed
方法里弹出代码补全弹窗。searchText
为用户输入的需要补全的代码。LookupImpl
为为代码补全的弹窗。选中逻辑与第一种方案一样。
class UtilsAction : AnAction() {override fun actionPerformed(e: AnActionEvent) {......//需要查找的字符val searchText = StringBuilder()//selectedText表示光标选中的文本,如果不为空,则查找选中的,没有就从光标位置向前拼接字符,一直到空格为止if (editor.selectionModel.selectedText == null|| editor.selectionModel.selectedText == "") {var indexText = document.text.subSequence(startOffset - 1, startOffset).toString()while (startOffset > 0 && nameMatch(indexText)) {searchText.insert(0, indexText)startOffset--indexText = document.text.subSequence(startOffset - 1, startOffset).toString()}} else {searchText.append(editor.selectionModel.selectedText)}if (project != null) {val lookup = obtainLookup(editor, project)for (methodList in UtilMethodsHandle.globalPsiMethods.values) {for (method in methodList) {var qualifiedName: String? = ""if (method.containingClass != null) {qualifiedName = method.containingClass!!.qualifiedName}LOG.info("actionPerformed: $method+$qualifiedName")if (!method.isValid) continue//检查元素是否有效,比如切换分支后就会失效//创建一个element,与第一种方案一样val element: LookupElement = LookupElementBuilder.create(method).withTypeText(qualifiedName).withIcon(MethodIcon)//.withIcon(AllIcons.Nodes.MethodReference).withBoldness(true)val item = CompletionResult.wrap(element, PlainPrefixMatcher(searchText.toString()), CompletionSorter.emptySorter())if (item != null) {//将element添加进去lookup.addItem(item.lookupElement, item.prefixMatcher)}}}lookup.addLookupListener(object : LookupListener {override fun itemSelected(event: LookupEvent) {//item选中事件,与val lookupElement = event.item as LookupElementif (lookupElement.psiElement is PsiMethod) {//如果选中的element是方法val psiMethod = lookupElement.psiElement as PsiMethod//得到上下文InsertionContextval insertionContext = InsertionContext(OffsetMap(document), Lookup.AUTO_INSERT_SELECT_CHAR, arrayOf(lookupElement), psiFile!!, editor, false)//val tailOffset = OffsetMap(document).getOffset(InsertionContext.TAIL_OFFSET)//如果是选中状态,计算开始位置需要减去字符长度if (startOffset == start) startOffset -= searchText.lengthdocument.insertString(startOffset, ".")document.insertString(insertionContext.tailOffset, "();")//导入所引用的类JavaCompletionUtil.insertClassReference(psiMethod.containingClass!!, psiFile, startOffset)//移动光标到代码尾部editor.caretModel.moveToOffset(insertionContext.tailOffset - 2)}}})lookup.showLookup()//显示弹窗}private fun obtainLookup(editor: Editor, project: Project): LookupImpl {val lookup = LookupManager.getInstance(project).createLookup(editor, LookupElement.EMPTY_ARRAY, "",DefaultArranger()) as LookupImpl/* if (editor.isOneLineMode) {lookup.setCancelOnClickOutside(true)lookup.setCancelOnOtherWindowOpen(true)}*///lookup.lookupFocusDegree = if (autopopup) LookupFocusDegree.UNFOCUSED else LookupFocusDegree.FOCUSEDreturn lookup}}
这里使用的代码补全弹窗是系统自带的弹窗,在这里说一下怎么找到各种UI相对应的类。
我们需要启用内部模式。在idea.properties里添加idea.is.internal=true
,保存并重启IDE。会看到Tool中多了一个选项Internal Actions,然后选择 UI -> UI Inspector,打开 UI 检查器,启用之后就可以以交互方式测试UI元素。查看时,将光标居中于UI元素上,使用Ctrl+Alt+鼠标左键
即可显示UI元素的内部描述.。
效果如图,可以看到相关的类,然后就可以再去找到具体的实现方法。
最终效果如下:
在这里插入图片描述
相关资料
IntelliJ Platform SDK
使用PSI分析Java代码
Intellij IDEA 插件开发秘籍
相关文章:
自定义IDEA代码补全插件
目标: 对于项目中的静态方法(主要是各种工具类里的静态方法),可以在输入方法名时直接提示相关的静态方法,选中后自动补全代码,并导入静态类。 设计: 初步构想,用户选择要导入的文…...
uniapp uview1.0 页面多个upload上传、回显之后处理数据
<view class"img-title w-s-color-3 f-28 row">商品图片</view><u-upload ref"images" :header"header" :file-list"fileListImages" :action"action" name"iFile" icon-name"camera"u…...
生活中的物理2——人类迷惑行为(用笔扎手)
1实验 材料 笔、手 实验 1、先用手轻轻碰一下笔尖(未成年人须家长监护) 2、再用另一只手碰碰笔尾 你发现了什么?? 2发现 你会发现碰笔尖的手明显比碰笔尾的手更痛 你想想为什么 3原理 压强f/s 笔尖的面积明显比笔尾的小 …...
vue3表格导入导出.xlsx
在这次使用时恰好整出来了,希望大家也能学习到,特此分享出来 使用前确保安装以下模块,最好全局配置element-plus ### 展示一下 ### ###导出选项 ### ###导入de数据 ### 安装的模块 npm install js-table2excel // 安装js-table2excel n…...
vscode dart语言出现蓝色波浪线
pubspec.yaml 注释掉:flutter_lints: ^2.0.0 analysis_options.yaml 注释掉:include: package:flutter_lints/flutter.yaml...
一种磁盘上循环覆盖文件策略
目录标题 1. 前言2. 软件设计流程思路3. 模拟测试3.1 分区准备工作3.2 模拟写数据3.3 测试 1. 前言 实际开发中经常需要存储数据, 无论是存储日志,还是二进制数据(图片,雷达数据或视频文件等), 不能一直存,是否存在一种策略: 当磁盘空间不足时…...
elementui消息弹出框MessageBox英文内容不换行问题
问题:当MessageBox内容为中文时,会自动换行,但当内容为英文时不会触发自动换行 如图,内容名称为英文时,名称太长会戳出提示框,不会自动换行 为数字英文会在英文数字处换行但是我们往往不需要它换行 解决方…...
WPF——样式和控件模板、数据绑定与校验转换
样式和控件模板 合并资源字典 Style简单样式的定义和使用 ControlTemplate控件模板的定义和使用 定义 使用 Trigger触发器 数据绑定与校验转换 数据绑定的设置 代码层实现绑定 数据模板DataTemplate xml文件的读取与显示 方法的返回值作为源绑定到控件中ObjectDataProvider L…...
服务器数据恢复-raid5故障导致上层分区无法访问的数据恢复案例
服务器数据恢复环境&故障: 一台服务器上3块硬盘组建了一组raid5磁盘阵列。服务器运行过程中有一块硬盘的指示灯变为红色,raid5磁盘阵列出现故障,服务器上层操作系统的分区无法识别。 服务器数据恢复过程: 1、将故障服务器上磁…...
石器时代H5小游戏架设教程
本文讲解石器时代 H5 之恐龙宝贝架设教程,想研究 H5 游戏如何实现,那请跟着此次教程学习在拥有小游戏源码的情况下该如何搭建起来 开始架设 1. 架设条件 石器时代架设需要准备: 一台linux 服务器,建议 CentOs 7.6 版本…...
计算机网络-网络协议
一、TCP/IP协议 作为一个小萌新,当然我无法将tcp/ip协议的大部分江山和盘托出,但是其中很多面试可能问到的知识,我觉得有必要总结一下! 首先,在学习tcp/ip协议之前,我们必须搞明白什么是tcp/ip协议。 1、…...
多维时序 | MATLAB实现KOA-CNN-BiGRU-Multihead-Attention多头注意力机制多变量时间序列预测
多维时序 | MATLAB实现KOA-CNN-BiGRU-Multihead-Attention多头注意力机制多变量时间序列预测 目录 多维时序 | MATLAB实现KOA-CNN-BiGRU-Multihead-Attention多头注意力机制多变量时间序列预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 MATLAB实现KOA-CNN-B…...
业务出海如何快速将站点搬迁到AWS云中?
随着国内市场趋于饱和,竞争压力越来越大,越来越多的企业选择出海,把业务放在海外做,从而追求更广阔的市场,获取更多客户。那都在讲出海,那怎么将站点完完整整的搬到海外呢?大家都会想࿰…...
ansible剧本playbook
Palybook组层部分 tasks 任务包含要在目标主机上执行的操作,使用模块定义这些操作,每个任务都是一个模块的调用variables变量:存储和传递数据,变量可以自定义,可以在palybook当中定义为全局变量,也可以在外部传参temp…...
.NET 中string类型的字符串内部化机制
当创建一个字符串时,如果具有相同字符序列的字符串已经存在于内存中,那么新创建的字符串会指向已经存在的那个字符串的内存地址,而不是创建一个全新的副本。这有助于节省内存,并提高字符串操作的效率。 因此相同内容的字符串变量…...
公共字段自动填充——后端
场景:当处理一些请求时,会重复的对数据库的某些字段进行赋值(如:在插入和更新某个物品时,需要更新该物品的更新时间和更新者的信息),这样会导致代码冗余。 如: 思路: 自…...
nginx upstream 6种负载均衡策略介绍
upstream参数 参数描述service反向服务地址加端口weight权重max_fails失败多少次,认为主机已经挂掉,踢出fail_timeout踢出后重新探测时间backup备用服务max_conns允许最大连接数slow_start当节点恢复,不立即加入 负载均衡策略 轮询&#x…...
基于Antd4 和React-hooks的项目开发
基于Antd4 和React-hooks的项目开发 https://github.com/dL-hx/react-cnode 项目依赖使用 react 16.13react-redux 7.xreact-router-dom 5.xredux 4.xantd 4axiosmoment 2.24 (日期格式化)qs 项目视图说明 首页主题详情用户列表用户详情关于 配置按需加载 https://3x.an…...
Spring中用到的设计模式
一、工厂模式 BeanFactory 1、简单工厂模型,是指由一个工厂对象决定创建哪一种产品类的实例,工厂类负责创建的对象较少,客户端只需要传入工厂类的参数,对于如何创建对象的逻辑不需要关心 优点: 只需传入一个正确的参数…...
常用网络接口自动化测试框架
(一)GUI界面测试工具:jmeter 1、添加线程组 2、添加http请求 3、为线程组添加察看结果树 4、写入接口参数并运行 5、在查看结果树窗口查看结果 6、多组数据可增加CSVDataSetConfig(添加.csv格式的文件,并在参数值里以${x}格式写入) 此时变量…...
【重点】【贪心】55.跳跃游戏
题目 法1:贪心 class Solution {public boolean canJump(int[] nums) {int maxIndex nums.length - 1;int curMaxIndex 0;for (int i 0; i < nums.length; i) {if (i < curMaxIndex) {curMaxIndex Math.max(i nums[i], curMaxIndex);if (curMaxIndex &…...
灰度化、二值化、边缘检测、轮廓检测
灰度化 定义 灰度图像是只含亮度信息,不含色彩信息的图像。灰度化处理是把彩色图像转换为灰度图像的过程,是图像处理中的基本操作。OpenCV 中彩色图像使用 BGR 格式。灰度图像中用 8bit 数字 0~255 表示灰度,如:0 表…...
基于JAVA的高校大学生创业管理系统 开源项目
目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统公告模块2.2 创业项目模块2.3 创业社团模块2.4 政府政策模块2.5 创业比赛模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 系统公告表3.2.2 创业项目表3.2.3 创业社团表3.2.4 政策表 四、系统展示五、核心代码5.…...
神经网络学习小记录76——Tensorflow2设置随机种子Seed来保证训练结果唯一
神经网络学习小记录76——Tensorflow2设置随机种子Seed来保证训练结果唯一 学习前言为什么每次训练结果不同什么是随机种子训练中设置随机种子 学习前言 好多同学每次训练结果不同,最大的指标可能会差到3-4%这样,这是因为随机种子没有设定导致的&#x…...
ai学习笔记-入门
目录 一、人工智能是什么?可以做什么? 人工智能(Artificial Intelligence): 人工智能的技术发展路线: 产业发展驱动因素:数据、算力、算法 二、人工智能这个工具的使用原理入门 神经网络⭕数学基础 1.神经网络的生物表示 …...
workflow系列教程(5-1)HTTP Server
往期教程 如果觉得写的可以,请给一个点赞关注支持一下 观看之前请先看,往期的博客教程,否则这篇博客没办法看懂 workFlow c异步网络库编译教程与简介 C异步网络库workflow入门教程(1)HTTP任务 C异步网络库workflow系列教程(2)redis任务 workflow系列教程(3)Series串联任务流…...
php-使用wangeditor实现富文本(完成图片上传)-npm
官网参考连接:快速开始 | wangEditor 样式: 一、新建一个临时文件夹test1和一个文件夹wangeditor 临时文件夹test1:临时存放通过npm下载的文件文件夹wangeditor:用于存放在临时文件夹test1拷贝的css和js 二、安装 editor 在确保有…...
mysql查看数据库中所有的表的建表语句
mysql查看数据库中所有的表: SHOW TABLES; 这条命令将返回数据库中所有表的列表。 如果要查看单个表的建表语句,可以使用以下命令: SHOW CREATE TABLE table_name; 其中,"table_name"为你要查看的表的名称。 如果…...
【Axure RP9】实现登入效验及实现左侧菜单栏跳转各页面
目录 一 效验简介 1.1 校验好处 1.2 应用场景 二 登入校验 2.1 效果 2.2 实现流程 三 左边菜单栏左侧菜单栏跳转各页面 3.1 效果 3.2 实现图 一 效验简介 1.1 校验好处 提高安全性: 在传统的用户名和密码登录的基础上,引入了另一种或多种验证…...
76. 最小覆盖子串。优化官方题解!
leetcode原题如下: 给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。 注意: 对于 t 中重复字符,我们寻找的子字符串中该字符数量…...
彩投网站建设/制作网站
BI是Business Intelligence(BI) 数据分析一般分为三个层面: (1)Memory内存分析层面 (2)BI分析层面 (3)Massive分析层面...
网页视频下载软件免费版/资源网站排名优化seo
给定两个整数 nnn 和 kkk,请你找到并输出能够整除 nnn 的第 kkk 小的正整数。 输入格式 一行,两个整数 nnn 和 kkk。 输出格式 输出能够整除 nnn 的第 kkk 小的整数。 如果不存在,则输出 −1−1−1。 数据范围 1≤n≤10151≤n≤10^{15}1≤…...
网站轮播效果怎么做的/东莞seo网站制作报价
函数:startswith()作用:判断字符串是否以指定字符或子字符串开头一、函数说明语法:string.startswith(str, beg0,endlen(string))或string[beg:end].startswith(str)参数说明:string: 被检测的字符串str: …...
浙江省住房和城乡建设厅网站/重庆网络推广公司
1.利用时间戳来获得随机数 利用System.currentTimeMillis()获得时间的位数,例如:个位,十位,百位…等等。 例如: int number1 (int)(System.currentTimeMillis() % 10); #获得时间个位数 int …...
毕业设计做 做交易网站/推广网
周一上班,发现苹果mac部分按键突然失灵!这可怎么办? 使用万能的重启大法,数字键 7,8,9 以及 m 等按键失灵,但是其他按键正常。 使用外接键盘发现是可以正常输入的,难道是笔记本键盘坏了?要去售…...
商城网站建设/手机百度极速版app下载安装
问题描述 请把纸条竖着放在桌⼦上,然后从纸条的下边向上⽅对折,压出折痕后再展 开。此时有1条折痕,突起的⽅向指向纸条的背⾯,这条折痕叫做“下”折痕 ;突起的⽅向指向纸条正⾯的折痕叫做“上”折痕。如果每次都从下边…...