Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
xxadev
vscode
提交
3ba44a03
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,发现更多精彩内容 >>
提交
3ba44a03
编写于
4月 01, 2019
作者:
J
Johannes Rieken
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
minimize suggest dto, #71060
上级
83d8f282
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
118 addition
and
84 deletion
+118
-84
src/vs/editor/common/modes.ts
src/vs/editor/common/modes.ts
+5
-0
src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts
src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts
+42
-15
src/vs/workbench/api/common/extHost.protocol.ts
src/vs/workbench/api/common/extHost.protocol.ts
+25
-9
src/vs/workbench/api/node/extHostLanguageFeatures.ts
src/vs/workbench/api/node/extHostLanguageFeatures.ts
+46
-60
未找到文件。
src/vs/editor/common/modes.ts
浏览文件 @
3ba44a03
...
...
@@ -460,6 +460,11 @@ export interface CompletionItem {
* A command that should be run upon acceptance of this item.
*/
command
?:
Command
;
/**
* @internal
*/
[
key
:
string
]:
any
;
}
export
interface
CompletionList
{
...
...
src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts
浏览文件 @
3ba44a03
...
...
@@ -10,8 +10,8 @@ import * as modes from 'vs/editor/common/modes';
import
*
as
search
from
'
vs/workbench/contrib/search/common/search
'
;
import
{
CancellationToken
}
from
'
vs/base/common/cancellation
'
;
import
{
Position
as
EditorPosition
}
from
'
vs/editor/common/core/position
'
;
import
{
Range
as
EditorRange
}
from
'
vs/editor/common/core/range
'
;
import
{
ExtHostContext
,
MainThreadLanguageFeaturesShape
,
ExtHostLanguageFeaturesShape
,
MainContext
,
IExtHostContext
,
ISerializedLanguageConfiguration
,
ISerializedRegExp
,
ISerializedIndentationRule
,
ISerializedOnEnterRule
,
LocationDto
,
WorkspaceSymbolDto
,
CodeActionDto
,
reviveWorkspaceEditDto
,
ISerializedDocumentFilter
,
DefinitionLinkDto
,
ISerializedSignatureHelpProviderMetadata
,
CodeInsetDto
,
LinkDto
,
CallHierarchyDto
}
from
'
../common/extHost.protocol
'
;
import
{
Range
as
EditorRange
,
IRange
}
from
'
vs/editor/common/core/range
'
;
import
{
ExtHostContext
,
MainThreadLanguageFeaturesShape
,
ExtHostLanguageFeaturesShape
,
MainContext
,
IExtHostContext
,
ISerializedLanguageConfiguration
,
ISerializedRegExp
,
ISerializedIndentationRule
,
ISerializedOnEnterRule
,
LocationDto
,
WorkspaceSymbolDto
,
CodeActionDto
,
reviveWorkspaceEditDto
,
ISerializedDocumentFilter
,
DefinitionLinkDto
,
ISerializedSignatureHelpProviderMetadata
,
CodeInsetDto
,
LinkDto
,
CallHierarchyDto
,
SuggestDataDto
}
from
'
../common/extHost.protocol
'
;
import
{
LanguageConfigurationRegistry
}
from
'
vs/editor/common/modes/languageConfigurationRegistry
'
;
import
{
LanguageConfiguration
,
IndentationRule
,
OnEnterRule
}
from
'
vs/editor/common/modes/languageConfiguration
'
;
import
{
IModeService
}
from
'
vs/editor/common/services/modeService
'
;
...
...
@@ -22,6 +22,7 @@ import * as codeInset from 'vs/workbench/contrib/codeinset/common/codeInset';
import
{
ExtensionIdentifier
}
from
'
vs/platform/extensions/common/extensions
'
;
import
*
as
callh
from
'
vs/workbench/contrib/callHierarchy/common/callHierarchy
'
;
import
{
IHeapService
}
from
'
vs/workbench/services/heap/common/heap
'
;
import
{
mixin
}
from
'
vs/base/common/objects
'
;
@
extHostNamedCustomer
(
MainContext
.
MainThreadLanguageFeatures
)
export
class
MainThreadLanguageFeatures
implements
MainThreadLanguageFeaturesShape
{
...
...
@@ -359,8 +360,29 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- suggest
private
static
_inflateSuggestDto
(
defaultRange
:
IRange
,
data
:
SuggestDataDto
):
modes
.
CompletionItem
{
return
{
label
:
data
.
a
,
kind
:
data
.
b
,
detail
:
data
.
c
,
documentation
:
data
.
d
,
sortText
:
data
.
e
,
filterText
:
data
.
f
,
preselect
:
data
.
g
,
insertText
:
data
.
h
||
data
.
a
,
insertTextRules
:
data
.
i
,
range
:
data
.
j
||
defaultRange
,
commitCharacters
:
data
.
k
,
additionalTextEdits
:
data
.
l
,
command
:
data
.
m
,
// not-standard
_id
:
data
.
x
,
_pid
:
data
.
y
};
}
$registerSuggestSupport
(
handle
:
number
,
selector
:
ISerializedDocumentFilter
[],
triggerCharacters
:
string
[],
supportsResolveDetails
:
boolean
):
void
{
this
.
_registrations
[
handle
]
=
modes
.
CompletionProviderRegistry
.
register
(
selector
,
<
modes
.
CompletionItemProvider
>
{
const
provider
:
modes
.
CompletionItemProvider
=
{
triggerCharacters
,
provideCompletionItems
:
(
model
:
ITextModel
,
position
:
EditorPosition
,
context
:
modes
.
CompletionContext
,
token
:
CancellationToken
):
Promise
<
modes
.
CompletionList
|
undefined
>
=>
{
return
this
.
_proxy
.
$provideCompletionItems
(
handle
,
model
.
uri
,
position
,
context
,
token
).
then
(
result
=>
{
...
...
@@ -368,20 +390,25 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
return
result
;
}
return
{
suggestions
:
result
.
suggestions
,
incomplete
:
result
.
incomplete
,
dispose
:
()
=>
{
if
(
typeof
result
.
_id
===
'
number
'
)
{
this
.
_proxy
.
$releaseCompletionItems
(
handle
,
result
.
_id
);
}
}
suggestions
:
result
.
b
.
map
(
d
=>
MainThreadLanguageFeatures
.
_inflateSuggestDto
(
result
.
a
,
d
)),
incomplete
:
result
.
c
,
dispose
:
()
=>
this
.
_proxy
.
$releaseCompletionItems
(
handle
,
result
.
x
)
};
});
},
resolveCompletionItem
:
supportsResolveDetails
?
(
model
,
position
,
suggestion
,
token
)
=>
this
.
_proxy
.
$resolveCompletionItem
(
handle
,
model
.
uri
,
position
,
suggestion
,
token
)
:
undefined
});
}
};
if
(
supportsResolveDetails
)
{
provider
.
resolveCompletionItem
=
(
model
,
position
,
suggestion
,
token
)
=>
{
return
this
.
_proxy
.
$resolveCompletionItem
(
handle
,
model
.
uri
,
position
,
suggestion
.
_id
,
suggestion
.
_pid
,
token
).
then
(
result
=>
{
if
(
!
result
)
{
return
suggestion
;
}
let
newSuggestion
=
MainThreadLanguageFeatures
.
_inflateSuggestDto
(
suggestion
.
range
,
result
);
return
mixin
(
suggestion
,
newSuggestion
,
true
);
});
};
}
this
.
_registrations
[
handle
]
=
modes
.
CompletionProviderRegistry
.
register
(
selector
,
provider
);
}
// --- parameter hints
...
...
src/vs/workbench/api/common/extHost.protocol.ts
浏览文件 @
3ba44a03
...
...
@@ -854,14 +854,30 @@ export class IdObject {
}
}
export
interface
SuggestionDto
extends
modes
.
CompletionItem
{
_id
:
number
;
_parentId
:
number
;
}
export
interface
SuggestResultDto
extends
IdObject
{
suggestions
:
SuggestionDto
[];
incomplete
?:
boolean
;
export
interface
SuggestDataDto
{
a
/* label */
:
string
;
b
/* kind */
:
modes
.
CompletionItemKind
;
c
/* detail */
?:
string
;
d
/* documentation */
?:
string
|
IMarkdownString
;
e
/* sortText */
?:
string
;
f
/* filterText */
?:
string
;
g
/* preselect */
?:
boolean
;
h
/* insertText */
?:
string
;
i
/* insertTextRules */
?:
modes
.
CompletionItemInsertTextRule
;
j
/* range */
?:
IRange
;
k
/* commitCharacters */
?:
string
[];
l
/* additionalTextEdits */
?:
ISingleEditOperation
[];
m
/* command */
?:
modes
.
Command
;
// not-standard
x
:
number
;
y
:
number
;
}
export
interface
SuggestResultDto
{
x
:
number
;
a
:
IRange
;
b
:
SuggestDataDto
[];
c
?:
boolean
;
}
export
interface
LocationDto
{
...
...
@@ -982,7 +998,7 @@ export interface ExtHostLanguageFeaturesShape {
$provideRenameEdits
(
handle
:
number
,
resource
:
UriComponents
,
position
:
IPosition
,
newName
:
string
,
token
:
CancellationToken
):
Promise
<
WorkspaceEditDto
|
undefined
>
;
$resolveRenameLocation
(
handle
:
number
,
resource
:
UriComponents
,
position
:
IPosition
,
token
:
CancellationToken
):
Promise
<
modes
.
RenameLocation
|
undefined
>
;
$provideCompletionItems
(
handle
:
number
,
resource
:
UriComponents
,
position
:
IPosition
,
context
:
modes
.
CompletionContext
,
token
:
CancellationToken
):
Promise
<
SuggestResultDto
|
undefined
>
;
$resolveCompletionItem
(
handle
:
number
,
resource
:
UriComponents
,
position
:
IPosition
,
suggestion
:
modes
.
CompletionItem
,
token
:
CancellationToken
):
Promise
<
modes
.
CompletionItem
>
;
$resolveCompletionItem
(
handle
:
number
,
resource
:
UriComponents
,
position
:
IPosition
,
id
:
number
,
pid
:
number
,
token
:
CancellationToken
):
Promise
<
SuggestDataDto
|
undefined
>
;
$releaseCompletionItems
(
handle
:
number
,
id
:
number
):
void
;
$provideSignatureHelp
(
handle
:
number
,
resource
:
UriComponents
,
position
:
IPosition
,
context
:
modes
.
SignatureHelpContext
,
token
:
CancellationToken
):
Promise
<
modes
.
SignatureHelp
|
undefined
>
;
$provideDocumentLinks
(
handle
:
number
,
resource
:
UriComponents
,
token
:
CancellationToken
):
Promise
<
LinkDto
[]
|
undefined
>
;
...
...
src/vs/workbench/api/node/extHostLanguageFeatures.ts
浏览文件 @
3ba44a03
...
...
@@ -15,7 +15,7 @@ import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
import
{
ExtHostCommands
,
CommandsConverter
}
from
'
vs/workbench/api/node/extHostCommands
'
;
import
{
ExtHostDiagnostics
}
from
'
vs/workbench/api/node/extHostDiagnostics
'
;
import
{
asPromise
}
from
'
vs/base/common/async
'
;
import
{
MainContext
,
MainThreadLanguageFeaturesShape
,
ExtHostLanguageFeaturesShape
,
ObjectIdentifier
,
IRawColorInfo
,
IMainContext
,
IdObject
,
ISerializedRegExp
,
ISerializedIndentationRule
,
ISerializedOnEnterRule
,
ISerializedLanguageConfiguration
,
WorkspaceSymbolDto
,
SuggestResultDto
,
WorkspaceSymbolsDto
,
SuggestionDto
,
CodeActionDto
,
ISerializedDocumentFilter
,
WorkspaceEditDto
,
ISerializedSignatureHelpProviderMetadata
,
LinkDto
,
CodeLensDto
,
MainThreadWebviewsShape
,
CodeInset
Dto
}
from
'
../common/extHost.protocol
'
;
import
{
MainContext
,
MainThreadLanguageFeaturesShape
,
ExtHostLanguageFeaturesShape
,
ObjectIdentifier
,
IRawColorInfo
,
IMainContext
,
IdObject
,
ISerializedRegExp
,
ISerializedIndentationRule
,
ISerializedOnEnterRule
,
ISerializedLanguageConfiguration
,
WorkspaceSymbolDto
,
SuggestResultDto
,
WorkspaceSymbolsDto
,
CodeActionDto
,
ISerializedDocumentFilter
,
WorkspaceEditDto
,
ISerializedSignatureHelpProviderMetadata
,
LinkDto
,
CodeLensDto
,
MainThreadWebviewsShape
,
CodeInsetDto
,
SuggestData
Dto
}
from
'
../common/extHost.protocol
'
;
import
{
regExpLeadsToEndlessLoop
,
regExpFlags
}
from
'
vs/base/common/strings
'
;
import
{
IPosition
}
from
'
vs/editor/common/core/position
'
;
import
{
IRange
,
Range
as
EditorRange
}
from
'
vs/editor/common/core/range
'
;
...
...
@@ -637,15 +637,18 @@ class SuggestAdapter {
const
doc
=
this
.
_documents
.
getDocument
(
resource
);
const
pos
=
typeConvert
.
Position
.
to
(
position
);
return
asPromise
<
vscode
.
CompletionItem
[]
|
vscode
.
CompletionList
|
null
|
undefined
>
(
()
=>
this
.
_provider
.
provideCompletionItems
(
doc
,
pos
,
token
,
typeConvert
.
CompletionContext
.
to
(
context
))
).
then
(
value
=>
{
return
asPromise
(()
=>
this
.
_provider
.
provideCompletionItems
(
doc
,
pos
,
token
,
typeConvert
.
CompletionContext
.
to
(
context
))).
then
(
value
=>
{
const
_id
=
this
.
_idPool
++
;
// the default text edit range
const
wordRangeBeforePos
=
(
doc
.
getWordRangeAtPosition
(
pos
)
as
Range
||
new
Range
(
pos
,
pos
))
.
with
({
end
:
pos
});
const
result
:
SuggestResultDto
=
{
_id
,
suggestions
:
[],
x
:
_id
,
b
:
[],
a
:
typeConvert
.
Range
.
from
(
wordRangeBeforePos
),
};
let
list
:
CompletionList
;
...
...
@@ -658,54 +661,45 @@ class SuggestAdapter {
}
else
{
list
=
value
;
result
.
incomplete
=
list
.
isIncomplete
;
result
.
c
=
list
.
isIncomplete
;
}
// the default text edit range
const
wordRangeBeforePos
=
(
doc
.
getWordRangeAtPosition
(
pos
)
as
Range
||
new
Range
(
pos
,
pos
))
.
with
({
end
:
pos
});
for
(
let
i
=
0
;
i
<
list
.
items
.
length
;
i
++
)
{
const
suggestion
=
this
.
_convertCompletionItem
(
list
.
items
[
i
],
pos
,
wordRangeBeforeP
os
,
i
,
_id
);
const
suggestion
=
this
.
_convertCompletionItem
2
(
list
.
items
[
i
],
p
os
,
i
,
_id
);
// check for bad completion item
// for the converter did warn
if
(
suggestion
)
{
result
.
suggestions
.
push
(
suggestion
);
result
.
b
.
push
(
suggestion
);
}
}
this
.
_cache
.
set
(
_id
,
list
.
items
);
if
(
SuggestAdapter
.
supportsResolving
(
this
.
_provider
))
{
this
.
_cache
.
set
(
_id
,
list
.
items
);
}
return
result
;
});
}
resolveCompletionItem
(
resource
:
URI
,
position
:
IPosition
,
suggestion
:
modes
.
CompletionItem
,
token
:
CancellationToken
):
Promise
<
modes
.
CompletionItem
>
{
resolveCompletionItem
(
_resource
:
URI
,
position
:
IPosition
,
id
:
number
,
pid
:
number
,
token
:
CancellationToken
):
Promise
<
SuggestDataDto
|
undefined
>
{
if
(
typeof
this
.
_provider
.
resolveCompletionItem
!==
'
function
'
)
{
return
Promise
.
resolve
(
suggestion
);
return
Promise
.
resolve
(
undefined
);
}
const
{
_parentId
,
_id
}
=
(
<
SuggestionDto
>
suggestion
);
const
item
=
this
.
_cache
.
has
(
_parentId
)
?
this
.
_cache
.
get
(
_parentId
)
!
[
_id
]
:
undefined
;
const
item
=
this
.
_cache
.
has
(
pid
)
?
this
.
_cache
.
get
(
pid
)
!
[
id
]
:
undefined
;
if
(
!
item
)
{
return
Promise
.
resolve
(
suggestion
);
return
Promise
.
resolve
(
undefined
);
}
return
asPromise
(()
=>
this
.
_provider
.
resolveCompletionItem
!
(
item
,
token
)).
then
(
resolvedItem
=>
{
if
(
!
resolvedItem
)
{
return
suggestion
;
return
undefined
;
}
const
doc
=
this
.
_documents
.
getDocument
(
resource
);
const
pos
=
typeConvert
.
Position
.
to
(
position
);
const
wordRangeBeforePos
=
(
doc
.
getWordRangeAtPosition
(
pos
)
as
Range
||
new
Range
(
pos
,
pos
)).
with
({
end
:
pos
});
const
newSuggestion
=
this
.
_convertCompletionItem
(
resolvedItem
,
pos
,
wordRangeBeforePos
,
_id
,
_parentId
);
if
(
newSuggestion
)
{
mixin
(
suggestion
,
newSuggestion
,
true
);
}
return
suggestion
;
return
this
.
_convertCompletionItem2
(
resolvedItem
,
pos
,
id
,
pid
);
});
}
...
...
@@ -713,60 +707,52 @@ class SuggestAdapter {
this
.
_cache
.
delete
(
id
);
}
private
_convertCompletionItem
(
item
:
vscode
.
CompletionItem
,
position
:
vscode
.
Position
,
defaultRange
:
vscode
.
Range
,
_id
:
number
,
_parentId
:
number
):
Suggestion
Dto
|
undefined
{
private
_convertCompletionItem
2
(
item
:
vscode
.
CompletionItem
,
position
:
vscode
.
Position
,
id
:
number
,
pid
:
number
):
SuggestData
Dto
|
undefined
{
if
(
typeof
item
.
label
!==
'
string
'
||
item
.
label
.
length
===
0
)
{
console
.
warn
(
'
INVALID text edit -> must have at least a label
'
);
return
undefined
;
}
const
result
:
SuggestionDto
=
{
//
_id
,
_parentId
,
const
result
:
SuggestDataDto
=
{
//
label
:
item
.
label
,
kind
:
typeConvert
.
CompletionItemKind
.
from
(
item
.
kind
),
detail
:
item
.
detail
,
documentation
:
typeof
item
.
documentation
===
'
undefined
'
?
undefined
:
typeConvert
.
MarkdownString
.
fromStrict
(
item
.
documentation
),
filterText
:
item
.
filterText
,
sortText
:
item
.
sortText
,
preselect
:
item
.
preselect
,
x
:
id
,
y
:
pid
,
//
range
:
undefined
!
,
// populated below
insertText
:
undefined
!
,
// populated below
insertTextRules
:
item
.
keepWhitespace
?
modes
.
CompletionItemInsertTextRule
.
KeepWhitespace
:
0
,
additionalTextEdits
:
item
.
additionalTextEdits
&&
item
.
additionalTextEdits
.
map
(
typeConvert
.
TextEdit
.
from
),
command
:
this
.
_commands
.
toInternal
(
item
.
command
),
commitCharacters
:
item
.
commitCharacters
a
:
item
.
label
,
b
:
typeConvert
.
CompletionItemKind
.
from
(
item
.
kind
),
c
:
item
.
detail
,
d
:
typeof
item
.
documentation
===
'
undefined
'
?
undefined
:
typeConvert
.
MarkdownString
.
fromStrict
(
item
.
documentation
),
e
:
item
.
sortText
,
f
:
item
.
filterText
,
g
:
item
.
preselect
,
i
:
item
.
keepWhitespace
?
modes
.
CompletionItemInsertTextRule
.
KeepWhitespace
:
0
,
k
:
item
.
commitCharacters
,
l
:
item
.
additionalTextEdits
&&
item
.
additionalTextEdits
.
map
(
typeConvert
.
TextEdit
.
from
),
m
:
this
.
_commands
.
toInternal
(
item
.
command
),
};
// 'insertText'-logic
if
(
item
.
textEdit
)
{
result
.
insertText
=
item
.
textEdit
.
newText
;
result
.
h
=
item
.
textEdit
.
newText
;
}
else
if
(
typeof
item
.
insertText
===
'
string
'
)
{
result
.
insertText
=
item
.
insertText
;
result
.
h
=
item
.
insertText
;
}
else
if
(
item
.
insertText
instanceof
SnippetString
)
{
result
.
insertText
=
item
.
insertText
.
value
;
result
.
insertTextRules
!
|=
modes
.
CompletionItemInsertTextRule
.
InsertAsSnippet
;
}
else
{
result
.
insertText
=
item
.
label
;
result
.
h
=
item
.
insertText
.
value
;
result
.
i
!
|=
modes
.
CompletionItemInsertTextRule
.
InsertAsSnippet
;
}
// 'overwrite[Before|After]'-logic
let
range
:
vscode
.
Range
;
let
range
:
vscode
.
Range
|
undefined
;
if
(
item
.
textEdit
)
{
range
=
item
.
textEdit
.
range
;
}
else
if
(
item
.
range
)
{
range
=
item
.
range
;
}
else
{
range
=
defaultRange
;
}
result
.
range
=
typeConvert
.
Range
.
from
(
range
);
result
.
j
=
typeConvert
.
Range
.
from
(
range
);
if
(
!
range
.
isSingleLine
||
range
.
start
.
line
!==
position
.
line
)
{
if
(
range
&&
(
!
range
.
isSingleLine
||
range
.
start
.
line
!==
position
.
line
)
)
{
console
.
warn
(
'
INVALID text edit -> must be single line and on the same line
'
);
return
undefined
;
}
...
...
@@ -1387,8 +1373,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return
this
.
_withAdapter
(
handle
,
SuggestAdapter
,
adapter
=>
adapter
.
provideCompletionItems
(
URI
.
revive
(
resource
),
position
,
context
,
token
),
undefined
);
}
$resolveCompletionItem
(
handle
:
number
,
resource
:
UriComponents
,
position
:
IPosition
,
suggestion
:
modes
.
CompletionItem
,
token
:
CancellationToken
):
Promise
<
modes
.
CompletionItem
>
{
return
this
.
_withAdapter
(
handle
,
SuggestAdapter
,
adapter
=>
adapter
.
resolveCompletionItem
(
URI
.
revive
(
resource
),
position
,
suggestion
,
token
),
suggestion
);
$resolveCompletionItem
(
handle
:
number
,
resource
:
UriComponents
,
position
:
IPosition
,
id
:
number
,
pid
:
number
,
token
:
CancellationToken
):
Promise
<
SuggestDataDto
|
undefined
>
{
return
this
.
_withAdapter
(
handle
,
SuggestAdapter
,
adapter
=>
adapter
.
resolveCompletionItem
(
URI
.
revive
(
resource
),
position
,
id
,
pid
,
token
),
undefined
);
}
$releaseCompletionItems
(
handle
:
number
,
id
:
number
):
void
{
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录