|
1 | 1 | import * as vscode from 'vscode'; |
2 | | -import { AnnotatorType } from './lspExt'; |
| 2 | +import { AnnotatorType } from './lspExtension'; |
3 | 3 | import { LanguageClient } from 'vscode-languageclient/node'; |
4 | | -import * as notifications from "./lspExt"; |
| 4 | +import * as notifications from "./lspExtension"; |
5 | 5 | import { get } from './configRenames'; |
6 | 6 |
|
| 7 | +// 装饰器类型映射接口 |
| 8 | +interface DecorationMap { |
| 9 | + [AnnotatorType.ReadOnlyParam]: vscode.TextEditorDecorationType; |
| 10 | + [AnnotatorType.Global]: vscode.TextEditorDecorationType; |
| 11 | + [AnnotatorType.ReadOnlyLocal]: vscode.TextEditorDecorationType; |
| 12 | + [AnnotatorType.MutLocal]: vscode.TextEditorDecorationType; |
| 13 | + [AnnotatorType.MutParam]: vscode.TextEditorDecorationType; |
| 14 | +} |
| 15 | + |
| 16 | +// 装饰器缓存 |
| 17 | +const decorationCache = new Map<string, vscode.TextEditorDecorationType>(); |
7 | 18 |
|
8 | | -let D_PARAM: vscode.TextEditorDecorationType; |
9 | | -let D_GLOBAL: vscode.TextEditorDecorationType; |
10 | | -let D_LOCAL: vscode.TextEditorDecorationType; |
11 | | -let D_MUT_LOCAL: vscode.TextEditorDecorationType; |
12 | | -let D_MUT_PARAM: vscode.TextEditorDecorationType; |
| 19 | +// 当前装饰器实例 |
| 20 | +let decorations: Partial<DecorationMap> = {}; |
| 21 | + |
| 22 | +/** |
| 23 | + * 创建装饰器的工厂函数 |
| 24 | + */ |
| 25 | +const createDecoration = (key: string): vscode.TextEditorDecorationType => { |
| 26 | + const cacheKey = `decoration:${key}`; |
| 27 | + if (decorationCache.has(cacheKey)) { |
| 28 | + return decorationCache.get(cacheKey)!; |
| 29 | + } |
13 | 30 |
|
14 | | -function createDecoration(key: string): vscode.TextEditorDecorationType { |
15 | | - let config: vscode.DecorationRenderOptions = {} |
16 | | - let color = vscode.workspace.getConfiguration("emmylua").get(key); |
17 | | - if (typeof (color) === 'string') { |
| 31 | + const config: vscode.DecorationRenderOptions = {}; |
| 32 | + const color = vscode.workspace.getConfiguration("emmylua").get<string>(key); |
| 33 | + |
| 34 | + if (color) { |
18 | 35 | config.light = { color }; |
19 | 36 | config.dark = { color }; |
20 | 37 | } |
21 | | - return vscode.window.createTextEditorDecorationType(config); |
22 | | -} |
| 38 | + |
| 39 | + const decoration = vscode.window.createTextEditorDecorationType(config); |
| 40 | + decorationCache.set(cacheKey, decoration); |
| 41 | + return decoration; |
| 42 | +}; |
| 43 | + |
| 44 | +/** |
| 45 | + * 创建带下划线的装饰器 |
| 46 | + */ |
| 47 | +const createDecorationUnderline = (key: string): vscode.TextEditorDecorationType => { |
| 48 | + const cacheKey = `underline:${key}`; |
| 49 | + if (decorationCache.has(cacheKey)) { |
| 50 | + return decorationCache.get(cacheKey)!; |
| 51 | + } |
23 | 52 |
|
24 | | -function createDecorationUnderline(key: string): vscode.TextEditorDecorationType { |
25 | | - let config: vscode.DecorationRenderOptions = {} |
26 | | - let color = vscode.workspace.getConfiguration("emmylua").get(key); |
27 | | - if (typeof (color) === 'string') { |
28 | | - config.light = { |
29 | | - color, |
30 | | - textDecoration: `underline;text-decoration-color:${color};text-underline-offset: 4px;` |
31 | | - }; |
32 | | - config.dark = { |
33 | | - color, |
34 | | - textDecoration: `underline;text-decoration-color:${color};text-underline-offset: 4px;` |
35 | | - }; |
| 53 | + const config: vscode.DecorationRenderOptions = {}; |
| 54 | + const color = vscode.workspace.getConfiguration("emmylua").get<string>(key); |
| 55 | + |
| 56 | + const textDecoration = color |
| 57 | + ? `underline;text-decoration-color:${color};text-underline-offset: 4px;` |
| 58 | + : 'underline;text-underline-offset: 4px;'; |
| 59 | + |
| 60 | + if (color) { |
| 61 | + config.light = { color, textDecoration }; |
| 62 | + config.dark = { color, textDecoration }; |
36 | 63 | } else { |
37 | | - config.light = { |
38 | | - textDecoration: `underline;text-underline-offset: 4px;` |
39 | | - }; |
40 | | - config.dark = { |
41 | | - textDecoration: `underline;text-underline-offset: 4px;` |
42 | | - }; |
| 64 | + config.light = { textDecoration }; |
| 65 | + config.dark = { textDecoration }; |
43 | 66 | } |
44 | | - return vscode.window.createTextEditorDecorationType(config); |
45 | | -} |
| 67 | + |
| 68 | + const decoration = vscode.window.createTextEditorDecorationType(config); |
| 69 | + decorationCache.set(cacheKey, decoration); |
| 70 | + return decoration; |
| 71 | +}; |
46 | 72 |
|
47 | | -function disposeDecorations(...decorations: (vscode.TextEditorDecorationType | undefined)[]) { |
48 | | - decorations.forEach(d => d && d.dispose()); |
49 | | -} |
| 73 | +/** |
| 74 | + * 批量释放装饰器 |
| 75 | + */ |
| 76 | +const disposeDecorations = (...decorationTypes: (vscode.TextEditorDecorationType | undefined)[]): void => { |
| 77 | + decorationTypes.forEach(decoration => decoration?.dispose()); |
| 78 | +}; |
50 | 79 |
|
51 | | -function updateDecorations() { |
52 | | - if (D_PARAM) { |
53 | | - disposeDecorations(D_PARAM, D_GLOBAL, D_LOCAL, D_MUT_LOCAL, D_MUT_PARAM); |
| 80 | +/** |
| 81 | + * 更新所有装饰器实例 |
| 82 | + */ |
| 83 | +const updateDecorations = (): void => { |
| 84 | + // 清理旧的装饰器 |
| 85 | + if (Object.keys(decorations).length > 0) { |
| 86 | + disposeDecorations(...Object.values(decorations)); |
| 87 | + decorations = {}; |
54 | 88 | } |
55 | 89 |
|
56 | | - D_PARAM = createDecoration("colors.parameter"); |
57 | | - D_GLOBAL = createDecoration("colors.global"); |
58 | | - D_LOCAL = createDecoration("colors.local"); |
59 | | - let config = vscode.workspace.getConfiguration( |
| 90 | + // 创建基础装饰器 |
| 91 | + decorations[AnnotatorType.ReadOnlyParam] = createDecoration("colors.parameter"); |
| 92 | + decorations[AnnotatorType.Global] = createDecoration("colors.global"); |
| 93 | + decorations[AnnotatorType.ReadOnlyLocal] = createDecoration("colors.local"); |
| 94 | + |
| 95 | + // 获取配置以决定是否使用下划线 |
| 96 | + const config = vscode.workspace.getConfiguration( |
60 | 97 | undefined, |
61 | 98 | vscode.workspace.workspaceFolders?.[0] |
62 | 99 | ); |
63 | | - let mutableUnderline = get<string>(config, "emmylua.colors.mutableUnderline"); |
| 100 | + const mutableUnderline = get<string>(config, "emmylua.colors.mutableUnderline"); |
| 101 | + |
| 102 | + // 创建可变变量的装饰器 |
64 | 103 | if (mutableUnderline) { |
65 | | - D_MUT_LOCAL = createDecorationUnderline("colors.local"); |
66 | | - D_MUT_PARAM = createDecorationUnderline("colors.parameter"); |
67 | | - } |
68 | | - else { |
69 | | - D_MUT_LOCAL = createDecoration("colors.local"); |
70 | | - D_MUT_PARAM = createDecoration("colors.parameter"); |
| 104 | + decorations[AnnotatorType.MutLocal] = createDecorationUnderline("colors.local"); |
| 105 | + decorations[AnnotatorType.MutParam] = createDecorationUnderline("colors.parameter"); |
| 106 | + } else { |
| 107 | + decorations[AnnotatorType.MutLocal] = createDecoration("colors.local"); |
| 108 | + decorations[AnnotatorType.MutParam] = createDecoration("colors.parameter"); |
71 | 109 | } |
72 | | -} |
| 110 | +}; |
73 | 111 |
|
74 | | -export function onDidChangeConfiguration() { |
| 112 | +/** |
| 113 | + * 配置变化时的处理函数 |
| 114 | + */ |
| 115 | +export const onDidChangeConfiguration = (): void => { |
| 116 | + // 清理缓存,强制重新创建装饰器 |
| 117 | + decorationCache.clear(); |
75 | 118 | updateDecorations(); |
76 | | -} |
| 119 | +}; |
77 | 120 |
|
78 | | -let timeoutToReqAnn: NodeJS.Timer; |
| 121 | +// 防抖定时器 |
| 122 | +let timeoutToReqAnn: NodeJS.Timer | undefined; |
79 | 123 |
|
80 | | -export function requestAnnotators(editor: vscode.TextEditor, client: LanguageClient) { |
81 | | - clearTimeout(timeoutToReqAnn); |
| 124 | +/** |
| 125 | + * 请求注释器 - 带防抖功能 |
| 126 | + */ |
| 127 | +export const requestAnnotators = (editor: vscode.TextEditor, client: LanguageClient): void => { |
| 128 | + if (timeoutToReqAnn) { |
| 129 | + clearTimeout(timeoutToReqAnn); |
| 130 | + } |
82 | 131 | timeoutToReqAnn = setTimeout(() => { |
83 | 132 | requestAnnotatorsImpl(editor, client); |
84 | 133 | }, 150); |
85 | | -} |
| 134 | +}; |
86 | 135 |
|
87 | | -function requestAnnotatorsImpl(editor: vscode.TextEditor, client: LanguageClient) { |
88 | | - if (!D_PARAM) { |
| 136 | +/** |
| 137 | + * 异步请求注释器实现 |
| 138 | + */ |
| 139 | +const requestAnnotatorsImpl = async (editor: vscode.TextEditor, client: LanguageClient): Promise<void> => { |
| 140 | + // 确保装饰器已初始化 |
| 141 | + if (Object.keys(decorations).length === 0) { |
89 | 142 | updateDecorations(); |
90 | 143 | } |
91 | 144 |
|
92 | | - let params: notifications.AnnotatorParams = { uri: editor.document.uri.toString() }; |
93 | | - client.sendRequest<notifications.IAnnotator[]>("emmy/annotator", params).then(list => { |
94 | | - let map: Map<AnnotatorType, vscode.Range[]> = new Map(); |
95 | | - map.set(AnnotatorType.ReadOnlyParam, []); |
96 | | - map.set(AnnotatorType.Global, []); |
97 | | - map.set(AnnotatorType.ReadOnlyLocal, []); |
98 | | - map.set(AnnotatorType.MutLocal, []); |
99 | | - map.set(AnnotatorType.MutParam, []); |
| 145 | + const params: notifications.AnnotatorParams = { |
| 146 | + uri: editor.document.uri.toString() |
| 147 | + }; |
100 | 148 |
|
101 | | - if (!list) { |
| 149 | + try { |
| 150 | + const annotationList = await client.sendRequest<notifications.IAnnotator[]>("emmy/annotator", params); |
| 151 | + |
| 152 | + if (!annotationList) { |
102 | 153 | return; |
103 | 154 | } |
104 | 155 |
|
105 | | - list.forEach(annotation => { |
106 | | - let ranges = map.get(annotation.type); |
| 156 | + // 使用 Map 来优化数据收集 |
| 157 | + const rangeMap = new Map<AnnotatorType, vscode.Range[]>([ |
| 158 | + [AnnotatorType.ReadOnlyParam, []], |
| 159 | + [AnnotatorType.Global, []], |
| 160 | + [AnnotatorType.ReadOnlyLocal, []], |
| 161 | + [AnnotatorType.MutLocal, []], |
| 162 | + [AnnotatorType.MutParam, []] |
| 163 | + ]); |
| 164 | + |
| 165 | + // 批量处理注释 |
| 166 | + for (const annotation of annotationList) { |
| 167 | + const ranges = rangeMap.get(annotation.type); |
107 | 168 | if (ranges) { |
108 | 169 | ranges.push(...annotation.ranges); |
109 | 170 | } |
| 171 | + } |
| 172 | + |
| 173 | + // 批量更新装饰器 |
| 174 | + rangeMap.forEach((ranges, type) => { |
| 175 | + updateAnnotators(editor, type, ranges); |
110 | 176 | }); |
111 | | - map.forEach((v, k) => { |
112 | | - updateAnnotators(editor, k, v); |
113 | | - }); |
114 | | - }); |
115 | | -} |
| 177 | + } catch (error) { |
| 178 | + console.error('Failed to get annotations from language server:', error); |
| 179 | + } |
| 180 | +}; |
116 | 181 |
|
117 | | -function updateAnnotators(editor: vscode.TextEditor, type: AnnotatorType, ranges: vscode.Range[]) { |
118 | | - switch (type) { |
119 | | - case AnnotatorType.ReadOnlyParam: |
120 | | - editor.setDecorations(D_PARAM, ranges); |
121 | | - break; |
122 | | - case AnnotatorType.Global: |
123 | | - editor.setDecorations(D_GLOBAL, ranges); |
124 | | - break; |
125 | | - case AnnotatorType.ReadOnlyLocal: |
126 | | - editor.setDecorations(D_LOCAL, ranges); |
127 | | - break; |
128 | | - case AnnotatorType.MutLocal: |
129 | | - editor.setDecorations(D_MUT_LOCAL, ranges); |
130 | | - break; |
131 | | - case AnnotatorType.MutParam: |
132 | | - editor.setDecorations(D_MUT_PARAM, ranges); |
133 | | - break; |
| 182 | +/** |
| 183 | + * 更新编辑器中特定类型的注释器 |
| 184 | + */ |
| 185 | +const updateAnnotators = ( |
| 186 | + editor: vscode.TextEditor, |
| 187 | + type: AnnotatorType, |
| 188 | + ranges: vscode.Range[] |
| 189 | +): void => { |
| 190 | + const decoration = decorations[type]; |
| 191 | + if (decoration) { |
| 192 | + editor.setDecorations(decoration, ranges); |
134 | 193 | } |
135 | | -} |
| 194 | +}; |
| 195 | + |
| 196 | +/** |
| 197 | + * 清理所有缓存和装饰器 - 用于扩展停用时清理 |
| 198 | + */ |
| 199 | +export const dispose = (): void => { |
| 200 | + // 清理防抖定时器 |
| 201 | + if (timeoutToReqAnn) { |
| 202 | + clearTimeout(timeoutToReqAnn); |
| 203 | + timeoutToReqAnn = undefined; |
| 204 | + } |
| 205 | + |
| 206 | + // 清理所有装饰器 |
| 207 | + disposeDecorations(...Object.values(decorations)); |
| 208 | + decorations = {}; |
| 209 | + |
| 210 | + // 清理缓存中的装饰器 |
| 211 | + decorationCache.forEach(decoration => decoration.dispose()); |
| 212 | + decorationCache.clear(); |
| 213 | +}; |
0 commit comments