背景
前几天在搞一个前端的新项目, 使用到webpack
中的一个alias(别名)可以避免引用路径的混乱, 但是vscode
无法很好地支持别名的路径提示
其中介绍使用了一个path-autocomplete
的插件, 简单介绍就是可以设置好别名对应的实际路径, 然后再推断路径提示的时候将别名替换为设置好的路径.
// src\features\PathAutocompleteProvider.ts
// configuration.data.pathMappings 即为一个 key为别名, value为真实路径的对象
Object.keys(configuration.data.pathMappings || {})
.map((key) => {
var candidatePaths = configuration.data.pathMappings[key];
if (typeof candidatePaths == 'string') {
candidatePaths = [candidatePaths];
}
return candidatePaths.map(candidatePath => {
if (workspaceRootPath) {
candidatePath = candidatePath.replace('${workspace}', workspaceRootPath);
}
if (workspaceFolderPath) {
candidatePath = candidatePath.replace('${folder}', workspaceFolderPath);
}
candidatePath = candidatePath.replace('${home}', configuration.data.homeDirectory);
return {
key: key,
path: candidatePath
};
});
})
.some((mappings) => {
var found = false;
mappings.forEach(mapping => {
// 关键的地方在这里, insertedPath 为当前编辑器输入的路径
// 判断输入的路径是否是以某个key值开头的, 如果是则将key进行替换成设置好的路径, 并返回
if (insertedPath.startsWith(mapping.key) || (mapping.key === '$root' && !insertedPath.startsWith('.'))) {
items.push({
// 该别名对应的路径
currentDir: mapping.path,
// 从路径中去掉别名
insertedPath: insertedPath.replace(mapping.key, '')
});
found = true;
}
});
// stop after the first mapping found
return found;
});
大部分情况下, 该插件是可以正常使用, 但是当配置中的key有相同前缀时, 就会出现问题了
比如我的设置是这样
// .setting.json
"path-autocomplete.pathMappings": {
"@": "${folder}/src",
"@view": "${folder}/src/components"
},
而此时输入insertedPath = '@view'
, 我的本意应该是去寻找"${folder}/src/components"
, 但是根据上述代码, 因为Object.keys()
返回的顺序是按照属性创建的先后顺序来的, 故@
排在@view
前面, 又因为@
满足insertedPath.startsWith(mapping.key)
这个条件, 导致插件误认为找到正确对应的路径, 造成错误, 无法显示正确路径
解决
解决办法很简单, 只要让路径匹配到有最长的有相同前缀的key
就行了, 即如果路径是@view
, 则让@view
排前面, 路径是@
则让key@
排前面, 只需在Object.keys()
后多加一个sort(), 按匹配前缀长度大小排列
// .src\features\PathAutocompleteProvider.ts
Object.keys(configuration.data.pathMappings || {})
// 插上这个即可
.sort((key1, key2) => {
const f1 = insertedPath.startsWith(key1) ? key1.length : 0;
const f2 = insertedPath.startsWith(key2) ? key2.length : 0;
return f2 - f1;
})
.map((key) => {
// ... 省略, 与前面一样
})
.some((mappings) => {
// ... 省略, 与前面一样
});