Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
xxadev
vscode
提交
99970f4d
V
vscode
项目概览
xxadev
/
vscode
与 Fork 源项目一致
从无法访问的项目Fork
通知
2
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
V
vscode
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
99970f4d
编写于
8月 22, 2019
作者:
P
Pine Wu
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Validation for array-of-string settings. Fix #77459
上级
4454769a
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
209 addition
and
17 deletion
+209
-17
src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css
...nch/contrib/preferences/browser/media/settingsEditor2.css
+4
-0
src/vs/workbench/contrib/preferences/browser/settingsTree.ts
src/vs/workbench/contrib/preferences/browser/settingsTree.ts
+65
-16
src/vs/workbench/services/preferences/common/preferencesModels.ts
...orkbench/services/preferences/common/preferencesModels.ts
+61
-0
src/vs/workbench/services/preferences/test/common/preferencesModel.test.ts
...services/preferences/test/common/preferencesModel.test.ts
+79
-1
未找到文件。
src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css
浏览文件 @
99970f4d
...
...
@@ -369,6 +369,10 @@
margin-top
:
-1px
;
z-index
:
1
;
}
.settings-editor
>
.settings-body
>
.settings-tree-container
.setting-item.setting-item-list
.setting-item-contents.invalid-input
.setting-item-validation-message
{
position
:
static
;
margin-top
:
1rem
;
}
.settings-editor
>
.settings-body
>
.settings-tree-container
.setting-item.setting-item-text
.setting-item-validation-message
{
width
:
500px
;
...
...
src/vs/workbench/contrib/preferences/browser/settingsTree.ts
浏览文件 @
99970f4d
...
...
@@ -224,8 +224,9 @@ interface ISettingComplexItemTemplate extends ISettingItemTemplate<void> {
button
:
Button
;
}
interface
ISettingListItemTemplate
extends
ISettingItemTemplate
<
voi
d
>
{
interface
ISettingListItemTemplate
extends
ISettingItemTemplate
<
string
[]
|
undefine
d
>
{
listWidget
:
ListSettingWidget
;
validationErrorMessageElement
:
HTMLElement
;
}
interface
ISettingExcludeItemTemplate
extends
ISettingItemTemplate
<
void
>
{
...
...
@@ -679,6 +680,9 @@ export class SettingArrayRenderer extends AbstractSettingRenderer implements ITr
renderTemplate
(
container
:
HTMLElement
):
ISettingListItemTemplate
{
const
common
=
this
.
renderCommonTemplate
(
null
,
container
,
'
list
'
);
const
descriptionElement
=
common
.
containerElement
.
querySelector
(
'
.setting-item-description
'
)
!
;
const
validationErrorMessageElement
=
$
(
'
.setting-item-validation-message
'
);
descriptionElement
.
after
(
validationErrorMessageElement
);
const
listWidget
=
this
.
_instantiationService
.
createInstance
(
ListSettingWidget
,
common
.
controlElement
);
listWidget
.
domNode
.
classList
.
add
(
AbstractSettingRenderer
.
CONTROL_CLASS
);
...
...
@@ -686,19 +690,40 @@ export class SettingArrayRenderer extends AbstractSettingRenderer implements ITr
const
template
:
ISettingListItemTemplate
=
{
...
common
,
listWidget
listWidget
,
validationErrorMessageElement
};
this
.
addSettingElementFocusHandler
(
template
);
common
.
toDispose
.
push
(
listWidget
.
onDidChangeList
(
e
=>
this
.
onDidChangeList
(
template
,
e
)));
common
.
toDispose
.
push
(
listWidget
.
onDidChangeList
(
e
=>
{
const
newList
=
this
.
computeNewList
(
template
,
e
);
this
.
onDidChangeList
(
template
,
newList
);
if
(
newList
!==
null
&&
template
.
onChange
)
{
template
.
onChange
(
newList
);
}
})
);
return
template
;
}
private
onDidChangeList
(
template
:
ISettingListItemTemplate
,
e
:
IListChangeEvent
):
void
{
private
onDidChangeList
(
template
:
ISettingListItemTemplate
,
newList
:
string
[]
|
undefined
|
null
):
void
{
if
(
!
template
.
context
||
newList
===
null
)
{
return
;
}
this
.
_onDidChangeSetting
.
fire
({
key
:
template
.
context
.
setting
.
key
,
value
:
newList
,
type
:
template
.
context
.
valueType
});
}
private
computeNewList
(
template
:
ISettingListItemTemplate
,
e
:
IListChangeEvent
):
string
[]
|
undefined
|
null
{
if
(
template
.
context
)
{
let
newValue
:
any
[]
=
[];
let
newValue
:
string
[]
=
[];
if
(
isArray
(
template
.
context
.
scopeValue
))
{
newValue
=
[...
template
.
context
.
scopeValue
];
}
else
if
(
isArray
(
template
.
context
.
value
))
{
...
...
@@ -732,29 +757,30 @@ export class SettingArrayRenderer extends AbstractSettingRenderer implements ITr
template
.
context
.
defaultValue
.
length
===
newValue
.
length
&&
template
.
context
.
defaultValue
.
join
()
===
newValue
.
join
()
)
{
return
this
.
_onDidChangeSetting
.
fire
({
key
:
template
.
context
.
setting
.
key
,
value
:
undefined
,
// reset setting
type
:
template
.
context
.
valueType
});
return
undefined
;
}
this
.
_onDidChangeSetting
.
fire
({
key
:
template
.
context
.
setting
.
key
,
value
:
newValue
,
type
:
template
.
context
.
valueType
});
return
newValue
;
}
return
undefined
;
}
renderElement
(
element
:
ITreeNode
<
SettingsTreeSettingElement
,
never
>
,
index
:
number
,
templateData
:
ISettingListItemTemplate
):
void
{
super
.
renderSettingElement
(
element
,
index
,
templateData
);
}
protected
renderValue
(
dataElement
:
SettingsTreeSettingElement
,
template
:
ISettingListItemTemplate
,
onChange
:
(
value
:
string
)
=>
void
):
void
{
protected
renderValue
(
dataElement
:
SettingsTreeSettingElement
,
template
:
ISettingListItemTemplate
,
onChange
:
(
value
:
string
[]
|
undefined
)
=>
void
):
void
{
const
value
=
getListDisplayValue
(
dataElement
);
template
.
listWidget
.
setValue
(
value
);
template
.
context
=
dataElement
;
template
.
onChange
=
(
v
)
=>
{
onChange
(
v
);
renderArrayValidations
(
dataElement
,
template
,
v
,
false
);
};
renderArrayValidations
(
dataElement
,
template
,
value
.
map
(
v
=>
v
.
value
),
true
);
}
}
...
...
@@ -1237,6 +1263,29 @@ function renderValidations(dataElement: SettingsTreeSettingElement, template: IS
DOM
.
removeClass
(
template
.
containerElement
,
'
invalid-input
'
);
}
function
renderArrayValidations
(
dataElement
:
SettingsTreeSettingElement
,
template
:
ISettingListItemTemplate
,
value
:
string
[]
|
undefined
,
calledOnStartup
:
boolean
)
{
DOM
.
addClass
(
template
.
containerElement
,
'
invalid-input
'
);
if
(
dataElement
.
setting
.
validator
)
{
const
errMsg
=
dataElement
.
setting
.
validator
(
value
);
if
(
errMsg
&&
errMsg
!==
''
)
{
DOM
.
addClass
(
template
.
containerElement
,
'
invalid-input
'
);
template
.
validationErrorMessageElement
.
innerText
=
errMsg
;
const
validationError
=
localize
(
'
validationError
'
,
"
Validation Error.
"
);
template
.
containerElement
.
setAttribute
(
'
aria-label
'
,
[
dataElement
.
setting
.
key
,
validationError
,
errMsg
].
join
(
'
'
));
if
(
!
calledOnStartup
)
{
ariaAlert
(
validationError
+
'
'
+
errMsg
);
}
return
;
}
else
{
template
.
containerElement
.
setAttribute
(
'
aria-label
'
,
dataElement
.
setting
.
key
);
DOM
.
removeClass
(
template
.
containerElement
,
'
invalid-input
'
);
}
}
}
function
cleanRenderedMarkdown
(
element
:
Node
):
void
{
for
(
let
i
=
0
;
i
<
element
.
childNodes
.
length
;
i
++
)
{
const
child
=
element
.
childNodes
.
item
(
i
);
...
...
src/vs/workbench/services/preferences/common/preferencesModels.ts
浏览文件 @
99970f4d
...
...
@@ -1028,6 +1028,67 @@ class SettingsContentBuilder {
}
export
function
createValidator
(
prop
:
IConfigurationPropertySchema
):
(
value
:
any
)
=>
(
string
|
null
)
{
// Only for array of string
if
(
prop
.
type
===
'
array
'
&&
prop
.
items
&&
!
isArray
(
prop
.
items
)
&&
prop
.
items
.
type
===
'
string
'
)
{
const
propItems
=
prop
.
items
;
if
(
propItems
&&
!
isArray
(
propItems
)
&&
propItems
.
type
===
'
string
'
)
{
const
withQuotes
=
(
s
:
string
)
=>
`'`
+
s
+
`'`
;
return
value
=>
{
if
(
!
value
)
{
return
null
;
}
let
message
=
''
;
const
stringArrayValue
=
value
as
string
[];
if
(
prop
.
minItems
&&
stringArrayValue
.
length
<
prop
.
minItems
)
{
message
+=
nls
.
localize
(
'
validations.stringArrayMinItem
'
,
'
Array must have at least {0} items
'
,
prop
.
minItems
);
message
+=
'
\n
'
;
}
if
(
prop
.
maxItems
&&
stringArrayValue
.
length
>
prop
.
maxItems
)
{
message
+=
nls
.
localize
(
'
validations.stringArrayMaxItem
'
,
'
Array must have less than {0} items
'
,
prop
.
maxItems
);
message
+=
'
\n
'
;
}
if
(
typeof
propItems
.
pattern
===
'
string
'
)
{
const
patternRegex
=
new
RegExp
(
propItems
.
pattern
);
stringArrayValue
.
forEach
(
v
=>
{
if
(
!
patternRegex
.
test
(
v
))
{
message
+=
propItems
.
patternErrorMessage
||
nls
.
localize
(
'
validations.stringArrayItemPattern
'
,
'
Value {0} must match regex {1}.
'
,
withQuotes
(
v
),
withQuotes
(
propItems
.
pattern
!
)
);
}
});
}
const
propItemsEnum
=
propItems
.
enum
;
if
(
propItemsEnum
)
{
stringArrayValue
.
forEach
(
v
=>
{
if
(
propItemsEnum
.
indexOf
(
v
)
===
-
1
)
{
message
+=
nls
.
localize
(
'
validations.stringArrayItemEnum
'
,
'
Value {0} is not one of {1}
'
,
withQuotes
(
v
),
'
[
'
+
propItemsEnum
.
map
(
withQuotes
).
join
(
'
,
'
)
+
'
]
'
);
message
+=
'
\n
'
;
}
});
}
return
message
;
};
}
}
return
value
=>
{
let
exclusiveMax
:
number
|
undefined
;
let
exclusiveMin
:
number
|
undefined
;
...
...
src/vs/workbench/services/preferences/test/common/preferencesModel.test.ts
浏览文件 @
99970f4d
...
...
@@ -250,4 +250,82 @@ suite('Preferences Model test', () => {
withMessage
.
rejects
(
'
'
).
withMessage
(
'
always error!
'
);
withMessage
.
rejects
(
'
1
'
).
withMessage
(
'
always error!
'
);
});
});
\ No newline at end of file
class
ArrayTester
{
private
validator
:
(
value
:
any
)
=>
string
|
null
;
constructor
(
private
settings
:
IConfigurationPropertySchema
)
{
this
.
validator
=
createValidator
(
settings
)
!
;
}
public
accepts
(
input
:
string
[])
{
assert
.
equal
(
this
.
validator
(
input
),
''
,
`Expected
${
JSON
.
stringify
(
this
.
settings
)}
to accept
\`
${
JSON
.
stringify
(
input
)}
\`
. Got
${
this
.
validator
(
input
)}
.`
);
}
public
rejects
(
input
:
any
[])
{
assert
.
notEqual
(
this
.
validator
(
input
),
''
,
`Expected
${
JSON
.
stringify
(
this
.
settings
)}
to reject
\`
${
JSON
.
stringify
(
input
)}
\`
.`
);
return
{
withMessage
:
(
message
:
string
)
=>
{
const
actual
=
this
.
validator
(
input
);
assert
.
ok
(
actual
);
assert
(
actual
!
.
indexOf
(
message
)
>
-
1
,
`Expected error of
${
JSON
.
stringify
(
this
.
settings
)}
on
\`
${
input
}
\`
to contain
${
message
}
. Got
${
this
.
validator
(
input
)}
.`
);
}
};
}
}
test
(
'
simple array
'
,
()
=>
{
{
const
arr
=
new
ArrayTester
({
type
:
'
array
'
,
items
:
{
type
:
'
string
'
}
});
arr
.
accepts
([]);
arr
.
accepts
([
'
foo
'
]);
arr
.
accepts
([
'
foo
'
,
'
bar
'
]);
}
});
test
(
'
min-max items array
'
,
()
=>
{
{
const
arr
=
new
ArrayTester
({
type
:
'
array
'
,
items
:
{
type
:
'
string
'
},
minItems
:
1
,
maxItems
:
2
});
arr
.
rejects
([]).
withMessage
(
'
Array must have at least 1 items
'
);
arr
.
accepts
([
'
a
'
]);
arr
.
accepts
([
'
a
'
,
'
a
'
]);
arr
.
rejects
([
'
a
'
,
'
a
'
,
'
a
'
]).
withMessage
(
'
Array must have less than 2 items
'
);
}
});
test
(
'
array of enums
'
,
()
=>
{
{
const
arr
=
new
ArrayTester
({
type
:
'
array
'
,
items
:
{
type
:
'
string
'
,
enum
:
[
'
a
'
,
'
b
'
]
}
});
arr
.
accepts
([
'
a
'
]);
arr
.
accepts
([
'
a
'
,
'
b
'
]);
arr
.
rejects
([
'
c
'
]).
withMessage
(
`Value 'c' is not one of`
);
arr
.
rejects
([
'
a
'
,
'
c
'
]).
withMessage
(
`Value 'c' is not one of`
);
arr
.
rejects
([
'
c
'
,
'
d
'
]).
withMessage
(
`Value 'c' is not one of`
);
arr
.
rejects
([
'
c
'
,
'
d
'
]).
withMessage
(
`Value 'd' is not one of`
);
}
});
test
(
'
min-max and enum
'
,
()
=>
{
const
arr
=
new
ArrayTester
({
type
:
'
array
'
,
items
:
{
type
:
'
string
'
,
enum
:
[
'
a
'
,
'
b
'
]
},
minItems
:
1
,
maxItems
:
2
});
arr
.
rejects
([
'
a
'
,
'
b
'
,
'
c
'
]).
withMessage
(
'
Array must have less than 2 items
'
);
arr
.
rejects
([
'
a
'
,
'
b
'
,
'
c
'
]).
withMessage
(
`Value 'c' is not one of`
);
});
test
(
'
pattern
'
,
()
=>
{
const
arr
=
new
ArrayTester
({
type
:
'
array
'
,
items
:
{
type
:
'
string
'
,
pattern
:
'
^(hello)*$
'
}
});
arr
.
accepts
([
'
hello
'
]);
arr
.
rejects
([
'
a
'
]).
withMessage
(
`Value 'a' must match regex`
);
});
test
(
'
pattern with error message
'
,
()
=>
{
const
arr
=
new
ArrayTester
({
type
:
'
array
'
,
items
:
{
type
:
'
string
'
,
pattern
:
'
^(hello)*$
'
,
patternErrorMessage
:
'
err: must be friendly
'
}
});
arr
.
rejects
([
'
a
'
]).
withMessage
(
`err: must be friendly`
);
});
});
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录