Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
lwm1986
roslyn
提交
d20421ec
R
roslyn
项目概览
lwm1986
/
roslyn
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
roslyn
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
d20421ec
编写于
5月 29, 2016
作者:
L
lorcanmooney
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Add namespace-name suggestions for C#
上级
7e4f3149
变更
10
隐藏空白更改
内联
并排
Showing
10 changed file
with
234 addition
and
20 deletion
+234
-20
src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SuggestionModeCompletionProviderTests.cs
...pletionProviders/SuggestionModeCompletionProviderTests.cs
+16
-0
src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs
...tion/CompletionProviders/SymbolCompletionProviderTests.cs
+152
-6
src/Features/CSharp/Portable/Completion/CompletionProviders/SnippetCompletionProvider.cs
...mpletion/CompletionProviders/SnippetCompletionProvider.cs
+4
-3
src/Features/CSharp/Portable/Completion/SuggestionMode/CSharpSuggestionModeCompletionProvider.cs
.../SuggestionMode/CSharpSuggestionModeCompletionProvider.cs
+4
-0
src/Workspaces/CSharp/Portable/Extensions/SyntaxTreeExtensions.cs
...spaces/CSharp/Portable/Extensions/SyntaxTreeExtensions.cs
+3
-3
src/Workspaces/CSharp/Portable/LanguageServices/CSharpSemanticFactsService.cs
...p/Portable/LanguageServices/CSharpSemanticFactsService.cs
+5
-0
src/Workspaces/CSharp/Portable/Recommendations/CSharpRecommendationService.cs
...p/Portable/Recommendations/CSharpRecommendationService.cs
+31
-7
src/Workspaces/Core/Portable/LanguageServices/SemanticsFactsService/ISemanticFactsService.cs
...geServices/SemanticsFactsService/ISemanticFactsService.cs
+1
-0
src/Workspaces/Core/Portable/Shared/Extensions/ContextQuery/AbstractSyntaxContext.cs
...e/Shared/Extensions/ContextQuery/AbstractSyntaxContext.cs
+11
-1
src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSemanticFactsService.vb
...table/LanguageServices/VisualBasicSemanticFactsService.vb
+7
-0
未找到文件。
src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SuggestionModeCompletionProviderTests.cs
浏览文件 @
d20421ec
...
...
@@ -590,6 +590,22 @@ void foo()
await
VerifyBuilderAsync
(
markup
);
}
[
WorkItem
(
7213
,
"https://github.com/dotnet/roslyn/issues/7213"
)]
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
Completion
)]
public
async
Task
NamespaceDeclaration_Unqualified
()
{
var
markup
=
@"namespace $$"
;
await
VerifyBuilderAsync
(
markup
);
}
[
WorkItem
(
7213
,
"https://github.com/dotnet/roslyn/issues/7213"
)]
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
Completion
)]
public
async
Task
NamespaceDeclaration_Qualified
()
{
var
markup
=
@"namespace A.$$"
;
await
VerifyBuilderAsync
(
markup
);
}
private
async
Task
VerifyNotBuilderAsync
(
string
markup
)
{
await
VerifyWorkerAsync
(
markup
,
isBuilder
:
false
);
...
...
src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs
浏览文件 @
d20421ec
...
...
@@ -370,20 +370,166 @@ public async Task MethodParamAttribute()
await
VerifyItemExistsAsync
(
AddUsingDirectives
(
"using System;"
,
content
),
@"System"
);
}
[
WorkItem
(
7213
,
"https://github.com/dotnet/roslyn/issues/7213"
)]
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
Completion
)]
public
async
Task
NamespaceName
1
()
public
async
Task
NamespaceName
_Unqualified_TopLevelNoPeers
()
{
await
VerifyItemIsAbsentAsync
(
AddUsingDirectives
(
"using System;"
,
@"namespace $$"
),
@"String"
);
await
VerifyItemIsAbsentAsync
(
AddUsingDirectives
(
"using System;"
,
@"namespace $$"
),
@"System"
);
var
source
=
@"using System;
namespace $$"
;
await
VerifyItemExistsAsync
(
source
,
"System"
);
await
VerifyItemIsAbsentAsync
(
source
,
"String"
);
}
[
WorkItem
(
7213
,
"https://github.com/dotnet/roslyn/issues/7213"
)]
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
Completion
)]
public
async
Task
NamespaceName_Unqualified_TopLevelWithPeer
()
{
var
source
=
@"
namespace A { }
namespace $$"
;
await
VerifyItemExistsAsync
(
source
,
"A"
);
}
[
WorkItem
(
7213
,
"https://github.com/dotnet/roslyn/issues/7213"
)]
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
Completion
)]
public
async
Task
NamespaceName
2
()
public
async
Task
NamespaceName
_Unqualified_NestedWithNoPeers
()
{
await
VerifyItemIsAbsentAsync
(
@"namespace $$"
,
@"String"
);
await
VerifyItemIsAbsentAsync
(
@"namespace $$"
,
@"System"
);
var
source
=
@"
namespace A
{
namespace $$
}"
;
await
VerifyNoItemsExistAsync
(
source
);
}
[
WorkItem
(
7213
,
"https://github.com/dotnet/roslyn/issues/7213"
)]
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
Completion
)]
public
async
Task
NamespaceName_Unqualified_NestedWithPeer
()
{
var
source
=
@"
namespace A
{
namespace B { }
namespace $$
}"
;
await
VerifyItemIsAbsentAsync
(
source
,
"A"
);
await
VerifyItemExistsAsync
(
source
,
"B"
);
}
[
WorkItem
(
7213
,
"https://github.com/dotnet/roslyn/issues/7213"
)]
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
Completion
)]
public
async
Task
NamespaceName_Unqualified_ExcludesCurrentDeclaration
()
{
var
source
=
@"namespace N$$S"
;
await
VerifyItemIsAbsentAsync
(
source
,
"NS"
);
}
// [WorkItem(7213, "https://github.com/dotnet/roslyn/issues/7213")]
// [Fact, Trait(Traits.Feature, Traits.Features.Completion)]
// public async Task NamespaceName_Unqualified_IncompleteDeclaration()
// {
// var source = @"
//namespace A.B.C0 { }
//
//namespace A
//{
// namespace B
// {
// namespace C$$
//
// namespace C1.X { }
// }
//
// namespace B.C2.Y { }
//}
//
//namespace A.B.C3.Z { }";
//
// await VerifyItemIsAbsentAsync(source, "C0");
// await VerifyItemExistsAsync(source, "C1");
// await VerifyItemExistsAsync(source, "C2");
// await VerifyItemExistsAsync(source, "C3");
// await VerifyItemIsAbsentAsync(source, "X");
// await VerifyItemIsAbsentAsync(source, "Y");
// await VerifyItemIsAbsentAsync(source, "Z");
// }
[
WorkItem
(
7213
,
"https://github.com/dotnet/roslyn/issues/7213"
)]
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
Completion
)]
public
async
Task
NamespaceName_Qualified_NoPeers
()
{
var
source
=
@"namespace A.$$"
;
await
VerifyNoItemsExistAsync
(
source
);
}
[
WorkItem
(
7213
,
"https://github.com/dotnet/roslyn/issues/7213"
)]
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
Completion
)]
public
async
Task
NamespaceName_Qualified_TopLevelWithPeer
()
{
var
source
=
@"
namespace A.B { }
namespace A.$$"
;
await
VerifyItemExistsAsync
(
source
,
"B"
);
}
[
WorkItem
(
7213
,
"https://github.com/dotnet/roslyn/issues/7213"
)]
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
Completion
)]
public
async
Task
NamespaceName_Qualified_NestedWithPeer
()
{
var
source
=
@"
namespace A
{
namespace B.C { }
namespace B.$$
}"
;
await
VerifyItemIsAbsentAsync
(
source
,
"A"
);
await
VerifyItemIsAbsentAsync
(
source
,
"B"
);
await
VerifyItemExistsAsync
(
source
,
"C"
);
}
// [WorkItem(7213, "https://github.com/dotnet/roslyn/issues/7213")]
// [Fact, Trait(Traits.Feature, Traits.Features.Completion)]
// public async Task NamespaceName_Qualified_IncompleteDeclaration()
// {
// var source = @"
//namespace A.B.C.D0 { }
//
//namespace A
//{
// namespace B
// {
// namespace C.D$$
//
// namespace C.D1.X { }
// }
//
// namespace B.C.D2.Y { }
//}
//
//namespace A.B.C.D3.Z { }";
//
// await VerifyItemIsAbsentAsync(source, "D0");
// await VerifyItemExistsAsync(source, "D1");
// await VerifyItemExistsAsync(source, "D2");
// await VerifyItemExistsAsync(source, "D3");
// await VerifyItemIsAbsentAsync(source, "X");
// await VerifyItemIsAbsentAsync(source, "Y");
// await VerifyItemIsAbsentAsync(source, "Z");
// }
[
Fact
,
Trait
(
Traits
.
Feature
,
Traits
.
Features
.
Completion
)]
public
async
Task
UnderNamespace
()
{
...
...
src/Features/CSharp/Portable/Completion/CompletionProviders/SnippetCompletionProvider.cs
浏览文件 @
d20421ec
...
...
@@ -110,7 +110,7 @@ private async Task<IEnumerable<CompletionItem>> GetSnippetsForDocumentAsync(Docu
return
SpecializedCollections
.
EmptyEnumerable
<
CompletionItem
>();
}
return
await
GetSnippetCompletionItemsAsync
(
workspace
,
semanticModel
,
position
,
itemSpan
,
isPreProcessorContext
:
true
,
cancellationToken
:
cancellationToken
).
ConfigureAwait
(
false
);
return
await
GetSnippetCompletionItemsAsync
(
workspace
,
semanticModel
,
itemSpan
,
isPreProcessorContext
:
true
,
cancellationToken
:
cancellationToken
).
ConfigureAwait
(
false
);
}
if
(
semanticFacts
.
IsGlobalStatementContext
(
semanticModel
,
position
,
cancellationToken
)
||
...
...
@@ -119,16 +119,17 @@ private async Task<IEnumerable<CompletionItem>> GetSnippetsForDocumentAsync(Docu
semanticFacts
.
IsTypeContext
(
semanticModel
,
position
,
cancellationToken
)
||
semanticFacts
.
IsTypeDeclarationContext
(
semanticModel
,
position
,
cancellationToken
)
||
semanticFacts
.
IsNamespaceContext
(
semanticModel
,
position
,
cancellationToken
)
||
semanticFacts
.
IsNamespaceDeclarationNameContext
(
semanticModel
,
position
,
cancellationToken
)
||
semanticFacts
.
IsMemberDeclarationContext
(
semanticModel
,
position
,
cancellationToken
)
||
semanticFacts
.
IsLabelContext
(
semanticModel
,
position
,
cancellationToken
))
{
return
await
GetSnippetCompletionItemsAsync
(
workspace
,
semanticModel
,
position
,
itemSpan
,
isPreProcessorContext
:
false
,
cancellationToken
:
cancellationToken
).
ConfigureAwait
(
false
);
return
await
GetSnippetCompletionItemsAsync
(
workspace
,
semanticModel
,
itemSpan
,
isPreProcessorContext
:
false
,
cancellationToken
:
cancellationToken
).
ConfigureAwait
(
false
);
}
return
SpecializedCollections
.
EmptyEnumerable
<
CompletionItem
>();
}
private
async
Task
<
IEnumerable
<
CompletionItem
>>
GetSnippetCompletionItemsAsync
(
Workspace
workspace
,
SemanticModel
semanticModel
,
int
position
,
TextSpan
itemSpan
,
bool
isPreProcessorContext
,
CancellationToken
cancellationToken
)
private
async
Task
<
IEnumerable
<
CompletionItem
>>
GetSnippetCompletionItemsAsync
(
Workspace
workspace
,
SemanticModel
semanticModel
,
TextSpan
itemSpan
,
bool
isPreProcessorContext
,
CancellationToken
cancellationToken
)
{
var
service
=
_snippetInfoService
??
workspace
.
Services
.
GetLanguageServices
(
semanticModel
.
Language
).
GetService
<
ISnippetInfoService
>();
if
(
service
==
null
)
...
...
src/Features/CSharp/Portable/Completion/SuggestionMode/CSharpSuggestionModeCompletionProvider.cs
浏览文件 @
d20421ec
...
...
@@ -59,6 +59,10 @@ protected override async Task<CompletionItem> GetSuggestionModeItemAsync(Documen
{
return
CreateSuggestionModeItem
(
CSharpFeaturesResources
.
RangeVariable
,
itemSpan
,
CSharpFeaturesResources
.
AutoselectDisabledDueToPotentialRangeVariableDecl
);
}
else
if
(
tree
.
IsNamespaceDeclarationNameContext
(
position
,
cancellationToken
))
{
return
CreateEmptySuggestionModeItem
(
itemSpan
);
}
}
return
null
;
...
...
src/Workspaces/CSharp/Portable/Extensions/SyntaxTreeExtensions.cs
浏览文件 @
d20421ec
...
...
@@ -114,13 +114,13 @@ private static bool BaseTypeDeclarationContainsPosition(BaseTypeDeclarationSynta
public
static
bool
IsNamespaceDeclarationNameContext
(
this
SyntaxTree
syntaxTree
,
int
position
,
CancellationToken
cancellationToken
)
{
var
token
=
syntaxTree
.
FindTokenOnLeftOfPosition
(
position
,
cancellationToken
);
var
namespace
Name
=
token
.
GetAncestor
<
NamespaceDeclarationSyntax
>();
if
(
namespace
Name
==
null
)
var
namespace
Declaration
=
token
.
GetAncestor
<
NamespaceDeclarationSyntax
>();
if
(
namespace
Declaration
==
null
)
{
return
false
;
}
return
namespace
Name
.
Name
.
Span
.
IntersectsWith
(
position
)
;
return
namespace
Declaration
.
Name
.
Span
.
IntersectsWith
(
position
)
||
token
==
namespaceDeclaration
.
NamespaceKeyword
;
}
public
static
bool
IsRightOfDotOrArrowOrColonColon
(
this
SyntaxTree
syntaxTree
,
int
position
,
CancellationToken
cancellationToken
)
...
...
src/Workspaces/CSharp/Portable/LanguageServices/CSharpSemanticFactsService.cs
浏览文件 @
d20421ec
...
...
@@ -60,6 +60,11 @@ public bool IsNamespaceContext(SemanticModel semanticModel, int position, Cancel
return
semanticModel
.
SyntaxTree
.
IsNamespaceContext
(
position
,
cancellationToken
,
semanticModel
);
}
public
bool
IsNamespaceDeclarationNameContext
(
SemanticModel
semanticModel
,
int
position
,
CancellationToken
cancellationToken
)
{
return
semanticModel
.
SyntaxTree
.
IsNamespaceDeclarationNameContext
(
position
,
cancellationToken
);
}
public
bool
IsTypeDeclarationContext
(
SemanticModel
semanticModel
,
int
position
,
CancellationToken
cancellationToken
)
{
return
semanticModel
.
SyntaxTree
.
IsTypeDeclarationContext
(
...
...
src/Workspaces/CSharp/Portable/Recommendations/CSharpRecommendationService.cs
浏览文件 @
d20421ec
...
...
@@ -7,6 +7,7 @@
using
System.Linq
;
using
System.Threading
;
using
System.Threading.Tasks
;
using
Microsoft.CodeAnalysis
;
using
Microsoft.CodeAnalysis.CSharp.Extensions
;
using
Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery
;
using
Microsoft.CodeAnalysis.CSharp.Symbols
;
...
...
@@ -48,13 +49,6 @@ internal class CSharpRecommendationService : AbstractRecommendationService
return
SpecializedCollections
.
EmptyEnumerable
<
ISymbol
>();
}
// TODO: don't show completion set at namespace name part to match Dev10 behavior
// if we want to provide new feature that shows all existing namespaces later, remove this
if
(
context
.
IsNamespaceDeclarationNameContext
)
{
return
SpecializedCollections
.
EmptyEnumerable
<
ISymbol
>();
}
if
(
context
.
IsRightOfNameSeparator
)
{
return
GetSymbolsOffOfContainer
(
context
,
cancellationToken
);
...
...
@@ -100,6 +94,10 @@ internal class CSharpRecommendationService : AbstractRecommendationService
{
return
SpecializedCollections
.
SingletonEnumerable
(
context
.
SemanticModel
.
GetDeclaredSymbol
(
context
.
ContainingTypeOrEnumDeclaration
,
cancellationToken
));
}
else
if
(
context
.
IsNamespaceDeclarationNameContext
)
{
return
GetSymbolsForNamespaceDeclarationNameContext
(
context
,
cancellationToken
);
}
return
SpecializedCollections
.
EmptyEnumerable
<
ISymbol
>();
}
...
...
@@ -238,6 +236,22 @@ internal class CSharpRecommendationService : AbstractRecommendationService
return
symbols
;
}
private
static
IEnumerable
<
ISymbol
>
GetSymbolsForNamespaceDeclarationNameContext
(
CSharpSyntaxContext
context
,
CancellationToken
cancellationToken
)
{
var
namespaceDeclaration
=
context
.
TargetToken
.
GetAncestor
<
NamespaceDeclarationSyntax
>();
var
declaredNamespaceSymbol
=
context
.
SemanticModel
.
GetDeclaredSymbol
(
namespaceDeclaration
);
var
containingNamespaceSymbol
=
context
.
SemanticModel
.
Compilation
.
GetCompilationNamespace
(
declaredNamespaceSymbol
.
ContainingNamespace
);
var
symbols
=
context
.
SemanticModel
.
LookupNamespacesAndTypes
(
context
.
LeftToken
.
SpanStart
,
containingNamespaceSymbol
)
.
Where
(
symbol
=>
IsNonIntersectingNamespace
(
symbol
,
context
));
return
symbols
;
}
private
static
IEnumerable
<
ISymbol
>
GetSymbolsForExpressionOrStatementContext
(
CSharpSyntaxContext
context
,
bool
filterOutOfScopeLocals
,
...
...
@@ -321,6 +335,11 @@ internal class CSharpRecommendationService : AbstractRecommendationService
position
:
name
.
SpanStart
,
container
:
symbol
);
if
(
context
.
IsNamespaceDeclarationNameContext
)
{
return
symbols
.
Where
(
s
=>
IsNonIntersectingNamespace
(
s
,
context
));
}
// Filter the types when in a using directive, but not an alias.
//
// Cases:
...
...
@@ -350,6 +369,11 @@ internal class CSharpRecommendationService : AbstractRecommendationService
return
SpecializedCollections
.
EmptyEnumerable
<
ISymbol
>();
}
private
static
bool
IsNonIntersectingNamespace
(
ISymbol
symbol
,
CSharpSyntaxContext
context
)
{
return
symbol
.
IsNamespace
()
&&
symbol
.
Locations
.
Any
(
location
=>
!
context
.
IntersectsWith
(
location
));
}
private
static
IEnumerable
<
ISymbol
>
GetSymbolsOffOfExpression
(
CSharpSyntaxContext
context
,
ExpressionSyntax
originalExpression
,
...
...
src/Workspaces/Core/Portable/LanguageServices/SemanticsFactsService/ISemanticFactsService.cs
浏览文件 @
d20421ec
...
...
@@ -44,6 +44,7 @@ internal interface ISemanticFactsService : ILanguageService
bool
IsStatementContext
(
SemanticModel
semanticModel
,
int
position
,
CancellationToken
cancellationToken
);
bool
IsTypeContext
(
SemanticModel
semanticModel
,
int
position
,
CancellationToken
cancellationToken
);
bool
IsNamespaceContext
(
SemanticModel
semanticModel
,
int
position
,
CancellationToken
cancellationToken
);
bool
IsNamespaceDeclarationNameContext
(
SemanticModel
semanticModel
,
int
position
,
CancellationToken
cancellationToken
);
bool
IsTypeDeclarationContext
(
SemanticModel
semanticModel
,
int
position
,
CancellationToken
cancellationToken
);
bool
IsMemberDeclarationContext
(
SemanticModel
semanticModel
,
int
position
,
CancellationToken
cancellationToken
);
bool
IsPreProcessorDirectiveContext
(
SemanticModel
semanticModel
,
int
position
,
CancellationToken
cancellationToken
);
...
...
src/Workspaces/Core/Portable/Shared/Extensions/ContextQuery/AbstractSyntaxContext.cs
浏览文件 @
d20421ec
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using
System
;
using
System.Collections.Generic
;
using
System.Threading
;
using
Microsoft.CodeAnalysis.Host
;
using
Microsoft.CodeAnalysis.LanguageServices
;
using
Roslyn.Utilities
;
namespace
Microsoft.CodeAnalysis.Shared.Extensions.ContextQuery
...
...
@@ -106,5 +106,15 @@ public ISet<INamedTypeSymbol> GetOuterTypes(CancellationToken cancellationToken)
{
return
this
.
Workspace
.
Services
.
GetService
<
TService
>();
}
public
bool
IntersectsWith
(
Location
location
)
{
if
(
location
==
null
)
{
throw
new
ArgumentNullException
(
nameof
(
location
));
}
return
location
.
SourceTree
==
SyntaxTree
&&
location
.
SourceSpan
.
IntersectsWith
(
Position
);
}
}
}
src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSemanticFactsService.vb
浏览文件 @
d20421ec
...
...
@@ -243,5 +243,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Public
Function
IsNameOfContext
(
semanticModel
As
SemanticModel
,
position
As
Integer
,
cancellationToken
As
CancellationToken
)
As
Boolean
Implements
ISemanticFactsService
.
IsNameOfContext
Return
semanticModel
.
SyntaxTree
.
IsNameOfContext
(
position
,
cancellationToken
)
End
Function
Public
Function
IsNamespaceDeclarationNameContext
(
semanticModel
As
SemanticModel
,
position
As
Integer
,
cancellationToken
As
CancellationToken
)
As
Boolean
Implements
ISemanticFactsService
.
IsNamespaceDeclarationNameContext
'
' TODO: part of https://github.com/dotnet/roslyn/issues/7213
'
Return
False
End
Function
End
Class
End
Namespace
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录