【AI工具开发】Notepad++插件开发实践:从基础交互到ScintillaCall集成

一、背景与目标

在文本编辑器领域,Notepad++凭借其轻量级特性和强大的插件生态,成为开发者群体中的热门选择。作为基于Scintilla组件构建的编辑器(Scintilla是开源的代码编辑控件,被Notepad++、Geany等知名工具广泛采用),其插件开发体系具有独特的技术特点。

本文是AI辅助开发系列的第二篇,前序文章已实现通过WinHttp封装C++访问大模型的SDK。本篇将聚焦于构建Notepad++插件开发框架,重点解决以下技术要点:

  • 标准化插件工程配置流程
  • Scintilla控件交互接口的现代化改造
  • 编辑器核心功能的深度集成

本文重点讲解插件开发的核心技术路径,重点攻克Scintilla控件交互的工程化实现,为后续AI功能集成奠定基础。

二、环境配置与工程搭建

1. 开发环境准备

  • 模板工程获取
    使用官方插件模板加速开发:

    bash 复制代码
    git 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的代码,并引入目录includecall到工程里面。

补充说明项目模板说明文档

连你奶奶都能轻松上手!

本模板旨在让插件开发变得尽可能简单易行。

通过仅编辑两个文件(PluginDefinition.h 和 PluginDefinition.cpp),即可通过4个步骤完成基础插件开发:

  1. 在"PluginDefinition.h"中定义插件名称
  2. 在"PluginDefinition.h"中定义插件命令数量
  3. 在"PluginDefinition.cpp"中自定义插件命令名称、关联函数名(及其他可选配置)
  4. 实现关联函数

只需按照以下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

https://npp-user-manual.org/docs/plugin-communication/

四、关键技术解析

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交互层建设,下一步将:

  1. 集成AI模型调用接口
  2. 实现AI问答功能

六、扩展阅读

通过本文所述方法,开发者可快速构建符合现代工程标准的Notepad++插件,为后续深度功能扩展奠定坚实基础。

相关推荐
东风西巷28 分钟前
Balabolka:免费高效的文字转语音软件
前端·人工智能·学习·语音识别·软件需求
非门由也39 分钟前
《sklearn机器学习——管道和复合估计器》联合特征(FeatureUnion)
人工智能·机器学习·sklearn
l12345sy39 分钟前
Day21_【机器学习—决策树(1)—信息增益、信息增益率、基尼系数】
人工智能·决策树·机器学习·信息增益·信息增益率·基尼指数
非门由也39 分钟前
《sklearn机器学习——管道和复合估算器》异构数据的列转换器
人工智能·机器学习·sklearn
计算机毕业设计指导1 小时前
基于ResNet50的智能垃圾分类系统
人工智能·分类·数据挖掘
飞哥数智坊1 小时前
终端里用 Claude Code 太难受?我把它接进 TRAE,真香!
人工智能·claude·trae
小王爱学人工智能1 小时前
OpenCV的阈值处理
人工智能·opencv·计算机视觉
新智元2 小时前
刚刚,光刻机巨头 ASML 杀入 AI!豪掷 15 亿押注「欧版 OpenAI」,成最大股东
人工智能·openai
机器之心2 小时前
全球图生视频榜单第一,爱诗科技PixVerse V5如何改变一亿用户的视频创作
人工智能·openai
新智元2 小时前
2025年了,AI还看不懂时钟!90%人都能答对,顶尖AI全军覆没
人工智能·openai