// ==UserScript== // @name LeetCode|力扣 智能C++语法智能提示 (IDE级增强版) // @namespace https://tampermonkey.net/ // @version 6.0.1 // @description 为 LeetCode 提供增强版的 C++ 自动补全:支持 STL 容器成员函数、LeetCode 内置结构体(TreeNode/ListNode)、常用算法模板(DFS/BFS/二分)、以及变量类型追踪。纯前端实现,伟大,无需多言。 // @author bigonion // @match https://leetcode.cn/problems/* // @grant unsafeWindow // @license MIT // ==/UserScript== (function () { 'use strict'; // --- 配置:常用 STL 和 LeetCode 结构体的成员函数库 --- const MEMBER_METHODS = { 'vector': ['push_back', 'pop_back', 'size', 'empty', 'clear', 'resize', 'reserve', 'begin', 'end', 'back', 'front', 'insert', 'erase'], 'string': ['length', 'size', 'substr', 'find', 'push_back', 'pop_back', 'empty', 'c_str', 'append', 'replace'], 'stack': ['push', 'pop', 'top', 'empty', 'size'], 'queue': ['push', 'pop', 'front', 'back', 'empty', 'size'], 'deque': ['push_back', 'push_front', 'pop_back', 'pop_front', 'front', 'back', 'size', 'empty'], 'priority_queue': ['push', 'pop', 'top', 'empty', 'size'], 'set': ['insert', 'erase', 'find', 'count', 'begin', 'end', 'size', 'empty', 'lower_bound', 'upper_bound'], 'unordered_set': ['insert', 'erase', 'find', 'count', 'begin', 'end', 'size', 'empty'], 'map': ['insert', 'erase', 'find', 'count', 'begin', 'end', 'size', 'empty', 'lower_bound', 'upper_bound'], 'unordered_map': ['insert', 'erase', 'find', 'count', 'begin', 'end', 'size', 'empty'], 'pair': ['first', 'second'], // LeetCode Specific 'TreeNode': ['val', 'left', 'right'], 'ListNode': ['val', 'next'] }; // --- 1. STATIC DICTIONARY (全局关键词 + 算法模板) --- const globalKeywords = [ // Keywords { label: 'int', type: 'keyword' }, { label: 'long long', type: 'keyword' }, { label: 'double', type: 'keyword' }, { label: 'bool', type: 'keyword' }, { label: 'void', type: 'keyword' }, { label: 'auto', type: 'keyword' }, { label: 'const', type: 'keyword' }, { label: 'static', type: 'keyword' }, { label: 'return', type: 'keyword' }, { label: 'continue', type: 'keyword' }, { label: 'break', type: 'keyword' }, { label: 'struct', type: 'keyword' }, { label: 'class', type: 'keyword' }, { label: 'nullptr', type: 'keyword' }, { label: 'new', type: 'keyword' }, { label: 'delete', type: 'keyword' }, // Control Flow Snippets { label: 'for', type: 'snippet', snippet: 'for (int ${1:i} = 0; ${1:i} < ${2:n}; ++${1:i}) {\n\t$0\n}', hover: 'For Loop' }, { label: 'while', type: 'snippet', snippet: 'while (${1:cond}) {\n\t$0\n}', hover: 'While Loop' }, { label: 'if', type: 'snippet', snippet: 'if (${1:cond}) {\n\t$0\n}', hover: 'If Statement' }, { label: 'else', type: 'snippet', snippet: 'else {\n\t$0\n}', hover: 'Else Statement' }, // Algorithm Snippets (核心增强) { label: 'dfs', type: 'snippet', snippet: 'function dfs = [&](int u) {\n\tif (u == ${1:target}) return;\n\tvisited[u] = true;\n\tfor (auto& v : adj[u]) {\n\t\tif (!visited[v]) dfs(v);\n\t}\n};', hover: 'DFS Template (Lambda)' }, { label: 'bfs', type: 'snippet', snippet: 'queue q;\nq.push(${1:start});\nvisited[${1:start}] = true;\nwhile (!q.empty()) {\n\tint u = q.front(); q.pop();\n\t$0\n}', hover: 'BFS Template' }, { label: 'binary_search', type: 'snippet', snippet: 'int l = 0, r = ${1:n} - 1;\nwhile (l <= r) {\n\tint mid = l + (r - l) / 2;\n\tif (check(mid)) l = mid + 1;\n\telse r = mid - 1;\n}', hover: 'Binary Search Template' }, { label: 'union_find', type: 'snippet', snippet: 'struct DSU {\n\tvector p;\n\tDSU(int n) : p(n) { iota(p.begin(), p.end(), 0); }\n\tint find(int x) { return p[x] == x ? x : p[x] = find(p[x]); }\n\tvoid unite(int x, int y) { p[find(x)] = find(y); }\n};', hover: 'Disjoint Set Union Class' }, // Common Constants & Macros { label: 'INT_MAX', type: 'constant' }, { label: 'INT_MIN', type: 'constant' }, { label: 'LLONG_MAX', type: 'constant' }, { label: 'LLONG_MIN', type: 'constant' }, { label: 'NULL', type: 'constant' }, // Standard Functions { label: 'sort', type: 'function', snippet: 'sort(${1:begin}, ${2:end});' }, { label: 'reverse', type: 'function', snippet: 'reverse(${1:begin}, ${2:end});' }, { label: 'max', type: 'function' }, { label: 'min', type: 'function' }, { label: 'abs', type: 'function' }, { label: 'pow', type: 'function' }, { label: 'memset', type: 'function', snippet: 'memset(${1:arr}, 0, sizeof(${1:arr}));' }, { label: 'accumulate', type: 'function', snippet: 'accumulate(${1:begin}, ${2:end}, 0LL)' } ]; // --- 2. 工具函数 --- const wait = (ms) => new Promise(r => setTimeout(r, ms)); const waitForMonaco = async () => { while (!unsafeWindow.monaco || !unsafeWindow.monaco.editor || !unsafeWindow.monaco.editor.getModels().length) { await wait(200); } return unsafeWindow.monaco; }; // --- 3. 主逻辑 --- waitForMonaco().then(init); function init(monaco) { const model = monaco.editor.getModels()[0]; // 强制识别为CPP (力扣有时会默认为纯文本) if (model.getLanguageId() !== 'cpp') monaco.editor.setModelLanguage(model, 'cpp'); // --- 状态管理 --- let varTable = new Map(); // name -> { type, normalizedType, line } // 预设 LeetCode 常见变量 varTable.set('root', { type: 'TreeNode*', normalizedType: 'TreeNode', line: 0 }); varTable.set('head', { type: 'ListNode*', normalizedType: 'ListNode', line: 0 }); // --- 4. 解析器 (Parser) --- // 优化正则:支持捕获 vector 等复杂类型,且忽略 const/static const VAR_RE = /^\s*(?:const\s+|static\s+)?(?:unsigned\s+)?([a-zA-Z0-9_<>:*\s&]+?)\s+([a-zA-Z_]\w*)(?:\s*[=;,)])/; // 归一化类型:将 "vector" -> "vector", "TreeNode*" -> "TreeNode" function normalizeType(rawType) { let t = rawType.trim(); // 移除指针和引用 (*, &) t = t.replace(/[&*]+/g, '').trim(); // 提取模板主类型 (例如 vector> -> vector) const templateMatch = /^([a-zA-Z0-9_]+)\s*<.*>$/.exec(t); if (templateMatch) return templateMatch[1]; return t; } const buildIndex = () => { const code = model.getValue(); const lines = code.split('\n'); // 清空时保留预设 (LeetCode 入参通常在类定义里,正则很难抓全,这里简单处理局部变量) // 实际使用中,我们不完全清空,而是覆盖。 lines.forEach((line, i) => { const m = VAR_RE.exec(line); if (m) { const rawType = m[1]; const name = m[2]; if (name === 'return' || name === 'if') return; // 排除误判 varTable.set(name, { type: rawType.trim(), normalizedType: normalizeType(rawType), line: i + 1 }); } }); }; // 初始构建 + 监听变化 (防抖) buildIndex(); let timeout; model.onDidChangeContent(() => { clearTimeout(timeout); timeout = setTimeout(buildIndex, 500); }); // --- 5. Completion Provider (核心) --- monaco.languages.registerCompletionItemProvider('cpp', { triggerCharacters: ['.', '>', ':'], // 触发成员提示的关键 provideCompletionItems: (model, position) => { const textUntilPosition = model.getValueInRange({ startLineNumber: position.lineNumber, startColumn: 1, endLineNumber: position.lineNumber, endColumn: position.column }); const word = model.getWordUntilPosition(position); const range = { startLineNumber: position.lineNumber, endLineNumber: position.lineNumber, startColumn: word.startColumn, endColumn: word.endColumn }; // check 1: 成员访问 (Object Member Access) // 匹配 ". " 或 "-> " 结尾 (允许中间有空格) const memberAccessMatch = textUntilPosition.trim().match(/([a-zA-Z_]\w*)\s*(\.|->)$/); if (memberAccessMatch) { const varName = memberAccessMatch[1]; const op = memberAccessMatch[2]; // '.' or '->' const varInfo = varTable.get(varName); if (varInfo) { const type = varInfo.normalizedType; const methods = MEMBER_METHODS[type]; if (methods) { return { suggestions: methods.map(m => ({ label: m, kind: monaco.languages.CompletionItemKind.Method, insertText: m, sortText: '0_' + m, // 保证排在最前 range: range })) }; } } // 如果没找到变量,但看起来像是指针访问,尝试给通用建议? 暂时留空 return { suggestions: [] }; } // check 2: 全局建议 (Global Suggestions) // 只有当前面不是 . 或 -> 时才触发 if (textUntilPosition.trim().endsWith('.') || textUntilPosition.trim().endsWith('>')) { return { suggestions: [] }; } const suggestions = []; const seen = new Set(); // A. 用户定义的变量 varTable.forEach((info, name) => { suggestions.push({ label: name, kind: monaco.languages.CompletionItemKind.Variable, detail: info.type, insertText: name, sortText: '1_' + name, range: range }); seen.add(name); }); // B. 静态字典 (关键词、Snippet) globalKeywords.forEach(item => { if (!seen.has(item.label)) { suggestions.push({ label: item.label, kind: item.type === 'snippet' ? monaco.languages.CompletionItemKind.Snippet : item.type === 'function' ? monaco.languages.CompletionItemKind.Function : monaco.languages.CompletionItemKind.Keyword, insertText: item.snippet || item.label, insertTextRules: item.snippet ? monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet : undefined, detail: item.hover || '', sortText: '2_' + item.label, range: range }); } }); // C. STL 容器类型提示 (vector, map...) Object.keys(MEMBER_METHODS).forEach(cls => { if(!seen.has(cls)) { suggestions.push({ label: cls, kind: monaco.languages.CompletionItemKind.Class, insertText: cls, sortText: '3_' + cls, range: range }); } }); return { suggestions: suggestions }; } }); // --- 6. Hover Provider (悬浮提示) --- monaco.languages.registerHoverProvider('cpp', { provideHover: (model, position) => { const word = model.getWordAtPosition(position); if (!word) return; // 查变量表 const v = varTable.get(word.word); if (v) { return { range: new monaco.Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn), contents: [ { value: `**variable** \`${word.word}\`` }, { value: 'Type: `' + v.type + '`' } ] }; } // 查关键词表 const k = globalKeywords.find(x => x.label === word.word); if (k && k.hover) { return { range: new monaco.Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn), contents: [{ value: k.hover }] }; } } }); console.log('%c [LeetCode C++ Helper] Loaded! ', 'background: #222; color: #bada55; font-size:14px'); } })();