当前位置: 首页>>代码示例>>C#>>正文


C# Document.GetCSharpSyntaxTreeAsync方法代码示例

本文整理汇总了C#中Document.GetCSharpSyntaxTreeAsync方法的典型用法代码示例。如果您正苦于以下问题:C# Document.GetCSharpSyntaxTreeAsync方法的具体用法?C# Document.GetCSharpSyntaxTreeAsync怎么用?C# Document.GetCSharpSyntaxTreeAsync使用的例子?那么恭喜您, 这里精选的方法代码示例或许可以为您提供帮助。您也可以进一步了解该方法所在Document的用法示例。


在下文中一共展示了Document.GetCSharpSyntaxTreeAsync方法的11个代码示例,这些例子默认根据受欢迎程度排序。您可以为喜欢或者感觉有用的代码点赞,您的评价将有助于系统推荐出更棒的C#代码示例。

示例1: GetSpeculativeTCompletions

        private async Task<IEnumerable<CompletionItem>> GetSpeculativeTCompletions(Document document, int position, CancellationToken cancellationToken)
        {
            var syntaxTree = await document.GetCSharpSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
            if (syntaxTree.IsInNonUserCode(position, cancellationToken) ||
                syntaxTree.IsPreProcessorDirectiveContext(position, cancellationToken))
            {
                return SpecializedCollections.EmptyEnumerable<CompletionItem>();
            }

            // If we're in a generic type argument context, use the start of the generic type name
            // as the position for the rest of the context checks.
            int testPosition = position;
            var leftToken = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken);

            var semanticModel = await document.GetCSharpSemanticModelForNodeAsync(leftToken.Parent, cancellationToken).ConfigureAwait(false);
            if (syntaxTree.IsGenericTypeArgumentContext(position, leftToken, cancellationToken, semanticModel))
            {
                // Walk out until we find the start of the partial written generic
                SyntaxToken nameToken;
                while (syntaxTree.IsInPartiallyWrittenGeneric(testPosition, cancellationToken, out nameToken))
                {
                    testPosition = nameToken.SpanStart;
                }

                // If the user types Foo<T, automatic brace completion will insert the close brace
                // and the generic won't be "partially written".
                if (testPosition == position)
                {
                    var typeArgumentList = leftToken.GetAncestor<TypeArgumentListSyntax>();
                    if (typeArgumentList != null)
                    {
                        if (typeArgumentList.LessThanToken != default(SyntaxToken) && typeArgumentList.GreaterThanToken != default(SyntaxToken))
                        {
                            testPosition = typeArgumentList.LessThanToken.SpanStart;
                        }
                    }
                }
            }

            if ((!leftToken.GetPreviousTokenIfTouchingWord(position).IsKindOrHasMatchingText(SyntaxKind.AsyncKeyword) &&
                syntaxTree.IsMemberDeclarationContext(testPosition, contextOpt: null, validModifiers: SyntaxKindSet.AllMemberModifiers, validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken)) ||
                syntaxTree.IsGlobalMemberDeclarationContext(testPosition, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) ||
                syntaxTree.IsGlobalStatementContext(testPosition, cancellationToken) ||
                syntaxTree.IsDelegateReturnTypeContext(testPosition, syntaxTree.FindTokenOnLeftOfPosition(testPosition, cancellationToken), cancellationToken))
            {
                var text = await syntaxTree.GetTextAsync(cancellationToken).ConfigureAwait(false);
                var textChangeSpan = this.GetTextChangeSpan(text, position);

                const string T = "T";
                return SpecializedCollections.SingletonEnumerable(
                    new CSharpCompletionItem(document.Project.Solution.Workspace, this, T, textChangeSpan, descriptionFactory: null, glyph: Glyph.TypeParameter));
            }

            return SpecializedCollections.EmptyEnumerable<CompletionItem>();
        }
开发者ID:ehsansajjad465,项目名称:roslyn,代码行数:55,代码来源:SpeculativeTCompletionProvider.cs

示例2: GetItemsWorkerAsync

        protected override async Task<IEnumerable<CompletionItem>> GetItemsWorkerAsync(Document document, int position, CompletionTriggerInfo triggerInfo, System.Threading.CancellationToken cancellationToken)
        {
            var tree = await document.GetCSharpSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
            if (!tree.IsEntirelyWithinCrefSyntax(position, cancellationToken))
            {
                return null;
            }

            var token = tree.FindTokenOnLeftOfPosition(position, cancellationToken);
            token = token.GetPreviousTokenIfTouchingWord(position);
            if (token.Kind() == SyntaxKind.None)
            {
                return null;
            }

            var result = SpecializedCollections.EmptyEnumerable<ISymbol>();
            var semanticModel = await document.GetCSharpSemanticModelForNodeAsync(token.Parent, cancellationToken).ConfigureAwait(false);

            // cref ""|, ""|"", ""a|""
            if (token.IsKind(SyntaxKind.DoubleQuoteToken, SyntaxKind.SingleQuoteToken) && token.Parent.IsKind(SyntaxKind.XmlCrefAttribute))
            {
                result = semanticModel.LookupSymbols(token.SpanStart)
                                        .FilterToVisibleAndBrowsableSymbols(document.ShouldHideAdvancedMembers(), semanticModel.Compilation);

                result = result.Concat(GetOperatorsAndIndexers(token, semanticModel, cancellationToken));
            }
            else if (IsSignatureContext(token))
            {
                result = semanticModel.LookupNamespacesAndTypes(token.SpanStart)
                                        .FilterToVisibleAndBrowsableSymbols(document.ShouldHideAdvancedMembers(), semanticModel.Compilation);
            }
            else if (token.IsKind(SyntaxKind.DotToken) && token.Parent.IsKind(SyntaxKind.QualifiedCref))
            {
                // cref "a.|"
                var parent = token.Parent as QualifiedCrefSyntax;
                var leftType = semanticModel.GetTypeInfo(parent.Container, cancellationToken).Type;
                var leftSymbol = semanticModel.GetSymbolInfo(parent.Container, cancellationToken).Symbol;

                var container = leftSymbol ?? leftType;

                result = semanticModel.LookupSymbols(token.SpanStart, container: (INamespaceOrTypeSymbol)container)
                                        .FilterToVisibleAndBrowsableSymbols(document.ShouldHideAdvancedMembers(), semanticModel.Compilation);

                if (container is INamedTypeSymbol)
                {
                    result = result.Concat(((INamedTypeSymbol)container).InstanceConstructors);
                }
            }

            return await CreateItemsAsync(document.Project.Solution.Workspace, semanticModel,
                position, result, token, cancellationToken).ConfigureAwait(false);
        }
开发者ID:ehsansajjad465,项目名称:roslyn,代码行数:52,代码来源:CrefCompletionProvider.cs

示例3: GetSnippetsForDocumentAsync

        private async Task<IEnumerable<CompletionItem>> GetSnippetsForDocumentAsync(Document document, int position, Workspace workspace, CancellationToken cancellationToken)
        {
            var syntaxTree = await document.GetCSharpSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
            var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
            var semanticFacts = document.GetLanguageService<ISemanticFactsService>();

            if (syntaxFacts.IsInNonUserCode(syntaxTree, position, cancellationToken) ||
                syntaxTree.IsRightOfDotOrArrowOrColonColon(position, cancellationToken) ||
                syntaxFacts.GetContainingTypeDeclaration(await syntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false), position) is EnumDeclarationSyntax)
            {
                return SpecializedCollections.EmptyEnumerable<CompletionItem>();
            }

            var span = new TextSpan(position, 0);
            var semanticModel = await document.GetCSharpSemanticModelForSpanAsync(span, cancellationToken).ConfigureAwait(false);
            if (semanticFacts.IsPreProcessorDirectiveContext(semanticModel, position, cancellationToken))
            {
                var directive = syntaxTree.GetRoot(cancellationToken).FindTokenOnLeftOfPosition(position, includeDirectives: true).GetAncestor<DirectiveTriviaSyntax>();
                if (directive.DirectiveNameToken.IsKind(
                    SyntaxKind.IfKeyword,
                    SyntaxKind.RegionKeyword,
                    SyntaxKind.ElseKeyword,
                    SyntaxKind.ElifKeyword,
                    SyntaxKind.ErrorKeyword,
                    SyntaxKind.LineKeyword,
                    SyntaxKind.PragmaKeyword,
                    SyntaxKind.EndIfKeyword,
                    SyntaxKind.UndefKeyword,
                    SyntaxKind.EndRegionKeyword,
                    SyntaxKind.WarningKeyword))
                {
                    return SpecializedCollections.EmptyEnumerable<CompletionItem>();
                }

                return await GetSnippetCompletionItemsAsync(workspace, semanticModel, position, isPreProcessorContext: true, cancellationToken: cancellationToken).ConfigureAwait(false);
            }

            if (semanticFacts.IsGlobalStatementContext(semanticModel, position, cancellationToken) ||
                semanticFacts.IsExpressionContext(semanticModel, position, cancellationToken) ||
                semanticFacts.IsStatementContext(semanticModel, position, cancellationToken) ||
                semanticFacts.IsTypeContext(semanticModel, position, cancellationToken) ||
                semanticFacts.IsTypeDeclarationContext(semanticModel, position, cancellationToken) ||
                semanticFacts.IsNamespaceContext(semanticModel, position, cancellationToken) ||
                semanticFacts.IsMemberDeclarationContext(semanticModel, position, cancellationToken) ||
                semanticFacts.IsLabelContext(semanticModel, position, cancellationToken))
            {
                return await GetSnippetCompletionItemsAsync(workspace, semanticModel, position, isPreProcessorContext: false, cancellationToken: cancellationToken).ConfigureAwait(false);
            }

            return SpecializedCollections.EmptyEnumerable<CompletionItem>();
        }
开发者ID:elemk0vv,项目名称:roslyn-1,代码行数:51,代码来源:SnippetCompletionProvider.cs

示例4: TryCompleteTag

        protected override void TryCompleteTag(ITextView textView, ITextBuffer subjectBuffer, Document document, SnapshotPoint position, CancellationToken cancellationToken)
        {
            var tree = document.GetCSharpSyntaxTreeAsync(cancellationToken).WaitAndGetResult(cancellationToken);
            var token = tree.FindTokenOnLeftOfPosition(position, cancellationToken, includeDocumentationComments: true);

            if (token.IsKind(SyntaxKind.GreaterThanToken))
            {
                var parentStartTag = token.Parent as XmlElementStartTagSyntax;
                if (parentStartTag == null)
                {
                    return;
                }

                // Slightly special case: <blah><blah$$</blah>
                // If we already have a matching end tag and we're parented by 
                // an xml element with the same start tag and a missing/nonmatching end tag, 
                // do completion anyway. Generally, if this is the case, we have to walk
                // up the parent elements until we find an unmatched start tag.

                if (parentStartTag.Name.LocalName.ValueText.Length > 0 && HasMatchingEndTag(parentStartTag))
                {
                    if (HasUnmatchedIdenticalParent(parentStartTag))
                    {
                        InsertTextAndMoveCaret(textView, subjectBuffer, position, "</" + parentStartTag.Name.LocalName.ValueText + ">", position);
                        return;
                    }
                }

                CheckNameAndInsertText(textView, subjectBuffer, position, parentStartTag, position.Position, "</{0}>");
            }
            else if (token.IsKind(SyntaxKind.LessThanSlashToken))
            {
                // /// <summary>
                // /// </$$
                // /// </summary>
                // We need to check for non-trivia XML text tokens after $$ that match the expected end tag text.

                if (token.Parent.IsKind(SyntaxKind.XmlElementEndTag) &&
                    token.Parent.IsParentKind(SyntaxKind.XmlElement))
                {
                    var parentElement = token.Parent.Parent as XmlElementSyntax;

                    if (!HasFollowingEndTagTrivia(parentElement, token))
                    {
                        CheckNameAndInsertText(textView, subjectBuffer, position, parentElement.StartTag, null, "{0}>");
                    }
                }
            }
        }
开发者ID:ehsansajjad465,项目名称:roslyn,代码行数:49,代码来源:XmlTagCompletionCommandHandler.cs

示例5: GetTypeDeclaration

        internal override SyntaxNode GetTypeDeclaration(Document document, int position, TypeDiscoveryRule typeDiscoveryRule, CancellationToken cancellationToken)
        {
            var tree = document.GetCSharpSyntaxTreeAsync(cancellationToken).WaitAndGetResult(cancellationToken);
            var token = tree.GetRoot(cancellationToken).FindToken(position != tree.Length ? position : Math.Max(0, position - 1));
            var typeDeclaration = token.GetAncestor<TypeDeclarationSyntax>();

            if (typeDeclaration == null ||
                typeDiscoveryRule == TypeDiscoveryRule.TypeDeclaration)
            {
                return typeDeclaration;
            }

            var spanStart = typeDeclaration.Identifier.SpanStart;
            var spanEnd = typeDeclaration.TypeParameterList != null ? typeDeclaration.TypeParameterList.Span.End : typeDeclaration.Identifier.Span.End;
            var span = new TextSpan(spanStart, spanEnd - spanStart);

            return span.IntersectsWith(position) ? typeDeclaration : null;
        }
开发者ID:ehsansajjad465,项目名称:roslyn,代码行数:18,代码来源:CSharpExtractInterfaceService.cs

示例6: GetItemsWorkerAsync

        protected override async Task<IEnumerable<CompletionItem>> GetItemsWorkerAsync(
            Document document, int position, CompletionTriggerInfo triggerInfo, CancellationToken cancellationToken)
        {
            if (document != null && document.SourceCodeKind == SourceCodeKind.Interactive)
            {
                // the provider might be invoked in non-interactive context:
                Workspace ws;
                if (Workspace.TryGetWorkspace(document.GetTextAsync(cancellationToken).WaitAndGetResult(cancellationToken).Container, out ws))
                {
                    var workspace = ws as InteractiveWorkspace;
                    if (workspace != null)
                    {
                        var window = workspace.Engine.CurrentWindow;
                        var tree = await document.GetCSharpSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);

                        if (tree.IsBeforeFirstToken(position, cancellationToken) &&
                            tree.IsPreProcessorKeywordContext(position, cancellationToken))
                        {
                            var textChangeSpan = await this.GetTextChangeSpanAsync(document, position, cancellationToken).ConfigureAwait(false);
                            var list = new List<CompletionItem>();

                            IInteractiveWindowCommands commands = window.GetInteractiveCommands();
                            if (commands != null)
                            {
                                foreach (var command in commands.GetCommands())
                                {
                                    foreach (var commandName in command.Names)
                                    {
                                        list.Add(new CSharpCompletionItem(
                                            workspace, this, commandName, textChangeSpan, c => Task.FromResult(command.Description.ToSymbolDisplayParts()), glyph: Glyph.Intrinsic));
                                    }
                                }
                            }

                            return list;
                        }
                    }
                }
            }

            return SpecializedCollections.EmptyEnumerable<CompletionItem>();
        }
开发者ID:ehsansajjad465,项目名称:roslyn,代码行数:42,代码来源:ReplCommandCompletionProvider.cs

示例7: MoveDeclarationNearReferenceAsync

        private async Task<Document> MoveDeclarationNearReferenceAsync(Document document, State state, CancellationToken cancellationToken)
        {
            var innermostStatements =
                state.InnermostBlock.Statements.Where(s => s != state.DeclarationStatement).ToList();
            var innermostAffectedIndex = innermostStatements.IndexOf(state.FirstStatementAffectedInInnermostBlock);

            var crossesMeaningfulBlock = CrossesMeaningfulBlock(state);
            var warningAnnotation = crossesMeaningfulBlock
                ? WarningAnnotation.Create(CSharpFeaturesResources.WarningDeclarationChangesScope)
                : null;

            var canMergeDeclarationAndAssignment = await CanMergeDeclarationAndAssignmentAsync(document, state, cancellationToken).ConfigureAwait(false);
            if (canMergeDeclarationAndAssignment)
            {
                // Replace the first reference with a new declaration.
                var declarationStatement = CreateMergedDeclarationStatement(state, state.FirstStatementAffectedInInnermostBlock);
                declarationStatement = warningAnnotation == null
                    ? declarationStatement
                    : declarationStatement.WithAdditionalAnnotations(warningAnnotation);

                innermostStatements[innermostAffectedIndex] = declarationStatement.WithAdditionalAnnotations(Formatter.Annotation);
            }
            else
            {
                // If we're not merging with an existing declaration, make the declaration semantically
                // explicit to improve the chances that it won't break code.
                var explicitDeclarationStatement = await Simplifier.ExpandAsync(state.DeclarationStatement, document, cancellationToken: cancellationToken).ConfigureAwait(false);

                // place the declaration above the first statement that references it.
                var declarationStatement = warningAnnotation == null
                    ? explicitDeclarationStatement
                    : explicitDeclarationStatement.WithAdditionalAnnotations(warningAnnotation);

                innermostStatements.Insert(innermostAffectedIndex, declarationStatement.WithAdditionalAnnotations(Formatter.Annotation));
            }

            var newInnermostBlock = state.InnermostBlock.WithStatements(
                    SyntaxFactory.List<StatementSyntax>(innermostStatements)).WithAdditionalAnnotations(Formatter.Annotation);

            var tree = await document.GetCSharpSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
            var rewriter = new Rewriter(state.InnermostBlock, newInnermostBlock, state.OutermostBlock, state.DeclarationStatement);
            var newRoot = rewriter.Visit(tree.GetRoot(cancellationToken));

            return document.WithSyntaxRoot(newRoot);
        }
开发者ID:ehsansajjad465,项目名称:roslyn,代码行数:45,代码来源:MoveDeclarationNearReferenceCodeRefactoringProvider.cs

示例8: GetSelectedMembersAsync

 internal static async Task<IList<MemberDeclarationSyntax>> GetSelectedMembersAsync(
     Document document, TextSpan textSpan, CancellationToken cancellationToken)
 {
     var tree = await document.GetCSharpSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
     return tree.GetMembersInSpan(textSpan, cancellationToken);
 }
开发者ID:ehsansajjad465,项目名称:roslyn,代码行数:6,代码来源:GenerateFromMembersHelpers.cs

示例9: GetTokenBeforeTheCaretAsync

        private static async Task<SyntaxToken> GetTokenBeforeTheCaretAsync(Document document, int caretPosition, CancellationToken cancellationToken)
        {
            var tree = await document.GetCSharpSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);

            var position = Math.Max(0, caretPosition - 1);
            var root = await tree.GetRootAsync(cancellationToken).ConfigureAwait(false);
            var token = root.FindToken(position, findInsideTrivia: true);
            return token;
        }
开发者ID:ehsansajjad465,项目名称:roslyn,代码行数:9,代码来源:CSharpEditorFormattingService.cs

示例10: GetItemsWorkerAsync

        protected override async Task<IEnumerable<CompletionItem>> GetItemsWorkerAsync(Document document, int position, CompletionTriggerInfo triggerInfo, CancellationToken cancellationToken)
        {
            if (triggerInfo.IsDebugger)
            {
                return null;
            }

            var tree = await document.GetCSharpSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
            var token = tree.FindTokenOnLeftOfPosition(position, cancellationToken);
            var parentTrivia = token.GetAncestor<DocumentationCommentTriviaSyntax>();

            if (parentTrivia == null)
            {
                return null;
            }

            var items = new List<CompletionItem>();
            var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
            var span = CompletionUtilities.GetTextChangeSpan(text, position);

            var attachedToken = parentTrivia.ParentTrivia.Token;
            if (attachedToken.Kind() == SyntaxKind.None)
            {
                return null;
            }

            var semanticModel = await document.GetCSharpSemanticModelForNodeAsync(attachedToken.Parent, cancellationToken).ConfigureAwait(false);

            ISymbol declaredSymbol = null;
            var memberDeclaration = attachedToken.GetAncestor<MemberDeclarationSyntax>();
            if (memberDeclaration != null)
            {
                declaredSymbol = semanticModel.GetDeclaredSymbol(memberDeclaration, cancellationToken);
            }
            else
            {
                var typeDeclaration = attachedToken.GetAncestor<TypeDeclarationSyntax>();
                if (typeDeclaration != null)
                {
                    declaredSymbol = semanticModel.GetDeclaredSymbol(typeDeclaration, cancellationToken);
                }
            }

            if (declaredSymbol != null)
            {
                items.AddRange(GetTagsForSymbol(declaredSymbol, span, parentTrivia, token));
            }

            if (token.Parent.Kind() == SyntaxKind.XmlEmptyElement || token.Parent.Kind() == SyntaxKind.XmlText ||
                (token.Parent.IsKind(SyntaxKind.XmlElementEndTag) && token.IsKind(SyntaxKind.GreaterThanToken)) ||
                (token.Parent.IsKind(SyntaxKind.XmlName) && token.Parent.IsParentKind(SyntaxKind.XmlEmptyElement)))
            {
                if (token.Parent.Parent.Kind() == SyntaxKind.XmlElement)
                {
                    items.AddRange(GetNestedTags(span));
                }

                if (token.Parent.Parent.Kind() == SyntaxKind.XmlElement && ((XmlElementSyntax)token.Parent.Parent).StartTag.Name.LocalName.ValueText == "list")
                {
                    items.AddRange(GetListItems(span));
                }

                if (token.Parent.IsParentKind(SyntaxKind.XmlEmptyElement) & token.Parent.Parent.IsParentKind(SyntaxKind.XmlElement))
                {
                    var element = (XmlElementSyntax)token.Parent.Parent.Parent;
                    if (element.StartTag.Name.LocalName.ValueText == "list")
                    {
                        items.AddRange(GetListItems(span));
                    }
                }

                if (token.Parent.Parent.Kind() == SyntaxKind.XmlElement && ((XmlElementSyntax)token.Parent.Parent).StartTag.Name.LocalName.ValueText == "listheader")
                {
                    items.AddRange(GetListHeaderItems(span));
                }

                if (token.Parent.Parent is DocumentationCommentTriviaSyntax)
                {
                    items.AddRange(GetTopLevelSingleUseNames(parentTrivia, span));
                    items.AddRange(GetTopLevelRepeatableItems(span));
                }
            }

            if (token.Parent.Kind() == SyntaxKind.XmlElementStartTag)
            {
                var startTag = (XmlElementStartTagSyntax)token.Parent;

                if (token == startTag.GreaterThanToken && startTag.Name.LocalName.ValueText == "list")
                {
                    items.AddRange(GetListItems(span));
                }

                if (token == startTag.GreaterThanToken && startTag.Name.LocalName.ValueText == "listheader")
                {
                    items.AddRange(GetListHeaderItems(span));
                }
            }

            items.AddRange(GetAlwaysVisibleItems(span));
            return items;
//.........这里部分代码省略.........
开发者ID:ehsansajjad465,项目名称:roslyn,代码行数:101,代码来源:XmlDocCommentCompletionProvider.cs

示例11: GetPlusEqualsTokenInsideAddAssignExpressionAsync

            private async Task<SyntaxToken?> GetPlusEqualsTokenInsideAddAssignExpressionAsync(Document document, int position, CancellationToken cancellationToken)
            {
                AssertIsBackground();
                var syntaxTree = await document.GetCSharpSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
                var token = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken);

                if (token.Kind() != SyntaxKind.PlusEqualsToken)
                {
                    return null;
                }

                if (!token.Parent.IsKind(SyntaxKind.AddAssignmentExpression))
                {
                    return null;
                }

                return token;
            }
开发者ID:ehsansajjad465,项目名称:roslyn,代码行数:18,代码来源:EventHookupSessionManager_EventHookupSession.cs


注:本文中的Document.GetCSharpSyntaxTreeAsync方法示例由纯净天空整理自Github/MSDocs等开源代码及文档管理平台,相关代码片段筛选自各路编程大神贡献的开源项目,源码版权归原作者所有,传播和使用请参考对应项目的License;未经允许,请勿转载。