一、背景与目标
在文本编辑器领域,Notepad++凭借其轻量级特性和强大的插件生态,成为开发者群体中的热门选择。作为基于Scintilla组件构建的编辑器(Scintilla是开源的代码编辑控件,被Notepad++、Geany等知名工具广泛采用),其插件开发体系具有独特的技术特点。
本文是AI辅助开发系列的第二篇,前序文章已实现通过WinHttp封装C++访问大模型的SDK。本篇将聚焦于构建Notepad++插件开发框架,重点解决以下技术要点:
- 标准化插件工程配置流程
- Scintilla控件交互接口的现代化改造
- 编辑器核心功能的深度集成
本文重点讲解插件开发的核心技术路径,重点攻克Scintilla控件交互的工程化实现,为后续AI功能集成奠定基础。
二、环境配置与工程搭建
1. 开发环境准备
-
模板工程获取 :
使用官方插件模板加速开发:bashgit clone https://github.com/npp-plugins/plugintemplate.git
-
编译配置要点 :
- Visual Studio工程需匹配Notepad++位数版本(x86/x64)
- 输出目录指向Notepad++插件目录(例:
C:\Program Files\Notepad++\plugins\
)
2. 工程目录结构
成功编译后生成核心文件:
AiCoder/
├── AiCoder.dll # 插件主程序
├── AiCoder.exp
├── AiCoder.lib
├── AiCoder.pdb
└── config.ini # 配置文件

3. 版本验证

到这里,插件就编译安装成功了,此时重启Notepad++程序,就可以看到我们刚才编译的插件了。
三、核心开发流程
1. 插件基础配置(四步法)
Step 1. 定义插件标识
cpp
// PluginDefinition.h
const TCHAR NPP_PLUGIN_NAME[] = TEXT("AiCoder");
Step 2. 声明命令数量
cpp
const int nbFunc = 2; // 支持两个功能命令
Step 3. 配置命令映射
cpp
// PluginDefinition.cpp
setCommand(0, L"About AiCoder", HelloAiCoder, NULL, false);
setCommand(1, L"选中内容问AI", AskBySelectedText, NULL, false);
Step 4. 实现核心逻辑
cpp
void AskBySelectedText() {
Scintilla::ScintillaCall call;
call.SetFnPtr((intptr_t)nppData._scintillaMainHandle);
auto text = call.GetSelText(); // 获取选中文本
// 后续处理逻辑...
}
Scintilla的Windows版本是一个标准的Windows控件,因此早期其主要编程接口通过Windows消息实现。早期版本的Scintilla模仿了标准Windows Edit和RichEdit控件定义的大部分API,但这些API现已被弃用,转而采用Scintilla自身更一致的API。除了实现常规Edit控件功能的消息外,Scintilla还支持语法样式控制、代码折叠、标记、自动补全和调用提示等功能。
该ScintillaCall的API还提供了适用于C++的类型安全绑定,通过ScintillaTypes.h、ScintillaMessages.h、ScintillaStructures.h和ScintillaCall.h头文件以及call/ScintillaCall.cxx实现。虽然上述头文件可独立使用,但ScintillaCall通过将消息封装为方法简化了其他头文件的使用,同时减少了大量类型转换操作。当调用失败时,ScintillaCall会抛出Scintilla::Failure
异常。
因此我从官网下载了一份包含ScintillaCall的代码,并引入目录include
和call
到工程里面。
补充说明项目模板说明文档
连你奶奶都能轻松上手!
本模板旨在让插件开发变得尽可能简单易行。
通过仅编辑两个文件(PluginDefinition.h 和 PluginDefinition.cpp),即可通过4个步骤完成基础插件开发:
- 在"PluginDefinition.h"中定义插件名称
- 在"PluginDefinition.h"中定义插件命令数量
- 在"PluginDefinition.cpp"中自定义插件命令名称、关联函数名(及其他可选配置)
- 实现关联函数
只需按照以下4个步骤(文件中已标注注释)操作即可:
//-------------------------------------//
//-- 步骤1. 定义插件名称(必填) --//
//-------------------------------------//
//-----------------------------------------------//
//-- 步骤2. 定义插件命令数量(必填) --//
//-----------------------------------------------//
//--------------------------------------------//
//-- 步骤3. 配置插件命令参数(可选) --//
//--------------------------------------------//
//----------------------------------------------//
//-- 步骤4. 实现关联函数(核心逻辑) --//
//----------------------------------------------//
如有任何疑问或建议,请在此处发帖:
https://community.notepad-plus-plus.org/category/5/notepad-plugin-development
更多插件开发指南请参考:
https://npp-user-manual.org/docs/plugins/#how-to-develop-a-plugin
四、关键技术解析
1. Notepad++与Scintilla架构关系
Notepad++底层采用Scintilla编辑器组件实现文本处理核心功能,该组件具有以下特性:
- 跨平台支持(Windows/GTK+/Cocoa)
- 高性能语法高亮与代码折叠
- 可扩展的插件接口体系
- 消息驱动型交互架构
Notepad++ Scintilla控件 ScintillaCall接口 SendMessage封装 插件系统 AI功能扩展
2. Scintilla交互接口演进
传统消息模式(SendMessage)
cpp
::SendMessage(hScintilla, SCI_GETSELTEXT, 0, (LPARAM)buffer);
传统开发多采用SendMessage方式,存在以下痛点:
- 参数类型转换繁琐
- 接口文档依赖性强
- 代码可维护性差
- 类型不安全、维护成本高
现代封装模式(ScintillaCall)
cpp
ScintillaCall call;
call.SetFnPtr((intptr_t)hScintilla);
auto pos = call.GetCurrentPos(); // 类型安全调用
- 优势 :
- 强类型接口约束
- 异常处理机制(抛出
Scintilla::Failure
) - 代码可读性显著提升
- 支持IDE智能提示
3.改造ScintillaCall
其中ScintillaCall.h
是声明和定义,ScintillaCall.cxx
是源码实现。因此,如果对接口不满意,可以直接修改源代码。
ScintillaCall最重要的是void SetFnPtr(FunctionDirect fn_, intptr_t ptr_) noexcept;
函数,其功能是设置目标Scintilla控件和消息处理函数指针。
考虑到做一个简单好用的Scintilla
接口类,这初始化还得实现一个函数指针,有点费劲,所以我改了下,改成默认使用系统SendMessage
了,这样使用只有设置Scintilla控件句柄即可,如下:
cpp
intptr_t __cdecl Scintilla::SciSendMessage(
intptr_t hWnd,
unsigned int Msg,
uintptr_t wParam,
intptr_t lParam,
int* pStatus
)
{
#ifdef WIN32
LRESULT lRet = ::SendMessage((HWND)hWnd, Msg, wParam, lParam);
#else
// 待补充
#endif
if (pStatus) *pStatus = 0;
return lRet;
}
ScintillaCall::ScintillaCall() noexcept : fn(Scintilla::SciSendMessage), ptr(0), statusLastCall(Status::Ok) {
}
void ScintillaCall::SetFnPtr(intptr_t ptr_, FunctionDirect fn_/* = nullptr*/) noexcept {
fn = fn_;
if (fn == nullptr) fn = SciSendMessage;
ptr = ptr_;
}
特性 | SendMessage | ScintillaCall |
---|---|---|
类型安全 | × (需手动转换) | √ (编译期检查) |
异常处理 | 手动错误码检查 | 自动异常抛出 |
代码可读性 | 低(大量类型转换) | 高(方法名自解释) |
维护成本 | 高 | 低 |
4. ScintillaCall集成实践
从Scintilla官网获取开发包,集成关键头文件:
include/
├── ScintillaCall.h # 核心封装类
├── ScintillaMessages.h # 消息常量
└── ScintillaTypes.h # 类型定义
自定义消息处理适配器:
cpp
intptr_t Scintilla::SciSendMessage(intptr_t hWnd, unsigned Msg,
uintptr_t wParam, intptr_t lParam) {
return ::SendMessage((HWND)hWnd, Msg, wParam, lParam);
}
五、后续规划
本文实现了插件基础框架与Scintilla交互层建设,下一步将:
- 集成AI模型调用接口
- 实现AI问答功能
六、扩展阅读
通过本文所述方法,开发者可快速构建符合现代工程标准的Notepad++插件,为后续深度功能扩展奠定坚实基础。