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

在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.mzph.cn/diannao/83720.shtml

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

相关文章

el-tree结合el-tree-transfer实现穿梭框里展示树形数据

参考文章&#xff1a;我把他的弹框单拉出来一个独立文件作为组件方便使用&#xff0c;遇到一些问题记录一下。 testComponet.vue <template><div class"per_container"><div class"per_con_left"><div class"per_con_title&q…

Go 后端中双 token 的实现模板

下面是一个典型的 Go 后端双 Token 认证机制 实现模板&#xff0c;使用 Gin 框架 JWT Redis&#xff0c;结构清晰、可拓展&#xff0c;适合实战开发。 项目结构建议 /utils├── jwt.go // Access & Refresh token 的生成和解析├── claims.go // 从请求…

Typescript学习教程,从入门到精通,TypeScript 对象语法知识点及案例代码(7)

TypeScript 对象语法知识点及案例代码 TypeScript 是 JavaScript 的超集&#xff0c;提供了静态类型检查和其他增强功能。在 TypeScript 中&#xff0c;对象是面向对象编程&#xff08;OOP&#xff09;的基础。 一、对象概述 在 TypeScript 中&#xff0c;对象是属性的集合&a…

应用BERT-GCN跨模态情绪分析:贸易缓和与金价波动的AI归因

本文运用AI量化分析框架&#xff0c;结合市场情绪因子、宏观经济指标及技术面信号&#xff0c;对黄金与美元指数的联动关系进行解析&#xff0c;揭示本轮贵金属回调的深层驱动因素。 周三&#xff0c;现货黄金价格单日跌幅达2.1%&#xff0c;盘中触及3167.94美元/盎司关键价位&…

命令行登录 MySQL 报 Segmentation fault 故障解决

问题描述&#xff1a;对 mysql8.0.35 源码进行 make&#xff0c;由于一开始因为yum源问题少安装依赖库 库&#xff0c;在链接时遇到错误 undefined reference to&#xff0c;后来安装了相关依赖库&#xff0c;再次 make 成功。于是将 mysqld 启动&#xff0c;再用 mysql -u roo…

Axure设计数字乡村可视化大屏:构建乡村数据全景图

今天&#xff0c;让我们一同深入了解由Axure设计的数字乡村可视化大屏&#xff0c;看看它如何通过精心的布局和多样化的图表类型&#xff0c;将乡村的各类数据以直观、易懂的方式呈现出来&#xff0c;为乡村管理者提供有力的数据支持。 原型效果预览链接&#xff1a;Axure数字乡…

3D个人简历网站 4.小岛

1.模型素材 在Sketchfab上下载狐狸岛模型&#xff0c;然后转换为素材资源asset&#xff0c;嫌麻烦直接在网盘链接下载素材&#xff0c; Fox’s islandshttps://sketchfab.com/3d-models/foxs-islands-163b68e09fcc47618450150be7785907https://gltf.pmnd.rs/ 素材夸克网盘&a…

智能开发工具PhpStorm v2025.1——增强AI辅助编码功能

PhpStorm是一个轻量级且便捷的PHP IDE&#xff0c;其旨在提高用户效率&#xff0c;可深刻理解用户的编码&#xff0c;提供智能代码补全&#xff0c;快速导航以及即时错误检查。可随时帮助用户对其编码进行调整&#xff0c;运行单元测试或者提供可视化debug功能。 立即获取PhpS…

Spark 的运行模式(--master) 和 部署方式(--deploy-mode)

Spark 的 运行模式&#xff08;--master&#xff09; 和 部署方式&#xff08;--deploy-mode&#xff09;&#xff0c;两者的核心区别在于 资源调度范围 和 Driver 进程的位置。 一、核心概念对比 维度--master&#xff08;运行模式&#xff09;--deploy-mode&#xff08;部署…

sqli—labs第八关——布尔盲注

一&#xff1a;确定注入类型 按照我们之前的步骤来 输入 ?id1 and 11-- ?id1 and 12-- 界面正常 第二行界面异常空白 所以注入类型为单引号闭合型 二&#xff1a; 布尔盲注 1.判断是否使用条件 &#xff08;1&#xff09;&#xff1a;存在注入但不会直接显示查询结果 …

ARP 原理总结

&#x1f310; 一、ARP 原理总结 ARP&#xff08;Address Resolution Protocol&#xff09;是用于通过 IP 地址解析 MAC 地址的协议&#xff0c;工作在 链路层 与 网络层之间&#xff08;OSI 模型的第三层与第二层之间&#xff09;。 &#x1f501; ARP通信过程&#xff1a; …

SpringCloud——EureKa

目录 1.前言 1.微服务拆分及远程调用 3.EureKa注册中心 远程调用的问题 eureka原理 搭建EureKaServer 服务注册 服务发现 1.前言 分布式架构&#xff1a;根据业务功能对系统进行拆分&#xff0c;每个业务模块作为独立项目开发&#xff0c;称为服务。 优点&#xff1a; 降…

机顶盒刷机笔记

疑难杂症解决 hitool线刷网口不通tftp超时--》关闭防火墙cm201-2卡刷所有包提示失败abort install--》找个卡刷包只刷fastboot分区再卡刷就能通过了&#xff08;cm201救砖包 (M8273版子&#xff09;&#xff09; 刷机工具 海兔烧录工具HiTool-STB-5.3.12工具&#xff0c;需要…

Linux动静态库制作与原理

什么是库 库是写好的现有的&#xff0c;成熟的&#xff0c;可以复用的代码。现实中每个程序都要依赖很多基础的底层库&#xff0c;不可能每个人的代码都从零开始&#xff0c;因此库的存在意义非同寻常。 本质上来说库是一种可执行代码的二进制形式&#xff0c;可以被操作系统…

如何通过小智AI制作会说话的机器人玩具?

一、硬件准备与组装 1. 核心硬件选择 主控芯片&#xff1a;选择支持无线网络连接、音频处理和可编程接口的嵌入式开发板 音频模块&#xff1a;配备拾音麦克风与小型扬声器&#xff0c;确保语音输入/输出功能 显示模块&#xff1a;选择适配的交互显示屏用于可视化反馈 扩展模…

如何控制邮件发送频率避免打扰用户

一、用户行为 监测用户与邮件的互动数据&#xff0c;如打开率、点击率下滑或退订申请增多&#xff0c;可能是发送频率过高的警示信号。利用邮件营销平台的分析工具&#xff0c;识别这些指标的变动趋势&#xff0c;为调整提供依据。 二、行业特性与受众差异 不同行业用户对邮…

定积分的“偶倍奇零”性质及其使用条件

定积分的“偶倍奇零”性质是针对对称区间上的奇偶函数积分的重要简化方法。以下是其核心内容和应用要点&#xff1a; ​一、基本性质 ​偶函数&#xff08;偶倍&#xff09;​ 若 f(x) 在 [−a,a] 上为偶函数&#xff08;即 f(−x)f(x)&#xff09;&#xff0c;则&#xff1a; …

如何在 Windows 11 或 10 上安装 Fliqlo 时钟屏保

了解如何在 Windows 11 或 10 上安装 Fliqlo,为您的 PC 或笔记本电脑屏幕添加一个翻转时钟屏保以显示时间。 Fliqlo 是一款适用于 Windows 和 macOS 平台的免费时钟屏保。它也适用于移动设备,但仅限于 iPhone 和 iPad。Fliqlo 的主要功能是在用户不活动时在 PC 或笔记本电脑…

【C/C++】C++并发编程:std::async与std::thread深度对比

文章目录 C并发编程&#xff1a;std::async与std::thread深度对比1 核心设计目的以及区别2 详细对比分析3 代码对比示例4 适用场景建议5 总结 C并发编程&#xff1a;std::async与std::thread深度对比 在 C 中&#xff0c;std::async 和 std::thread 都是用于并发编程的工具&am…

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

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