WPS中代码段的识别方法及JS宏实现

news2025/7/19 11:32:19

在WPS中,文档的基本结构可以通过对象模型来理解:

(1)Document对象:表示整个文档

(2)Range对象:表示文档中的一段连续区域,可以是一个字符、一个句子或整个文档

(3)Paragraph对象:表示文档中的段落

(4)Selection对象:表示当前用户选中的区域

为了更高效的工作,我们希望通过WPS JS宏可以帮助你识别文档中的代码段并添加样式快速实现美化文档效果。这时,就要先确定文档中的内容是否为代码段,有很多的方法。

WPS改造系列文章:

1.在WPS中通过JavaScript宏(JSA)调用本地DeepSeek API优化文档教程:在WPS中通过JavaScript宏(JSA)调用本地DeepSeek API优化文档教程_wps js宏-CSDN博客 

2.在WPS中通过JavaScript宏(JSA)调用DeepSeek官网API优化文档教程:在WPS中通过JavaScript宏(JSA)调用DeepSeek官网API优化文档教程_wps js宏官方文档-CSDN博客 

3.在WPS中通过JavaScript宏(JSA)调用DeepSeek官方API自动识别标题级别和目录:在WPS中通过JavaScript宏(JSA)调用DeepSeek官方API自动识别标题级别和目录_wps js宏官方文档-CSDN博客 

4.基于Deepseek对WPS文档自动设置标题格式的代码优化:基于Deepseek对WPS文档自动设置标题格式的代码优化_deepseek识别一级标题-CSDN博客 

5.基于JSA宏对WPS文档中所有图片设置为居中显示的代码实现:基于JSA宏对WPS文档中所有图片设置为居中显示的代码实现_wps js宏 图片-CSDN博客 

6.WPS中代码段的识别方法及JS宏实现:WPS中代码段的识别方法及JS宏实现_wps js range-CSDN博客 

一、基于标记的代码段识别方法

1.方法概述

基于标记的方法,即通过特定的开始和结束标记来识别代码块。这种方法适用于已经使用特定标记(如 ```、/* */ 等)标识的代码。

这种方法需要定义多种可能的代码块标记,包括常见的Markdown、HTML和注释风格标记,使用正则表达式来查找文档中的代码块,之后再为每个识别出的代码块应用统一的样式,包括边框、背景色和等宽字体。

为什么提示 “未在文档中找到代码块!”?

这可能有以下几个原因:

(1)文档中确实没有使用预定义标记的代码块

(2)代码块使用了其他标记或格式

(3)正则表达式匹配不够灵活,无法识别某些格式的代码块

2.改进代码块识别的方法

以下是几种可以改进代码块识别的方法:

(1)增加更多的标记类型:可以扩展codeStartMarkers和codeEndMarkers数组,添加更多可能的代码块标记。添加了常见的编程和 HTML 标记(如 <?php、<script 等),可以根据需要继续扩展这个列表。

(2)基于代码特征识别:除了标记外,还可以基于代码的一些特征来识别,如缩进、特定关键字等。例如,通过检测连续缩进的段落来识别代码块,设置了一个缩进阈值(4 个空格),超过这个阈值的段落会被视为代码的一部分。例如,添加关键字检测检查段落是否以常见的代码关键字(如function、if、for等)开头,这有助于识别没有明显缩进但确实是代码的段落。

(3)使用AI辅助识别:如果需要更智能的识别,可以考虑调用AI API来分析文本是否为代码。

3.代码示例

// 高亮代码块主函数 - 供自定义功能区按钮调用

function highlightCodeBlocks() {

    try {

        // 获取当前文档

        let doc = ThisDocument;

        if (!doc) {

            alert("未找到当前文档!");

            return;

        }

        

        // 定义代码块样式配置

        let codeBlockStyle = {

            borderColor: "#cccccc",

            borderWidth: 1,

            backgroundColor: "#f7f7f9",

            fontFamily: "Consolas, 'Courier New', monospace",

            fontSize: 10.5

        };

        

        // 获取文档内容

        let content = doc.Content.Text;

        

        // 方法1: 使用标记识别代码块

        let codeBlocks = findCodeBlocksByMarkers(content);

        

        // 方法2: 识别连续缩进的段落作为代码块

        if (codeBlocks.length === 0) {

            codeBlocks = findCodeBlocksByIndentation(doc);

        }

        

        // 如果没有找到代码块,提示用户

        if (codeBlocks.length === 0) {

            alert("未在文档中找到代码块!");

            return;

        }

        

        // 处理每个代码块

        for (let block of codeBlocks) {

            try {

                // 创建Range对象

                let codeRange = doc.Range(block.start, block.end);

                

                // 应用代码块样式

                applyCodeBlockStyle(codeRange, codeBlockStyle);

            } catch (e) {

                console.error(`处理代码块时出错: ${e.message}`);

            }

        }

        

        alert(`成功处理 ${codeBlocks.length} 个代码块!`);

    } catch (e) {

        alert(`执行过程中出错: ${e.message}`);

    }

}



// 方法1: 使用标记识别代码块

function findCodeBlocksByMarkers(content) {

    // 定义代码块可能的开始和结束标记

    let codeStartMarkers = [

        "```", "/*", "<code>", "# 代码开始", "// 代码开始",

        "<?php", "<script", "<style", "<html"

    ];

    let codeEndMarkers = [

        "```", "*/", "</code>", "# 代码结束", "// 代码结束",

        "?>", "</script>", "</style>", "</html>"

    ];

    

    let codeBlocks = [];

    

    // 使用正则表达式查找代码块

    for (let i = 0; i < codeStartMarkers.length; i++) {

        let startMarker = codeStartMarkers[i];

        let endMarker = codeEndMarkers[i];

        

        // 构建正则表达式

        let regexPattern = `${escapeRegExp(startMarker)}(.*?)${escapeRegExp(endMarker)}`;

        let regex = new RegExp(regexPattern, 'gs');

        

        let match;

        while ((match = regex.exec(content)) !== null) {

            codeBlocks.push({

                start: match.index,

                end: match.index + match[0].length,

                content: match[0]

            });

        }

    }

    

    return codeBlocks;

}



// 方法2: 识别连续缩进的段落作为代码块

function findCodeBlocksByIndentation(doc) {

    let codeBlocks = [];

    let currentCodeBlock = null;

    let indentThreshold = 4; // 至少4个空格的缩进

    

    // 遍历文档中的每个段落

    for (let i = 1; i <= doc.Paragraphs.Count; i++) {

        let para = doc.Paragraphs(i);

        let firstLineIndent = para.Format.FirstLineIndent;

        let leftIndent = para.Format.LeftIndent;

        let totalIndent = Math.abs(firstLineIndent) + Math.abs(leftIndent);

        

        // 检查段落是否以常见代码关键字开头

        let isCodeLine = false;

        let paraText = para.Range.Text.trim();

        let codeKeywords = ["function", "class", "def", "if", "for", "while", "import", "package"];

        

        for (let keyword of codeKeywords) {

            if (paraText.startsWith(keyword)) {

                isCodeLine = true;

                break;

            }

        }

        

        // 如果段落有足够的缩进或者以代码关键字开头

        if (totalIndent >= indentThreshold || isCodeLine) {

            if (!currentCodeBlock) {

                // 开始一个新的代码块

                currentCodeBlock = {

                    start: para.Range.Start,

                    end: para.Range.End,

                    paragraphCount: 1

                };

            } else {

                // 扩展当前代码块

                currentCodeBlock.end = para.Range.End;

                currentCodeBlock.paragraphCount++;

            }

        } else if (currentCodeBlock) {

            // 如果当前有代码块且当前段落不是代码行,则结束当前代码块

            // 只添加至少包含2个段落的代码块,避免误判

            if (currentCodeBlock.paragraphCount >= 2) {

                codeBlocks.push(currentCodeBlock);

            }

            currentCodeBlock = null;

        }

    }

    

    // 添加最后一个代码块(如果有)

    if (currentCodeBlock && currentCodeBlock.paragraphCount >= 2) {

        codeBlocks.push(currentCodeBlock);

    }

    

    return codeBlocks;

}



// 应用代码块样式

function applyCodeBlockStyle(range, style) {

    // 应用段落格式

    range.ParagraphFormat.Borders.Enable = true;

    range.ParagraphFormat.Borders.OutsideLineStyle = wdLineStyleSingle;

    range.ParagraphFormat.Borders.OutsideLineWidth = style.borderWidth;

    range.ParagraphFormat.Borders.OutsideColor = RGBToColor(style.borderColor);

    

    // 应用字符格式

    range.Font.Name = style.fontFamily;

    range.Font.Size = style.fontSize;

    

    // 应用底纹

    range.Shading.BackgroundPatternColor = RGBToColor(style.backgroundColor);

    

    // 增加段落间距

    range.ParagraphFormat.SpaceBefore = 6;

    range.ParagraphFormat.SpaceAfter = 6;

}



// 辅助函数:将RGB颜色字符串转换为WPS颜色值

function RGBToColor(rgbStr) {

    // 移除可能的#符号

    rgbStr = rgbStr.replace('#', '');

    

    // 解析RGB值

    let r = parseInt(rgbStr.substring(0, 2), 16);

    let g = parseInt(rgbStr.substring(2, 4), 16);

    let b = parseInt(rgbStr.substring(4, 6), 16);

    

    // WPS使用BGR顺序

    return r + (g << 8) + (b << 16);

}



// 辅助函数:转义正则表达式特殊字符

function escapeRegExp(string) {

    return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');

}

二、基于文本特征的代码段识别方法

如果我是直接将代码文本复制到文档中,没有使用任何特殊标记(如 ``` 或缩进)。这种情况下,我们需要更智能的方式来识别代码段。

我设计了一种基于文本特征的代码段识别方法,通过分析文本的语言特征、结构模式和统计特性来判断是否为代码。这种方法不需要用户添加任何特殊标记,只需要将纯代码文本粘贴到文档中即可。

1.智能代码识别原理

通过使用了以下策略来识别纯文本代码:

(1)特征库匹配:内置了多种编程语言的特征库,包括关键字、操作符和特殊符号

(2)统计分析:分析文本中代码特征的出现频率,超过一定阈值则认为是代码

(3)连续段落检查:要求代码至少连续出现 3 行,避免误判

(4)文本过滤:排除包含 URL、邮箱或大量中文的段落,这些通常不是代码

(5)多语言支持:目前支持通用代码、Python、JavaScript 和 Java,可以根据需要扩展更多语言

2.主要改进思路

(1)增强语言的特征库:以下以JavaScript为例,添加更多JavaScript关键字和操作符,增加对JavaScript注释的特殊处理。

(2)降低识别阈值:将JavaScript的最小特征匹配数从4降低到2,将连续代码行要求从3降低到2。

(3)改进注释处理:专门添加了对//、/*和*/的检查,直接将注释行识别为代码的一部分

(4)优化中文检测:将中文阈值从20%提高到30%,减少误判。

3.代码示例

// 高亮代码块主函数 - 供自定义功能区按钮调用

function highlightCodeBlocks() {

    try {

        // 获取当前文档

        let doc = ThisDocument;

        if (!doc) {

            alert("未找到当前文档!");

            return;

        }

        

        // 定义代码块样式配置

        let codeBlockStyle = {

            borderColor: "#cccccc",

            borderWidth: 1,

            backgroundColor: "#f7f7f9",

            fontFamily: "Consolas, 'Courier New', monospace",

            fontSize: 10.5

        };

        

        // 使用智能代码识别

        let codeBlocks = findCodeBlocksByAI(doc);

        

        // 如果没有找到代码块,提示用户

        if (codeBlocks.length === 0) {

            alert("未在文档中找到可识别的代码块!");

            return;

        }

        

        // 处理每个代码块

        for (let block of codeBlocks) {

            try {

                // 创建Range对象

                let codeRange = doc.Range(block.start, block.end);

                

                // 应用代码块样式

                applyCodeBlockStyle(codeRange, codeBlockStyle);

            } catch (e) {

                console.error(`处理代码块时出错: ${e.message}`);

            }

        }

        

        alert(`成功识别并处理 ${codeBlocks.length} 个代码块!`);

    } catch (e) {

        alert(`执行过程中出错: ${e.message}`);

    }

}



// 智能代码识别方法

function findCodeBlocksByAI(doc) {

    let codeBlocks = [];

    let paragraphs = doc.Paragraphs;

    

    // 代码特征库 - 增强了JavaScript识别能力

    let codePatterns = [

        // JavaScript 特征 - 增强版

        {

            name: "javascript",

            keywords: [

                "function", "class", "let", "const", "var", "if", "else", "for", "while",

                "return", "import", "export", "async", "await", "try", "catch", "finally",

                "switch", "case", "default", "break", "continue", "this", "new", "delete"

            ],

            operators: [

                "==", "===", "!=", "!==", ">=", "<=", "++", "--", "&&", "||", "=>", "=",

                "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", "&=", "^=", "|="

            ],

            symbols: [

                "{", "}", "[", "]", "(", ")", ";", ":", ",", "`", "'", "\"", ".", "..", "...",

                "?", "??", "?."

            ],

            comments: ["//", "/*", "*/"], // 专门处理注释

            minOccurrences: 2, // 降低阈值,只需满足2个特征

            minConsecutive: 2 // 只需连续2行

        },

        // 通用代码特征

        {

            name: "general",

            keywords: ["function", "class", "def", "return", "if", "else", "for", "while", "import", "package"],

            operators: ["==", "!=", "=>", "++", "--", "&&", "||", "=>", "->", "::"],

            symbols: ["{", "}", "[", "]", "(", ")", ";", ":", "=", ","],

            minOccurrences: 3,

            minConsecutive: 3

        },

        // Python 特征

        {

            name: "python",

            keywords: ["def", "class", "if", "else", "elif", "for", "while", "in", "range", "return", "import", "from", "as"],

            operators: ["==", "!=", ">=", "<=", "=", "+=", "-=", "*=", "/=", "%=", "//=", "**="],

            symbols: [":", "#", "(", ")", "[", "]", "{", "}", ","],

            indentation: true,

            minOccurrences: 3,

            minConsecutive: 3

        },

        // Java 特征

        {

            name: "java",

            keywords: ["public", "private", "protected", "class", "interface", "void", "static", "if", "else", "for", "while", "return", "import", "package"],

            operators: ["==", "!=", ">=", "<=", "++", "--", "&&", "||", "=", "+=", "-=", "*=", "/=", "%="],

            symbols: ["{", "}", "[", "]", "(", ")", ";", ":", ",", "\"", "'"],

            minOccurrences: 3,

            minConsecutive: 3

        }

    ];

    

    // 分析每个段落,寻找代码块

    let currentBlock = null;

    let consecutiveCodeLines = 0;

    

    for (let i = 1; i <= paragraphs.Count; i++) {

        let para = paragraphs(i);

        let text = para.Range.Text.trim();

        

        // 跳过空段落

        if (text.length === 0) {

            if (currentBlock) {

                consecutiveCodeLines++;

            }

            continue;

        }

        

        // 检查当前段落是否为代码

        let isCode = analyzeParagraph(text, codePatterns);

        

        if (isCode) {

            consecutiveCodeLines++;

            

            if (!currentBlock) {

                // 开始一个新的代码块

                currentBlock = {

                    start: para.Range.Start,

                    end: para.Range.End,

                    paragraphCount: 1

                };

            } else {

                // 扩展当前代码块

                currentBlock.end = para.Range.End;

                currentBlock.paragraphCount++;

            }

        } else {

            // 如果当前有代码块且当前段落不是代码行,则结束当前代码块

            if (currentBlock && consecutiveCodeLines >= codePatterns[0].minConsecutive) {

                codeBlocks.push(currentBlock);

            }

            currentBlock = null;

            consecutiveCodeLines = 0;

        }

    }

    

    // 添加最后一个代码块(如果有)

    if (currentBlock && consecutiveCodeLines >= codePatterns[0].minConsecutive) {

        codeBlocks.push(currentBlock);

    }

    

    return codeBlocks;

}



// 分析段落是否为代码 - 改进版

function analyzeParagraph(text, codePatterns) {

    // 检查是否包含URL、邮箱等非代码文本

    if (text.match(/https?:\/\/\S+|www\.\S+|\S+@\S+\.\S+/)) {

        return false;

    }

    

    // 检查是否包含中文(超过30%的字符是中文可能不是代码)

    let chineseChars = text.match(/[\u4e00-\u9fa5]/g);

    if (chineseChars && chineseChars.length / text.length > 0.3) {

        return false;

    }

    

    // 特殊处理JavaScript注释行

    if (text.startsWith("//") || text.startsWith("/*") || text.endsWith("*/")) {

        return true; // 直接认为注释行是代码的一部分

    }

    

    // 检查是否符合任意一种代码模式

    for (let pattern of codePatterns) {

        let score = 0;

        

        // 检查关键字

        for (let keyword of pattern.keywords) {

            // 确保关键字是独立的,而不是其他单词的一部分

            let regex = new RegExp(`\\b${keyword}\\b`);

            if (regex.test(text)) {

                score++;

            }

        }

        

        // 检查操作符

        for (let operator of pattern.operators) {

            if (text.includes(operator)) {

                score++;

            }

        }

        

        // 检查符号

        for (let symbol of pattern.symbols) {

            if (text.includes(symbol)) {

                score++;

            }

        }

        

        // 检查缩进(仅对Python)

        if (pattern.indentation && (text.startsWith('    ') || text.startsWith('\t'))) {

            score++;

        }

        

        // 如果得分超过阈值,认为是代码

        if (score >= pattern.minOccurrences) {

            return true;

        }

    }

    

    return false;

}



// 应用代码块样式

function applyCodeBlockStyle(range, style) {

    // 应用段落格式

    range.ParagraphFormat.Borders.Enable = true;

    range.ParagraphFormat.Borders.OutsideLineStyle = wdLineStyleSingle;

    range.ParagraphFormat.Borders.OutsideLineWidth = style.borderWidth;

    range.ParagraphFormat.Borders.OutsideColor = RGBToColor(style.borderColor);

    

    // 应用字符格式

    range.Font.Name = style.fontFamily;

    range.Font.Size = style.fontSize;

    

    // 应用底纹

    range.Shading.BackgroundPatternColor = RGBToColor(style.backgroundColor);

    

    // 增加段落间距

    range.ParagraphFormat.SpaceBefore = 6;

    range.ParagraphFormat.SpaceAfter = 6;

}



// 辅助函数:将RGB颜色字符串转换为WPS颜色值

function RGBToColor(rgbStr) {

    // 移除可能的#符号

    rgbStr = rgbStr.replace('#', '');

    

    // 解析RGB值

    let r = parseInt(rgbStr.substring(0, 2), 16);

    let g = parseInt(rgbStr.substring(2, 4), 16);

    let b = parseInt(rgbStr.substring(4, 6), 16);

    

    // WPS使用BGR顺序

    return r + (g << 8) + (b << 16);

}



// 辅助函数:转义正则表达式特殊字符

function escapeRegExp(string) {

    return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');

}

4.使用方法

(1)将代码添加到WPS JS宏编辑器

(2)在自定义功能区中添加对highlightCodeBlocks函数的调用

(3)将纯代码文本复制粘贴到文档中

(4)点击自定义功能区中的按钮执行宏

这个版本应该能够识别没有任何标记的纯代码文本,并为其添加边框和背景色,使其在文档中明显区分出来。如果识别效果不理想,你可以调整findCodeBlocksByAI函数中的参数,如增加语言特征库或调整阈值。

三、基于密度结构识别方法

如果现有的识别算法还不够灵活,无法准确识别你的代码。我再提供一个更简单直接的版本,这个版本会尝试通过代码的密度结构来识别,而不是依赖特定的关键字

1.新的代码识别方法

(1)计算每行的符号密度(如括号、分号等)

(2)检查是否有一致的缩进模式

(3)寻找代码特有的结构模式(如函数定义、循环等)

2.这个版本的改进

(1)基于符号密度的识别:计算每行中代码特有的符号数量,超过阈值即认为是代码。

(2)结构模式匹配:使用正则表达式识别常见的代码结构(函数定义、循环等)。

(3)宽松的条件:连续2行代码即可识别为代码块,允许代码块中包含少量空行,降低了对特定关键字的依赖。

(4)更好的注释处理:直接将注释行识别为代码的一部分。

3.代码示例

// 高亮代码块主函数 - 供自定义功能区按钮调用

function highlightCodeBlocks() {

    try {

        // 获取当前文档

        let doc = ThisDocument;

        if (!doc) {

            alert("未找到当前文档!");

            return;

        }

        

        // 定义代码块样式配置

        let codeBlockStyle = {

            borderColor: "#cccccc",

            borderWidth: 1,

            backgroundColor: "#f7f7f9",

            fontFamily: "Consolas, 'Courier New', monospace",

            fontSize: 10.5

        };

        

        // 使用基于结构和密度的代码识别

        let codeBlocks = findCodeBlocksByStructure(doc);

        

        // 如果没有找到代码块,提示用户

        if (codeBlocks.length === 0) {

            alert("未在文档中找到可识别的代码块!");

            return;

        }

        

        // 处理每个代码块

        for (let block of codeBlocks) {

            try {

                // 创建Range对象

                let codeRange = doc.Range(block.start, block.end);

                

                // 应用代码块样式

                applyCodeBlockStyle(codeRange, codeBlockStyle);

            } catch (e) {

                console.error(`处理代码块时出错: ${e.message}`);

            }

        }

        

        alert(`成功识别并处理 ${codeBlocks.length} 个代码块!`);

    } catch (e) {

        alert(`执行过程中出错: ${e.message}`);

    }

}



// 基于结构和密度的代码识别方法

function findCodeBlocksByStructure(doc) {

    let codeBlocks = [];

    let paragraphs = doc.Paragraphs;

    

    // 代码特征符号

    let codeSymbols = ["{", "}", "[", "]", "(", ")", ";", ":", "=", "+", "-", "*", "/", "%", "&", "|", "^", "!", "~", "<", ">", ","];

    

    // 代码结构模式

    let codePatterns = [

        /^\s*function\s/,

        /^\s*class\s/,

        /^\s*(let|const|var)\s/,

        /^\s*if\s*\(/,

        /^\s*for\s*\(/,

        /^\s*while\s*\(/,

        /^\s*switch\s*\(/,

        /^\s*return\s/,

        /^\s*def\s/,

        /^\s*class\s/,

        /^\s*import\s/,

        /^\s*from\s/,

        /^\s*public\s/,

        /^\s*private\s/,

        /^\s*protected\s/

    ];

    

    // 分析每个段落,寻找代码块

    let currentBlock = null;

    let consecutiveCodeLines = 0;

    

    for (let i = 1; i <= paragraphs.Count; i++) {

        let para = paragraphs(i);

        let text = para.Range.Text.trim();

        

        // 跳过空段落

        if (text.length === 0) {

            if (currentBlock) {

                consecutiveCodeLines++;

                // 空行允许,但连续空行过多则结束代码块

                if (consecutiveCodeLines > 3) {

                    if (currentBlock.paragraphCount >= 2) {

                        codeBlocks.push(currentBlock);

                    }

                    currentBlock = null;

                    consecutiveCodeLines = 0;

                }

            }

            continue;

        }

        

        // 检查当前段落是否为代码

        let isCode = analyzeLineAsCode(text, codeSymbols, codePatterns);

        

        if (isCode) {

            consecutiveCodeLines++;

            

            if (!currentBlock) {

                // 开始一个新的代码块

                currentBlock = {

                    start: para.Range.Start,

                    end: para.Range.End,

                    paragraphCount: 1

                };

            } else {

                // 扩展当前代码块

                currentBlock.end = para.Range.End;

                currentBlock.paragraphCount++;

            }

        } else {

            // 如果当前有代码块且当前段落不是代码行,则结束当前代码块

            if (currentBlock && currentBlock.paragraphCount >= 2) {

                codeBlocks.push(currentBlock);

            }

            currentBlock = null;

            consecutiveCodeLines = 0;

        }

    }

    

    // 添加最后一个代码块(如果有)

    if (currentBlock && currentBlock.paragraphCount >= 2) {

        codeBlocks.push(currentBlock);

    }

    

    return codeBlocks;

}



// 分析行是否为代码

function analyzeLineAsCode(text, codeSymbols, codePatterns) {

    // 检查是否包含URL、邮箱等非代码文本

    if (text.match(/https?:\/\/\S+|www\.\S+|\S+@\S+\.\S+/)) {

        return false;

    }

    

    // 检查是否包含大量中文(超过30%的字符是中文可能不是代码)

    let chineseChars = text.match(/[\u4e00-\u9fa5]/g);

    if (chineseChars && chineseChars.length / text.length > 0.3) {

        return false;

    }

    

    // 检查是否是注释行

    if (text.startsWith("//") || text.startsWith("/*") || text.startsWith("#") || text.endsWith("*/")) {

        return true;

    }

    

    // 检查符号密度 - 代码通常有较高的符号密度

    let symbolCount = 0;

    for (let symbol of codeSymbols) {

        symbolCount += (text.split(symbol).length - 1);

    }

    

    // 如果符号数量超过阈值,认为是代码

    if (symbolCount >= 3) {

        return true;

    }

    

    // 检查是否匹配任何代码结构模式

    for (let pattern of codePatterns) {

        if (pattern.test(text)) {

            return true;

        }

    }

    

    // 检查是否有一致的缩进(代码通常有一致的缩进模式)

    if (text.startsWith('    ') || text.startsWith('\t')) {

        return true;

    }

    

    return false;

}



// 应用代码块样式

function applyCodeBlockStyle(range, style) {

    // 应用段落格式

    range.ParagraphFormat.Borders.Enable = true;

    range.ParagraphFormat.Borders.OutsideLineStyle = wdLineStyleSingle;

    range.ParagraphFormat.Borders.OutsideLineWidth = style.borderWidth;

    range.ParagraphFormat.Borders.OutsideColor = RGBToColor(style.borderColor);

    

    // 应用字符格式

    range.Font.Name = style.fontFamily;

    range.Font.Size = style.fontSize;

    

    // 应用底纹

    range.Shading.BackgroundPatternColor = RGBToColor(style.backgroundColor);

    

    // 增加段落间距

    range.ParagraphFormat.SpaceBefore = 6;

    range.ParagraphFormat.SpaceAfter = 6;

}



// 辅助函数:将RGB颜色字符串转换为WPS颜色值

function RGBToColor(rgbStr) {

    // 移除可能的#符号

    rgbStr = rgbStr.replace('#', '');

    

    // 解析RGB值

    let r = parseInt(rgbStr.substring(0, 2), 16);

    let g = parseInt(rgbStr.substring(2, 4), 16);

    let b = parseInt(rgbStr.substring(4, 6), 16);

    

    // WPS使用BGR顺序

    return r + (g << 8) + (b << 16);

}



// 辅助函数:转义正则表达式特殊字符

function escapeRegExp(string) {

    return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');

}

四、基于字符统计和行模式的代码识别

到了这里,看起来我们需要更激进的代码识别策略。我将尝试一种基于字符分布和行模式的方法,这种方法不依赖于特定的语言关键字,而是分析文本的整体特征

1.这个版本的特点

(1)不依赖特定语言:通过分析字符分布和行模式来识别代码,适用于各种编程语言

(2)统计特征分析:计算代码特有的符号密度,分析行首缩进模式,检查行长度分布,检测代码结构特征。

(3)宽松的识别条件:只要满足符号密度、缩进、长度和结构的综合条件即可识别,对注释行有特殊处理,允许代码块中有少量空行。

(4)中文文本过滤:通过限制中文字符比例来减少误判。

2.代码示例

以下是一个完全重写的代码块识别算法,它通过分析文本的字符分布、行长度变化和结构模式来识别代码:

// 高亮代码块主函数 - 供自定义功能区按钮调用

function highlightCodeBlocks() {

    try {

        // 获取当前文档

        let doc = ThisDocument;

        if (!doc) {

            alert("未找到当前文档!");

            return;

        }

        

        // 定义代码块样式配置

        let codeBlockStyle = {

            borderColor: "#cccccc",

            borderWidth: 1,

            backgroundColor: "#f7f7f9",

            fontFamily: "Consolas, 'Courier New', monospace",

            fontSize: 10.5

        };

        

        // 使用基于统计特征的代码识别

        let codeBlocks = findCodeBlocksByStats(doc);

        

        // 如果没有找到代码块,提示用户

        if (codeBlocks.length === 0) {

            alert("未在文档中找到可识别的代码块!");

            return;

        }

        

        // 处理每个代码块

        for (let block of codeBlocks) {

            try {

                // 创建Range对象

                let codeRange = doc.Range(block.start, block.end);

                

                // 应用代码块样式

                applyCodeBlockStyle(codeRange, codeBlockStyle);

            } catch (e) {

                console.error(`处理代码块时出错: ${e.message}`);

            }

        }

        

        alert(`成功识别并处理 ${codeBlocks.length} 个代码块!`);

    } catch (e) {

        alert(`执行过程中出错: ${e.message}`);

    }

}



// 基于统计特征的代码识别方法

function findCodeBlocksByStats(doc) {

    let codeBlocks = [];

    let paragraphs = doc.Paragraphs;

    

    // 代码特征字符

    let codeChars = "{}[]();:.,+-*/%=&|!~<>\"'`";

    

    // 分析每个段落,寻找代码块

    let currentBlock = null;

    let consecutiveCodeLines = 0;

    

    for (let i = 1; i <= paragraphs.Count; i++) {

        let para = paragraphs(i);

        let text = para.Range.Text;

        

        // 跳过空段落

        if (text.trim().length === 0) {

            if (currentBlock) {

                // 空行允许,但连续空行过多则结束代码块

                consecutiveCodeLines++;

                if (consecutiveCodeLines > 3) {

                    if (currentBlock.paragraphCount >= 2) {

                        codeBlocks.push(currentBlock);

                    }

                    currentBlock = null;

                    consecutiveCodeLines = 0;

                }

            }

            continue;

        }

        

        // 检查当前段落是否为代码

        let isCode = analyzeLineByStats(text, codeChars);

        

        if (isCode) {

            consecutiveCodeLines++;

            

            if (!currentBlock) {

                // 开始一个新的代码块

                currentBlock = {

                    start: para.Range.Start,

                    end: para.Range.End,

                    paragraphCount: 1

                };

            } else {

                // 扩展当前代码块

                currentBlock.end = para.Range.End;

                currentBlock.paragraphCount++;

            }

        } else {

            // 如果当前有代码块且当前段落不是代码行,则结束当前代码块

            if (currentBlock && currentBlock.paragraphCount >= 2) {

                codeBlocks.push(currentBlock);

            }

            currentBlock = null;

            consecutiveCodeLines = 0;

        }

    }

    

    // 添加最后一个代码块(如果有)

    if (currentBlock && currentBlock.paragraphCount >= 2) {

        codeBlocks.push(currentBlock);

    }

    

    return codeBlocks;

}



// 基于统计特征分析行是否为代码

function analyzeLineByStats(text, codeChars) {

    // 移除行首缩进

    let trimmedText = text.trim();

    

    // 检查是否包含URL、邮箱等非代码文本

    if (trimmedText.match(/https?:\/\/\S+|www\.\S+|\S+@\S+\.\S+/)) {

        return false;

    }

    

    // 检查是否是注释行

    if (trimmedText.startsWith("//") || trimmedText.startsWith("/*") ||

        trimmedText.startsWith("#") || trimmedText.endsWith("*/")) {

        return true;

    }

    

    // 计算代码特征字符的比例

    let codeCharCount = 0;

    for (let i = 0; i < trimmedText.length; i++) {

        if (codeChars.includes(trimmedText[i])) {

            codeCharCount++;

        }

    }

    

    // 代码通常有较高的符号密度

    let codeCharRatio = codeCharCount / trimmedText.length;

    

    // 计算行首缩进量(空格或制表符)

    let indentCount = 0;

    while (indentCount < text.length && (text[indentCount] === ' ' || text[indentCount] === '\t')) {

        indentCount++;

    }

    

    // 代码通常有一致的缩进模式

    let hasIndent = indentCount > 0;

    

    // 代码行通常不会太长也不会太短

    let lineLength = trimmedText.length;

    let isTypicalLength = lineLength > 5 && lineLength < 200;

    

    // 检查是否有代码特有的结构模式

    let hasCodeStructure =

        trimmedText.includes('{') || trimmedText.includes('}') ||

        trimmedText.includes('(') || trimmedText.includes(')') ||

        trimmedText.includes(';') || trimmedText.includes('=') ||

        trimmedText.includes(':') || trimmedText.includes(',');

    

    // 计算中文字符比例

    let chineseChars = text.match(/[\u4e00-\u9fa5]/g);

    let chineseRatio = chineseChars ? chineseChars.length / text.length : 0;

    

    // 综合判断:如果符号密度高、有缩进、长度适中、有代码结构,且中文比例低,则认为是代码

    return (codeCharRatio > 0.1 || hasIndent) &&

           isTypicalLength &&

           hasCodeStructure &&

           chineseRatio < 0.3;

}



// 应用代码块样式

function applyCodeBlockStyle(range, style) {

    // 应用段落格式

    range.ParagraphFormat.Borders.Enable = true;

    range.ParagraphFormat.Borders.OutsideLineStyle = wdLineStyleSingle;

    range.ParagraphFormat.Borders.OutsideLineWidth = style.borderWidth;

    range.ParagraphFormat.Borders.OutsideColor = RGBToColor(style.borderColor);

    

    // 应用字符格式

    range.Font.Name = style.fontFamily;

    range.Font.Size = style.fontSize;

    

    // 应用底纹

    range.Shading.BackgroundPatternColor = RGBToColor(style.backgroundColor);

    

    // 增加段落间距

    range.ParagraphFormat.SpaceBefore = 6;

    range.ParagraphFormat.SpaceAfter = 6;

}



// 辅助函数:将RGB颜色字符串转换为WPS颜色值

function RGBToColor(rgbStr) {

    // 移除可能的#符号

    rgbStr = rgbStr.replace('#', '');

    

    // 解析RGB值

    let r = parseInt(rgbStr.substring(0, 2), 16);

    let g = parseInt(rgbStr.substring(2, 4), 16);

    let b = parseInt(rgbStr.substring(4, 6), 16);

    

    // WPS使用BGR顺序

    return r + (g << 8) + (b << 16);

}



// 辅助函数:转义正则表达式特殊字符

function escapeRegExp(string) {

    return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');

}

五、新增JS宏并配置菜单调用

在WPS中配置自定义功能区时:

(1)打开 "选项" > "自定义功能区"

(2)在 "从下列位置选择命令" 下拉菜单中选择 "宏"

(3)选择 "Project.Module5.highlightCodeBlocks"

(4)将其添加到你自定义的功能区中

(5)为按钮设置图标和名称

这样配置后,点击自定义功能区中的按钮就能直接调用highlightCodeBlocks()函数,实现代码块高亮功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2379748.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小米MUJIA智能音频眼镜来袭

智能眼镜赛道风云再起&#xff0c;小米新力作MIJIA智能音频眼镜2正式亮相&#xff0c;引发市场热议。 这款产品在设计和功能上都有显著提升&#xff0c;为用户带来更舒适便捷的佩戴体验&#xff0c;同时也标志着小米在智能眼镜领域的持续深耕。 轻薄设计&#xff0c;舒适体验 …

基于SpringBoot的家政预约系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

Axure难点解决分享:垂直菜单展开与收回(4大核心问题与专家级解决方案)

亲爱的小伙伴,在您浏览之前,烦请关注一下,在此深表感谢!如有帮助请订阅专栏! Axure产品经理精品视频课已登录CSDN可点击学习https://edu.csdn.net/course/detail/40420 课程主题:垂直菜单展开与收回 主要内容:超长菜单实现、展开与收回bug解释、Axure9版本限制等问题解…

Linux:网络层的重要协议或技术

一、DNS DNS(Domain Name System)是一整套从域名映射到IP的系统 1.1 DNS的背景 TCP/IP中使用IP地址和端口号来确定网络上的一台主机的一个程序. 但是IP地址不方便记忆. 于是人们发明了一种叫主机名的东西, 是一个字符串, 并且使用hosts文件来描述主机名和IP地址的关系. 最初,…

JAVA请求vllm的api服务报错Unsupported upgrade request、 Invalid HTTP request received.

环境&#xff1a; vllm 0.8.5 java 17 Qwen3-32B-FP8 问题描述&#xff1a; JAVA请求vllm的api服务报错Unsupported upgrade request、 Invalid HTTP request received. WARNING: Unsupported upgrade request. INFO: - "POST /v1/chat/completions HTTP/1.1&…

基于 CSS Grid 的网页,拆解页面整体布局结构

通过以下示例拆解网页整体布局结构&#xff1a; 一、基础结构&#xff08;HTML骨架&#xff09; <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"…

psotgresql18 源码编译安装

环境&#xff1a; 系统&#xff1a;centos7.9 数据库&#xff1a;postgresql18beta1 #PostgreSQL 18 已转向 DocBook XML 构建体系&#xff08;SGML 未来将被弃用&#xff09;。需要安装 XML 工具链&#xff0c;如下&#xff1a; yum install -y docbook5-style-xsl libxsl…

虚幻引擎5-Unreal Engine笔记之Pawn与胶囊体的关系

虚幻引擎5-Unreal Engine笔记之Pawn与胶囊体的关系 code review! 文章目录 虚幻引擎5-Unreal Engine笔记之Pawn与胶囊体的关系1. 什么是Pawn&#xff1f;2. 什么是胶囊体&#xff08;Capsule Component&#xff09;&#xff1f;3. Pawn与胶囊体的具体关系&#xff08;1&#x…

Vue环境下数据导出PDF的全面指南

文章目录 1. 前言2. 原生浏览器打印方案2.1 使用window.print()实现2.2 使用CSS Paged Media模块 3. 常用第三方库方案3.1 使用jsPDF3.2 使用html2canvas jsPDF3.3 使用pdfmake3.4 使用vue-pdf 4. 服务器端导出方案4.1 前端请求服务器生成PDF4.2 使用无头浏览器生成PDF 5. 方法…

Linux中的DNS的安装与配置

DNS简介 DNS&#xff08;DomainNameSystem&#xff09;是互联网上的一项服务&#xff0c;它作为将域名和IP地址相互映射的一个分布式数据库&#xff0c;能够使人更方便的访问互联网。 DNS使用的是53端口 通常DNS是以UDP这个较快速的数据传输协议来查询的&#xff0c;但是没有查…

linux服务器与时间服务器同步时间

内网部署服务器&#xff0c;需要同步时间 使用系统内置的systemctl-timesyncd进行时间同步 1.编辑配置文件 sudo nano /etc/systemd/timesyncd.conf修改添加内容入下 [Time] NTP10.100.13.198 FallbackNTP#说明 #NTP10.100.13.198&#xff1a;你的主 NTP 时间服务器 IP #Fall…

【数据结构篇】排序1(插入排序与选择排序)

注&#xff1a;本文以排升序为例 常见的排序算法&#xff1a; 目录&#xff1a; 一 直接插入排序&#xff1a; 1.1 基本思想&#xff1a; 1.2 代码&#xff1a; 1.3 复杂度&#xff1a; 二 希尔排序&#xff08;直接插入排序的优化&#xff09;&#xff1a; 2.1 基本思想…

《Linux服务与安全管理》| DNS服务器安装和配置

《Linux服务与安全管理》| DNS服务器安装和配置 目录 《Linux服务与安全管理》| DNS服务器安装和配置 第一步&#xff1a;使用dnf命令安装BIND服务 第二步&#xff1a;查看服务器server01的网络配置 第三步&#xff1a;配置全局配置文件 第四步&#xff1a;修改bind的区域…

Notepad++ 学习(三)使用python插件编写脚本:实现跳转指定标签页(自主研发)

目录 一、先看成果二、安装Python Script插件三、配置Python脚本四、使用脚本跳转标签页方法一&#xff1a;通过菜单运行方法二&#xff1a;设置快捷键&#xff08;推荐&#xff09; 五、注意事项六、进阶使用 官网地址&#xff1a; https://notepad-plus-plus.org/Python Scri…

Stable Diffusion 学习笔记02

模型下载网站&#xff1a; 1&#xff0c;LiblibAI-哩布哩布AI - 中国领先的AI创作平台 2&#xff0c;Civitai: The Home of Open-Source Generative AI 模型的安装&#xff1a; 将下载的sd模型放置在sd1.5的文件内即可&#xff0c;重启客户端可用。 外挂VAE模型&#xff1a…

python:pymysql概念、基本操作和注入问题讲解

python&#xff1a;pymysql分享目录 一、概念二、数据准备三、安装pymysql四、pymysql使用&#xff08;一&#xff09;使用步骤&#xff08;二&#xff09;查询操作&#xff08;三&#xff09;增&#xff08;四&#xff09;改&#xff08;五&#xff09;删 五、关于pymysql注入…

机器学习-人与机器生数据的区分模型测试 - 模型融合与检验

模型融合 # 先用普通Pipeline训练 from sklearn.pipeline import Pipeline#from sklearn2pmml.pipeline import PMMLPipeline train_pipe Pipeline([(scaler, StandardScaler()),(ensemble, VotingClassifier(estimators[(rf, RandomForestClassifier(n_estimators200, max_de…

机器学习 day03

文章目录 前言一、特征降维1.特征选择2.主成分分析&#xff08;PCA&#xff09; 二、KNN算法三、模型的保存与加载 前言 通过今天的学习&#xff0c;我掌握了机器学习中的特征降维的概念以及用法&#xff0c;KNN算法的基本原理及用法&#xff0c;模型的保存和加载 一、特征降维…

嵌入式软件--stm32 DAY 6 USART串口通讯(下)

1.寄存器轮询_收发字符串 通过寄存器轮询方式实现了收发单个字节之后&#xff0c;我们趁热打铁&#xff0c;争上游&#xff0c;进阶到字符串。字符串就是多个字符。很明显可以循环收发单个字节实现。 然后就是接收字符串。如果接受单个字符的函数放在while里&#xff0c;它也可…

问题处理——在ROS2(humble)+Gazebo+rqt下,无法显示仿真无人机的相机图像

文章目录 前言一、问题展示二、解决方法&#xff1a;1.下载对应版本的PX42.下载对应版本的Gazebo3.启动 总结 前言 在ROS2的环境下&#xff0c;进行无人机仿真的过程中&#xff0c;有时需要调取无人机的相机图像信息&#xff0c;但是使用rqt&#xff0c;却发现相机图像无法显示…