a-calc 3.0.0-beta.20260130.2 → 3.0.0-beta.20260201.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/a-calc.versions.js +4 -3
- package/browser/index.js +2 -4
- package/cjs/index.js +2 -4
- package/es/index.js +2 -4
- package/mcp-server/src/index.js +0 -0
- package/package.json +130 -128
- package/ts-plugin/dist/config_extractor.d.ts +9 -0
- package/ts-plugin/dist/config_extractor.js +104 -0
- package/ts-plugin/dist/constants.d.ts +34 -0
- package/ts-plugin/dist/constants.js +274 -0
- package/ts-plugin/dist/index.d.ts +7 -0
- package/ts-plugin/dist/index.js +294 -0
- package/ts-plugin/dist/utils.d.ts +49 -0
- package/ts-plugin/dist/utils.js +204 -0
- package/ts-plugin/dist/variable_extractor.d.ts +16 -0
- package/ts-plugin/dist/variable_extractor.js +138 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FORMAT_SYNTAX = exports.COMPACT_PRESETS = exports.THOUSANDS_PRESETS = exports.ACALC_PRESETS = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* a-calc ! 系统预设
|
|
6
|
+
*/
|
|
7
|
+
exports.ACALC_PRESETS = [
|
|
8
|
+
{
|
|
9
|
+
name: 't',
|
|
10
|
+
description: '千分位预设',
|
|
11
|
+
has_value: true,
|
|
12
|
+
value_required: false,
|
|
13
|
+
example: '!t 或 !t:indian'
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: 'c',
|
|
17
|
+
description: '紧凑格式',
|
|
18
|
+
has_value: true,
|
|
19
|
+
value_required: false,
|
|
20
|
+
example: '!c 或 !c:cn'
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'i',
|
|
24
|
+
description: '整数补零',
|
|
25
|
+
has_value: true,
|
|
26
|
+
value_required: true,
|
|
27
|
+
example: '!i:3'
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'g',
|
|
31
|
+
description: '格式化分组',
|
|
32
|
+
has_value: true,
|
|
33
|
+
value_required: true,
|
|
34
|
+
example: '!g:money'
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: 'u',
|
|
38
|
+
description: '单位(默认位置)',
|
|
39
|
+
has_value: true,
|
|
40
|
+
value_required: true,
|
|
41
|
+
example: '!u:元'
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: 'ua',
|
|
45
|
+
description: '单位在数字后',
|
|
46
|
+
has_value: true,
|
|
47
|
+
value_required: true,
|
|
48
|
+
example: '!ua:元'
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: 'ub',
|
|
52
|
+
description: '单位在数字前',
|
|
53
|
+
has_value: true,
|
|
54
|
+
value_required: true,
|
|
55
|
+
example: '!ub:$'
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: 'um',
|
|
59
|
+
description: '单位在符号后',
|
|
60
|
+
has_value: true,
|
|
61
|
+
value_required: true,
|
|
62
|
+
example: '!um:元'
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'uh',
|
|
66
|
+
description: '转换但隐藏单位',
|
|
67
|
+
has_value: true,
|
|
68
|
+
value_required: false,
|
|
69
|
+
example: '!uh 或 !uh:元'
|
|
70
|
+
},
|
|
71
|
+
];
|
|
72
|
+
/**
|
|
73
|
+
* 千分位预设值
|
|
74
|
+
*/
|
|
75
|
+
exports.THOUSANDS_PRESETS = [
|
|
76
|
+
{ name: 'en', description: '英文/国际 (1,234,567)' },
|
|
77
|
+
{ name: 'eu', description: '欧洲 (1.234.567)' },
|
|
78
|
+
{ name: 'swiss', description: "瑞士 (1'234'567)" },
|
|
79
|
+
{ name: 'space', description: '空格分隔 (1 234 567)' },
|
|
80
|
+
{ name: 'fr', description: '法国 (1 234 567,89)' },
|
|
81
|
+
{ name: 'indian', description: '印度 (12,34,567)' },
|
|
82
|
+
{ name: 'wan', description: '万进制 (1234,5678)' },
|
|
83
|
+
];
|
|
84
|
+
/**
|
|
85
|
+
* 紧凑格式预设值
|
|
86
|
+
*/
|
|
87
|
+
exports.COMPACT_PRESETS = [
|
|
88
|
+
{ name: 'en', description: '英文 (K/M/B/T)' },
|
|
89
|
+
{ name: 'cn', description: '中文千进制 (千/百万/十亿)' },
|
|
90
|
+
{ name: 'wan', description: '万进制 (万/亿)' },
|
|
91
|
+
{ name: 'storage', description: '存储单位 (KB/MB/GB)' },
|
|
92
|
+
{ name: 'indian', description: '印度 (K/L/Cr)' },
|
|
93
|
+
];
|
|
94
|
+
/**
|
|
95
|
+
* 格式化语法列表(用于 | 后的补全)
|
|
96
|
+
* insert_text: 实际插入的文本(不含占位符)
|
|
97
|
+
*/
|
|
98
|
+
exports.FORMAT_SYNTAX = [
|
|
99
|
+
// 小数位控制
|
|
100
|
+
{
|
|
101
|
+
name: '=N',
|
|
102
|
+
insert_text: '=',
|
|
103
|
+
description: '精确 N 位小数',
|
|
104
|
+
example: '=2 → 1.50',
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: '<=N',
|
|
108
|
+
insert_text: '<=',
|
|
109
|
+
description: '最多 N 位小数(不补零)',
|
|
110
|
+
example: '<=2 → 1.5',
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: '>=N',
|
|
114
|
+
insert_text: '>=',
|
|
115
|
+
description: '最少 N 位小数(补零)',
|
|
116
|
+
example: '>=3 → 1.500',
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: '>=N<=M',
|
|
120
|
+
insert_text: '>=',
|
|
121
|
+
description: '小数位数范围',
|
|
122
|
+
example: '>=2<=4 → 1.5 ~ 1.5000',
|
|
123
|
+
},
|
|
124
|
+
// 舍入规则
|
|
125
|
+
{
|
|
126
|
+
name: '~5',
|
|
127
|
+
insert_text: '~5',
|
|
128
|
+
description: '四舍五入',
|
|
129
|
+
example: '~5=2: 1.125 → 1.13',
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: '~6',
|
|
133
|
+
insert_text: '~6',
|
|
134
|
+
description: '银行家舍入(四舍六入五取偶)',
|
|
135
|
+
example: '~6=2: 1.125 → 1.12',
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: '~+',
|
|
139
|
+
insert_text: '~+',
|
|
140
|
+
description: '进位(向上取整)',
|
|
141
|
+
example: '~+=2: 1.121 → 1.13',
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
name: '~-',
|
|
145
|
+
insert_text: '~-',
|
|
146
|
+
description: '截断(向下取整,默认)',
|
|
147
|
+
example: '~-=2: 1.129 → 1.12',
|
|
148
|
+
},
|
|
149
|
+
// 千分位
|
|
150
|
+
{
|
|
151
|
+
name: ',',
|
|
152
|
+
insert_text: ',',
|
|
153
|
+
description: '千分位分隔',
|
|
154
|
+
example: '1234567 → 1,234,567',
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
name: '!t',
|
|
158
|
+
insert_text: '!t',
|
|
159
|
+
description: '千分位(默认预设)',
|
|
160
|
+
example: '1234567 → 1,234,567',
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
name: '!t:preset',
|
|
164
|
+
insert_text: '!t:',
|
|
165
|
+
description: '千分位预设',
|
|
166
|
+
example: '!t:indian → 12,34,567',
|
|
167
|
+
},
|
|
168
|
+
// 紧凑格式
|
|
169
|
+
{
|
|
170
|
+
name: '!c',
|
|
171
|
+
insert_text: '!c',
|
|
172
|
+
description: '紧凑格式(默认 K/M/B/T)',
|
|
173
|
+
example: '1500 → 1.5K',
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
name: '!c:preset',
|
|
177
|
+
insert_text: '!c:',
|
|
178
|
+
description: '紧凑格式预设',
|
|
179
|
+
example: '!c:wan → 1万',
|
|
180
|
+
},
|
|
181
|
+
// 百分比
|
|
182
|
+
{
|
|
183
|
+
name: '%',
|
|
184
|
+
insert_text: '%',
|
|
185
|
+
description: '百分比格式',
|
|
186
|
+
example: '0.1234 → 12.34%',
|
|
187
|
+
},
|
|
188
|
+
// 正负号
|
|
189
|
+
{
|
|
190
|
+
name: '+',
|
|
191
|
+
insert_text: '+',
|
|
192
|
+
description: '显示正号',
|
|
193
|
+
example: '100 → +100',
|
|
194
|
+
},
|
|
195
|
+
// 整数补零
|
|
196
|
+
{
|
|
197
|
+
name: '!i:N',
|
|
198
|
+
insert_text: '!i:',
|
|
199
|
+
description: '整数部分补零到 N 位',
|
|
200
|
+
example: '!i:3: 5 → 005',
|
|
201
|
+
},
|
|
202
|
+
// 科学计数法
|
|
203
|
+
{
|
|
204
|
+
name: '!e',
|
|
205
|
+
insert_text: '!e',
|
|
206
|
+
description: '科学计数法',
|
|
207
|
+
example: '123456789 → 1.23e+8',
|
|
208
|
+
},
|
|
209
|
+
// 分数
|
|
210
|
+
{
|
|
211
|
+
name: '/',
|
|
212
|
+
insert_text: '/',
|
|
213
|
+
description: '分数形式',
|
|
214
|
+
example: '0.5 → 1/2',
|
|
215
|
+
},
|
|
216
|
+
// 返回类型
|
|
217
|
+
{
|
|
218
|
+
name: '!n',
|
|
219
|
+
insert_text: '!n',
|
|
220
|
+
description: '返回数字类型',
|
|
221
|
+
example: '"3" → 3 (number)',
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
name: '!s',
|
|
225
|
+
insert_text: '!s',
|
|
226
|
+
description: '返回字符串类型(默认)',
|
|
227
|
+
example: '3 → "3" (string)',
|
|
228
|
+
},
|
|
229
|
+
// 单位
|
|
230
|
+
{
|
|
231
|
+
name: '!u:unit',
|
|
232
|
+
insert_text: '!u:',
|
|
233
|
+
description: '单位(默认位置)',
|
|
234
|
+
example: '!u:元 → 100元',
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
name: '!ua:unit',
|
|
238
|
+
insert_text: '!ua:',
|
|
239
|
+
description: '单位在数字后',
|
|
240
|
+
example: '!ua:$ → 100$',
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
name: '!ub:unit',
|
|
244
|
+
insert_text: '!ub:',
|
|
245
|
+
description: '单位在数字前',
|
|
246
|
+
example: '!ub:$ → $100',
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
name: '!um:unit',
|
|
250
|
+
insert_text: '!um:',
|
|
251
|
+
description: '单位在符号后',
|
|
252
|
+
example: '!um:$ → +$100',
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
name: '!uh:unit',
|
|
256
|
+
insert_text: '!uh:',
|
|
257
|
+
description: '转换但隐藏单位',
|
|
258
|
+
example: '!uh:元 → 100',
|
|
259
|
+
},
|
|
260
|
+
// 范围限制
|
|
261
|
+
{
|
|
262
|
+
name: '[min,max]',
|
|
263
|
+
insert_text: '[',
|
|
264
|
+
description: '范围限制',
|
|
265
|
+
example: '[0,100]: 150 → 100',
|
|
266
|
+
},
|
|
267
|
+
// 格式化分组
|
|
268
|
+
{
|
|
269
|
+
name: '!g:name',
|
|
270
|
+
insert_text: '!g:',
|
|
271
|
+
description: '格式化分组',
|
|
272
|
+
example: '!g:money → 预设格式',
|
|
273
|
+
},
|
|
274
|
+
];
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const constants_1 = require("./constants");
|
|
3
|
+
const utils_1 = require("./utils");
|
|
4
|
+
const config_extractor_1 = require("./config_extractor");
|
|
5
|
+
const variable_extractor_1 = require("./variable_extractor");
|
|
6
|
+
function init(modules) {
|
|
7
|
+
const ts_module = modules.typescript;
|
|
8
|
+
// 缓存提取的配置
|
|
9
|
+
let cached_config = null;
|
|
10
|
+
function create(info) {
|
|
11
|
+
const logger = info.project.projectService.logger;
|
|
12
|
+
logger.info('a-calc-ts-plugin: initializing');
|
|
13
|
+
const ls = info.languageService;
|
|
14
|
+
// 获取或更新配置
|
|
15
|
+
function get_config() {
|
|
16
|
+
const program = ls.getProgram();
|
|
17
|
+
// 简单的缓存策略:每次都重新提取(后续可优化)
|
|
18
|
+
if (program) {
|
|
19
|
+
cached_config = (0, config_extractor_1.extract_config_from_project)(ts_module, program, logger);
|
|
20
|
+
}
|
|
21
|
+
return cached_config || { units: [], shortcut_prefix: '!u' };
|
|
22
|
+
}
|
|
23
|
+
const original_get_completions = ls.getCompletionsAtPosition.bind(ls);
|
|
24
|
+
ls.getCompletionsAtPosition = (file_name, position, options) => {
|
|
25
|
+
const completions = get_acalc_completions(ts_module, ls, file_name, position, logger, get_config);
|
|
26
|
+
if (completions)
|
|
27
|
+
return completions;
|
|
28
|
+
return original_get_completions(file_name, position, options);
|
|
29
|
+
};
|
|
30
|
+
return ls;
|
|
31
|
+
}
|
|
32
|
+
return { create };
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 获取 a-calc 表达式的补全
|
|
36
|
+
*/
|
|
37
|
+
function get_acalc_completions(ts_module, ls, file_name, position, logger, get_config) {
|
|
38
|
+
const program = ls.getProgram();
|
|
39
|
+
if (!program)
|
|
40
|
+
return undefined;
|
|
41
|
+
const source_file = program.getSourceFile(file_name);
|
|
42
|
+
if (!source_file)
|
|
43
|
+
return undefined;
|
|
44
|
+
const type_checker = program.getTypeChecker();
|
|
45
|
+
const context = (0, utils_1.is_in_acalc_string)(ts_module, source_file, position, type_checker, logger);
|
|
46
|
+
logger.info(`a-calc-ts-plugin: in_string=${context.in_string}, offset=${context.offset_in_string}, has_second_arg=${!!context.second_arg}`);
|
|
47
|
+
if (!context.in_string)
|
|
48
|
+
return undefined;
|
|
49
|
+
const text_before = (0, utils_1.get_text_before_cursor)(context.string_content, context.offset_in_string);
|
|
50
|
+
logger.info(`a-calc-ts-plugin: text_before="${text_before}"`);
|
|
51
|
+
const trigger = (0, utils_1.detect_trigger)(text_before);
|
|
52
|
+
logger.info(`a-calc-ts-plugin: trigger=${JSON.stringify(trigger)}`);
|
|
53
|
+
// 在 a-calc 字符串内,如果没有特定触发,返回空补全列表
|
|
54
|
+
// 避免原始 TypeScript 补全显示奇怪的内容
|
|
55
|
+
if (!trigger) {
|
|
56
|
+
return {
|
|
57
|
+
isGlobalCompletion: false,
|
|
58
|
+
isMemberCompletion: false,
|
|
59
|
+
isNewIdentifierLocation: false,
|
|
60
|
+
entries: [],
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
if (trigger.type === 'preset_name') {
|
|
64
|
+
return create_preset_name_completions(ts_module, trigger.prefix);
|
|
65
|
+
}
|
|
66
|
+
if (trigger.type === 'preset_value') {
|
|
67
|
+
return create_preset_value_completions(ts_module, trigger.preset, trigger.prefix, get_config());
|
|
68
|
+
}
|
|
69
|
+
if (trigger.type === 'shortcut') {
|
|
70
|
+
return create_shortcut_completions(ts_module, trigger.prefix, get_config());
|
|
71
|
+
}
|
|
72
|
+
if (trigger.type === 'format_syntax') {
|
|
73
|
+
const result = create_format_syntax_completions(ts_module, position);
|
|
74
|
+
logger.info(`a-calc-ts-plugin: format_syntax entries count=${result.entries.length}`);
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
if (trigger.type === 'format_variable') {
|
|
78
|
+
logger.info(`a-calc-ts-plugin: format_variable trigger, prefix="${trigger.prefix}"`);
|
|
79
|
+
const program = ls.getProgram();
|
|
80
|
+
if (program && context.second_arg) {
|
|
81
|
+
const type_checker = program.getTypeChecker();
|
|
82
|
+
const variables = (0, variable_extractor_1.extract_variables_from_arg)(ts_module, context.second_arg, type_checker, logger);
|
|
83
|
+
logger.info(`a-calc-ts-plugin: extracted ${variables.length} variables for format area`);
|
|
84
|
+
if (variables.length > 0) {
|
|
85
|
+
const result = create_variable_completions(ts_module, variables, trigger.prefix, position);
|
|
86
|
+
logger.info(`a-calc-ts-plugin: returning ${result.entries.length} format variable completions`);
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
if (trigger.type === 'variable') {
|
|
93
|
+
logger.info(`a-calc-ts-plugin: variable trigger, prefix="${trigger.prefix}"`);
|
|
94
|
+
logger.info(`a-calc-ts-plugin: context.second_arg exists=${!!context.second_arg}`);
|
|
95
|
+
const program = ls.getProgram();
|
|
96
|
+
if (program && context.second_arg) {
|
|
97
|
+
const type_checker = program.getTypeChecker();
|
|
98
|
+
const variables = (0, variable_extractor_1.extract_variables_from_arg)(ts_module, context.second_arg, type_checker, logger);
|
|
99
|
+
logger.info(`a-calc-ts-plugin: extracted ${variables.length} variables`);
|
|
100
|
+
if (variables.length > 0) {
|
|
101
|
+
const result = create_variable_completions(ts_module, variables, trigger.prefix, position);
|
|
102
|
+
logger.info(`a-calc-ts-plugin: returning ${result.entries.length} variable completions`);
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
logger.info(`a-calc-ts-plugin: no program or second_arg, skipping variable completion`);
|
|
108
|
+
}
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* 创建预设名补全 (! 后)
|
|
115
|
+
*/
|
|
116
|
+
function create_preset_name_completions(ts_module, prefix) {
|
|
117
|
+
const entries = constants_1.ACALC_PRESETS
|
|
118
|
+
.filter(p => p.name.startsWith(prefix))
|
|
119
|
+
.map((p, i) => ({
|
|
120
|
+
name: p.name,
|
|
121
|
+
kind: ts_module.ScriptElementKind.keyword,
|
|
122
|
+
kindModifiers: '',
|
|
123
|
+
sortText: String(i).padStart(2, '0'),
|
|
124
|
+
labelDetails: { description: p.description },
|
|
125
|
+
}));
|
|
126
|
+
return {
|
|
127
|
+
isGlobalCompletion: false,
|
|
128
|
+
isMemberCompletion: false,
|
|
129
|
+
isNewIdentifierLocation: false,
|
|
130
|
+
entries,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* 创建预设值补全 (!t: 后)
|
|
135
|
+
*/
|
|
136
|
+
function create_preset_value_completions(ts_module, preset, prefix, config) {
|
|
137
|
+
// 千分位预设
|
|
138
|
+
if (preset === 't') {
|
|
139
|
+
return create_completions_from_list(ts_module, constants_1.THOUSANDS_PRESETS, prefix);
|
|
140
|
+
}
|
|
141
|
+
// 紧凑格式预设
|
|
142
|
+
if (preset === 'c') {
|
|
143
|
+
return create_completions_from_list(ts_module, constants_1.COMPACT_PRESETS, prefix);
|
|
144
|
+
}
|
|
145
|
+
// 单位相关预设 (u, ua, ub, um, uh)
|
|
146
|
+
if (['u', 'ua', 'ub', 'um', 'uh'].includes(preset)) {
|
|
147
|
+
return create_unit_completions(ts_module, config.units, prefix);
|
|
148
|
+
}
|
|
149
|
+
return undefined;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* 创建快捷语法补全 (: 后)
|
|
153
|
+
*/
|
|
154
|
+
function create_shortcut_completions(ts_module, prefix, config) {
|
|
155
|
+
const shortcut_prefix = config.shortcut_prefix;
|
|
156
|
+
// 根据 shortcut_prefix 决定补全内容
|
|
157
|
+
if (shortcut_prefix === '!t') {
|
|
158
|
+
return create_completions_from_list(ts_module, constants_1.THOUSANDS_PRESETS, prefix);
|
|
159
|
+
}
|
|
160
|
+
if (shortcut_prefix === '!c') {
|
|
161
|
+
return create_completions_from_list(ts_module, constants_1.COMPACT_PRESETS, prefix);
|
|
162
|
+
}
|
|
163
|
+
// 默认 !u 或其他单位相关前缀
|
|
164
|
+
return create_unit_completions(ts_module, config.units, prefix);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* 从列表创建补全
|
|
168
|
+
*/
|
|
169
|
+
function create_completions_from_list(ts_module, list, prefix) {
|
|
170
|
+
const entries = list
|
|
171
|
+
.filter(item => item.name.startsWith(prefix))
|
|
172
|
+
.map((item, i) => ({
|
|
173
|
+
name: item.name,
|
|
174
|
+
kind: ts_module.ScriptElementKind.string,
|
|
175
|
+
kindModifiers: '',
|
|
176
|
+
sortText: String(i).padStart(2, '0'),
|
|
177
|
+
labelDetails: { description: item.description },
|
|
178
|
+
}));
|
|
179
|
+
return {
|
|
180
|
+
isGlobalCompletion: false,
|
|
181
|
+
isMemberCompletion: false,
|
|
182
|
+
isNewIdentifierLocation: false,
|
|
183
|
+
entries,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* 创建单位补全
|
|
188
|
+
*/
|
|
189
|
+
function create_unit_completions(ts_module, units, prefix) {
|
|
190
|
+
const entries = units
|
|
191
|
+
.filter(u => u.startsWith(prefix))
|
|
192
|
+
.map((u, i) => ({
|
|
193
|
+
name: u,
|
|
194
|
+
kind: ts_module.ScriptElementKind.string,
|
|
195
|
+
kindModifiers: '',
|
|
196
|
+
sortText: String(i).padStart(2, '0'),
|
|
197
|
+
}));
|
|
198
|
+
return {
|
|
199
|
+
isGlobalCompletion: false,
|
|
200
|
+
isMemberCompletion: false,
|
|
201
|
+
isNewIdentifierLocation: false,
|
|
202
|
+
entries,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* 创建格式化语法补全(| 后)
|
|
207
|
+
*/
|
|
208
|
+
function create_format_syntax_completions(ts_module, position) {
|
|
209
|
+
const entries = constants_1.FORMAT_SYNTAX.map((item, i) => ({
|
|
210
|
+
name: item.name,
|
|
211
|
+
insertText: item.insert_text,
|
|
212
|
+
kind: ts_module.ScriptElementKind.keyword,
|
|
213
|
+
kindModifiers: '',
|
|
214
|
+
sortText: String(i).padStart(2, '0'),
|
|
215
|
+
labelDetails: {
|
|
216
|
+
description: `${item.description} (${item.example})`,
|
|
217
|
+
},
|
|
218
|
+
replacementSpan: {
|
|
219
|
+
start: position,
|
|
220
|
+
length: 0,
|
|
221
|
+
},
|
|
222
|
+
}));
|
|
223
|
+
return {
|
|
224
|
+
isGlobalCompletion: false,
|
|
225
|
+
isMemberCompletion: false,
|
|
226
|
+
isNewIdentifierLocation: true,
|
|
227
|
+
entries,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* 创建变量补全(表达式区域)
|
|
232
|
+
*/
|
|
233
|
+
function create_variable_completions(ts_module, variables, prefix, position) {
|
|
234
|
+
// 解析前缀,支持深度路径如 "user.na"
|
|
235
|
+
const prefix_parts = prefix.split('.');
|
|
236
|
+
const parent_path = prefix_parts.slice(0, -1).join('.');
|
|
237
|
+
const current_prefix = prefix_parts[prefix_parts.length - 1] || '';
|
|
238
|
+
// 过滤变量
|
|
239
|
+
let filtered_variables;
|
|
240
|
+
if (parent_path) {
|
|
241
|
+
// 有父路径:只显示该路径下的直接子属性
|
|
242
|
+
filtered_variables = variables.filter(v => {
|
|
243
|
+
// 检查是否是 parent_path 的直接子属性
|
|
244
|
+
if (!v.path.startsWith(parent_path + '.'))
|
|
245
|
+
return false;
|
|
246
|
+
const remaining = v.path.slice(parent_path.length + 1);
|
|
247
|
+
// 只要直接子属性(不包含更深的路径)
|
|
248
|
+
if (remaining.includes('.'))
|
|
249
|
+
return false;
|
|
250
|
+
// 匹配当前前缀
|
|
251
|
+
return remaining.startsWith(current_prefix);
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
// 无父路径:只显示顶层属性
|
|
256
|
+
filtered_variables = variables.filter(v => {
|
|
257
|
+
// 只要顶层属性
|
|
258
|
+
if (v.path.includes('.'))
|
|
259
|
+
return false;
|
|
260
|
+
// 匹配前缀
|
|
261
|
+
return v.path.startsWith(current_prefix);
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
// 计算需要替换的文本长度
|
|
265
|
+
const replace_length = current_prefix.length;
|
|
266
|
+
const entries = filtered_variables.map((v, i) => {
|
|
267
|
+
// 获取要显示和插入的名称(只是当前层级的名称)
|
|
268
|
+
const display_name = parent_path
|
|
269
|
+
? v.path.slice(parent_path.length + 1)
|
|
270
|
+
: v.path;
|
|
271
|
+
return {
|
|
272
|
+
name: display_name,
|
|
273
|
+
kind: v.has_children
|
|
274
|
+
? ts_module.ScriptElementKind.moduleElement
|
|
275
|
+
: ts_module.ScriptElementKind.memberVariableElement,
|
|
276
|
+
kindModifiers: '',
|
|
277
|
+
sortText: String(i).padStart(3, '0'),
|
|
278
|
+
labelDetails: {
|
|
279
|
+
description: v.type_desc,
|
|
280
|
+
},
|
|
281
|
+
replacementSpan: {
|
|
282
|
+
start: position - replace_length,
|
|
283
|
+
length: replace_length,
|
|
284
|
+
},
|
|
285
|
+
};
|
|
286
|
+
});
|
|
287
|
+
return {
|
|
288
|
+
isGlobalCompletion: false,
|
|
289
|
+
isMemberCompletion: parent_path.length > 0,
|
|
290
|
+
isNewIdentifierLocation: false,
|
|
291
|
+
entries,
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
module.exports = init;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type * as ts from 'typescript/lib/tsserverlibrary';
|
|
2
|
+
/**
|
|
3
|
+
* a-calc 字符串上下文信息
|
|
4
|
+
*/
|
|
5
|
+
export interface AcalcStringContext {
|
|
6
|
+
in_string: boolean;
|
|
7
|
+
string_content: string;
|
|
8
|
+
offset_in_string: number;
|
|
9
|
+
/** 第二个参数的 AST 节点(用于提取变量) */
|
|
10
|
+
second_arg?: ts.Expression;
|
|
11
|
+
/** 函数名 */
|
|
12
|
+
func_name?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* 检测位置是否在 calc/fmt 等函数的字符串参数内
|
|
16
|
+
*/
|
|
17
|
+
export declare function is_in_acalc_string(ts_module: typeof ts, source_file: ts.SourceFile, position: number, type_checker?: ts.TypeChecker, logger?: {
|
|
18
|
+
info: (msg: string) => void;
|
|
19
|
+
}): AcalcStringContext;
|
|
20
|
+
/**
|
|
21
|
+
* 获取光标前的文本,用于判断触发条件
|
|
22
|
+
*/
|
|
23
|
+
export declare function get_text_before_cursor(content: string, offset: number): string;
|
|
24
|
+
/**
|
|
25
|
+
* 补全触发类型
|
|
26
|
+
*/
|
|
27
|
+
export type TriggerType = {
|
|
28
|
+
type: 'preset_name';
|
|
29
|
+
prefix: string;
|
|
30
|
+
} | {
|
|
31
|
+
type: 'preset_value';
|
|
32
|
+
preset: string;
|
|
33
|
+
prefix: string;
|
|
34
|
+
} | {
|
|
35
|
+
type: 'shortcut';
|
|
36
|
+
prefix: string;
|
|
37
|
+
} | {
|
|
38
|
+
type: 'format_syntax';
|
|
39
|
+
} | {
|
|
40
|
+
type: 'variable';
|
|
41
|
+
prefix: string;
|
|
42
|
+
} | {
|
|
43
|
+
type: 'format_variable';
|
|
44
|
+
prefix: string;
|
|
45
|
+
} | null;
|
|
46
|
+
/**
|
|
47
|
+
* 检测补全触发类型
|
|
48
|
+
*/
|
|
49
|
+
export declare function detect_trigger(text_before: string): TriggerType;
|