/////////////////////////////////////////////////////////////////////////////////////////////////////
        // OBJECT
        /////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Initializes a new instance of the <c>SimpleOutliningSource</c> class.
        /// </summary>
        /// <param name="snapshot">The <see cref="ITextSnapshot"/> to use for this outlining source.</param>
        /// <param name="parseData">The <see cref="ILLParseData"/> containing AST data.</param>
        public SimpleOutliningSource(ITextSnapshot snapshot, ILLParseData parseData) : base(snapshot)
        {
            if (parseData == null)
            {
                throw new ArgumentNullException("parseData");
            }

            // Create a 'Function' outlining node definition if one hasn't yet been created
            if (functionDefinition == null)
            {
                functionDefinition = new OutliningNodeDefinition("Function");
                functionDefinition.IsImplementation = true;
            }

            Step4d.CompilationUnit compilationUnit = parseData.Ast as Step4d.CompilationUnit;
            if ((compilationUnit != null) && (compilationUnit.HasMembers))
            {
                // Loop through AST nodes
                foreach (Step4d.FunctionDeclaration functionAstNode in compilationUnit.Members)
                {
                    // If the function declaration has a body with a text range...
                    if ((functionAstNode.Body != null) && (functionAstNode.Body.StartOffset.HasValue) && (functionAstNode.Body.EndOffset.HasValue))
                    {
                        // Add an outlining node
                        this.AddNode(new TextRange(functionAstNode.Body.StartOffset.Value, functionAstNode.Body.EndOffset.Value), functionDefinition);
                    }
                }
            }
        }
示例#2
0
        /// <summary>
        /// Occurs when the document's parse data has changed.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">The <c>EventArgs</c> that contains data related to this event.</param>
        private void OnEditorDocumentParseDataChanged(object sender, EventArgs e)
        {
            //
            // NOTE: The parse data here is generated in a worker thread... this event handler is called
            //         back in the UI thread though so any processing done below could slow down UI if
            //         the processing is lengthy
            //

            ILLParseData parseData = editor.Document.ParseData as ILLParseData;

            if (parseData != null)
            {
                // Show the AST
                if (parseData.Ast != null)
                {
                    astOutputEditor.Document.SetText(parseData.Ast.ToTreeString(0).Replace("\t", "  "));
                }
                else
                {
                    astOutputEditor.Document.SetText(null);
                }

                // Output errors
                errorListView.ItemsSource = parseData.Errors;
            }
        }
示例#3
0
        private void OnCodeEditorUserInterfaceUpdate(object sender, RoutedEventArgs e)
        {
            // If there is a pending parse data change...
            if (hasPendingParseData)
            {
                // Clear flag
                hasPendingParseData = false;

                ILLParseData parseData = codeEditor.Document.ParseData as ILLParseData;
                if (parseData != null)
                {
                    //if (codeEditor.Document.CurrentSnapshot.Length < 10000)
                    //{
                    //    // Show the AST
                    //    if (parseData.Ast != null)
                    //        astOutputEditor.Document.SetText(parseData.Ast.ToTreeString(0));
                    //    else
                    //        astOutputEditor.Document.SetText(null);
                    //}
                    //else
                    //    astOutputEditor.Document.SetText("(Not displaying large AST for performance reasons)");

                    //// Output errors
                    //errorListView.ItemsSource = parseData.Errors;
                }
                else
                {
                    // Clear UI
                    //astOutputEditor.Document.SetText(null);
                    //errorListView.ItemsSource = null;
                    //messagePanel.Content = null;
                }
            }
        }
        /// <summary>
        /// Examines the AST data to determine the context type and insert the containing function declaration.
        /// </summary>
        /// <param name="context">The <see cref="SimpleContext"/> to update.</param>
        private static void UpdateFromAst(SimpleContext context)
        {
            // Get the snapshot offset
            TextSnapshotOffset snapshotOffset = context.SnapshotOffset;

            // Get the document
            ICodeDocument document = snapshotOffset.Snapshot.Document as ICodeDocument;

            if (document == null)
            {
                return;
            }

            ILLParseData parseData = document.ParseData as ILLParseData;

            if (parseData != null)
            {
                CompilationUnit compilationUnit = parseData.Ast as CompilationUnit;
                if ((compilationUnit != null) && (compilationUnit.HasMembers))
                {
                    // Translate the snapshot offset to the AST's snapshot
                    if (parseData.Snapshot != null)
                    {
                        snapshotOffset = snapshotOffset.TranslateTo(parseData.Snapshot, TextOffsetTrackingMode.Negative);
                    }

                    // Loop through AST nodes
                    foreach (FunctionDeclaration functionAstNode in compilationUnit.Members)
                    {
                        // If the child node is a function declaration with valid offsets...
                        if ((functionAstNode.StartOffset.HasValue) && (functionAstNode.EndOffset.HasValue))
                        {
                            // If the function's text range contains the offset...
                            TextRange functionTextRange = new TextRange(functionAstNode.StartOffset.Value, functionAstNode.EndOffset.Value);
                            if (functionTextRange.Contains(snapshotOffset.Offset))
                            {
                                // Initially assume we are in a header
                                context.Type = SimpleContextType.FunctionDeclarationHeader;
                                context.ContainingFunctionDeclaration = functionAstNode;

                                // If the function has a body with a range...
                                if ((functionAstNode.Body != null) && (functionAstNode.Body.StartOffset.HasValue) && (functionAstNode.Body.EndOffset.HasValue))
                                {
                                    // If the block's text range contains the offset...
                                    TextRange blockTextRange = new TextRange(functionAstNode.Body.StartOffset.Value + 1, functionAstNode.Body.EndOffset.Value - 1);
                                    if (blockTextRange.Contains(snapshotOffset.Offset))
                                    {
                                        // Mark that we are in a block instead
                                        context.Type = SimpleContextType.FunctionDeclarationBlock;
                                    }
                                }
                                break;
                            }
                        }
                    }
                }
            }
        }
示例#5
0
        /// <summary>
        /// Occurs after a brief delay following any document text, parse data, or view selection update, allowing consumers to update the user interface during an idle period.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">The <see cref="RoutedEventArgs"/> that contains data related to this event.</param>
        private void OnEditorUserInterfaceUpdate(object sender, RoutedEventArgs e)
        {
            // If there is a pending parse data change...
            if (hasPendingParseData)
            {
                // Clear flag
                hasPendingParseData = false;

                ILLParseData parseData = editor.Document.ParseData as ILLParseData;
                if (parseData != null)
                {
                    // Output errors
                    errorListView.ItemsSource = parseData.Errors;
                }
                else
                {
                    // Clear UI
                    errorListView.ItemsSource = null;
                }
            }
        }
        /// <summary>
        /// Occurs after a brief delay following any document text, parse data, or view selection update, allowing consumers to update the user interface during an idle period.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">The <see cref="RoutedEventArgs"/> that contains data related to this event.</param>
        private void OnSyntaxEditorUserInterfaceUpdate(object sender, RoutedEventArgs e)
        {
            // If there is a pending parse data change...
            if (hasPendingParseData)
            {
                // Clear flag
                hasPendingParseData = false;

                ILLParseData parseData = editor.Document.ParseData as ILLParseData;
                if (parseData != null)
                {
                    if (editor.Document.CurrentSnapshot.Length < 10000)
                    {
                        // Show the AST
                        if (parseData.Ast != null)
                        {
                            astOutputEditor.Document.SetText(parseData.Ast.ToTreeString(0));
                        }
                        else
                        {
                            astOutputEditor.Document.SetText(null);
                        }
                    }
                    else
                    {
                        astOutputEditor.Document.SetText("(Not displaying large AST for performance reasons)");
                    }

                    // Output errors
                    errorListView.ItemsSource = parseData.Errors;
                }
                else
                {
                    // Clear UI
                    astOutputEditor.Document.SetText("(Language may not have AST building features)");
                    errorListView.ItemsSource = null;
                }
            }
        }
示例#7
0
        /////////////////////////////////////////////////////////////////////////////////////////////////////
        // PUBLIC PROCEDURES
        /////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Returns an <see cref="IOutliningSource"/> for the specified <see cref="ITextSnapshot"/>.
        /// </summary>
        /// <param name="snapshot">The <see cref="ITextSnapshot"/> for which to return an outlining source.</param>
        /// <returns>An <see cref="IOutliningSource"/> for the specified <see cref="ITextSnapshot"/>.</returns>
        public IOutliningSource GetOutliningSource(ITextSnapshot snapshot)
        {
            if (snapshot != null)
            {
                ICodeDocument document = snapshot.Document as ICodeDocument;
                if (document != null)
                {
                    // Get the parse data
                    ILLParseData parseData = document.ParseData as ILLParseData;
                    if (parseData != null)
                    {
                        // Create an outlining source based on the parse data
                        SimpleOutliningSource source = new SimpleOutliningSource(snapshot, parseData);

                        // Translate the data to the desired snapshot, which could be slightly newer than the parsed source
                        source.TranslateTo(snapshot);

                        return(source);
                    }
                }
            }
            return(null);
        }
        /// <summary>
        /// Examines the snapshot text to determine more detail about the context.
        /// </summary>
        /// <param name="context">The <see cref="SimpleContext"/> to update.</param>
        /// <param name="includeArgumentInfo">Whether to populate the argument-related context properties, for use with parameter info.</param>
        private static void UpdateFromSnapshotText(SimpleContext context, bool includeArgumentInfo)
        {
            // Get the snapshot offset
            TextSnapshotOffset snapshotOffset = context.SnapshotOffset;

            // Create a default initialization range
            context.InitializationSnapshotRange = new TextSnapshotRange(snapshotOffset);

            // Get a snapshot reader
            ITextSnapshotReader reader = snapshotOffset.Snapshot.GetReader(snapshotOffset.Offset);

            if (reader == null)
            {
                return;
            }

            IToken token = reader.ReadToken();

            if (token != null)
            {
                // If the current token is a comment, flag no context
                switch (token.Id)
                {
                case SimpleTokenId.MultiLineCommentEndDelimiter:
                case SimpleTokenId.MultiLineCommentLineTerminator:
                case SimpleTokenId.MultiLineCommentStartDelimiter:
                case SimpleTokenId.MultiLineCommentText:
                case SimpleTokenId.SingleLineCommentEndDelimiter:
                case SimpleTokenId.SingleLineCommentStartDelimiter:
                case SimpleTokenId.SingleLineCommentText:
                    context.Type = SimpleContextType.None;
                    return;
                }
            }

            // If we are in a function declaration block...
            if (context.Type == SimpleContextType.FunctionDeclarationBlock)
            {
                // Get the AST
                ICodeDocument document = snapshotOffset.Snapshot.Document as ICodeDocument;
                if (document == null)
                {
                    return;
                }
                ILLParseData parseData = document.ParseData as ILLParseData;
                if (parseData == null)
                {
                    return;
                }
                CompilationUnit compilationUnit = parseData.Ast as CompilationUnit;
                if ((compilationUnit == null) || (!compilationUnit.HasMembers))
                {
                    return;
                }

                // If argument data should be scanned...
                if (includeArgumentInfo)
                {
                    // Scan backward to look for argument data
                    reader.Offset = snapshotOffset.Offset;
                    reader.GoToCurrentTokenStart();
                    var startOffset = reader.Offset;
                    token = reader.ReadTokenReverse();
                    while (token != null)
                    {
                        // Quit if we look behind too far (for performance reasons) - 500 is arbitrary number and could be tweaked
                        if ((!context.ArgumentIndex.HasValue) && (startOffset - reader.Offset > 500))
                        {
                            return;
                        }

                        switch (token.Id)
                        {
                        case SimpleTokenId.CloseParenthesis:
                            // Skip over ( ... ) pair
                            if (!reader.GoToPreviousMatchingTokenById(SimpleTokenId.CloseParenthesis, SimpleTokenId.OpenParenthesis))
                            {
                                return;
                            }
                            break;

                        case SimpleTokenId.Comma:
                            // Update the context data
                            if (context.ArgumentIndex.HasValue)
                            {
                                context.ArgumentIndex++;
                            }
                            else
                            {
                                context.ArgumentIndex          = 1;
                                context.ArgumentSnapshotOffset = new TextSnapshotOffset(reader.Snapshot, token.EndOffset);
                            }
                            break;

                        case SimpleTokenId.OpenParenthesis:
                            // Update the context data
                            context.ArgumentListSnapshotOffset = new TextSnapshotOffset(reader.Snapshot, token.EndOffset);
                            if (!context.ArgumentIndex.HasValue)
                            {
                                context.ArgumentIndex          = 0;
                                context.ArgumentSnapshotOffset = context.ArgumentListSnapshotOffset;
                            }

                            // Go to the previous token
                            reader.GoToPreviousToken();
                            token = reader.Token;
                            if ((token != null) && (token.Id == SimpleTokenId.Identifier))
                            {
                                // Loop through the AST nodes to ensure that the identifier text matches a declared function name
                                string functionName = reader.TokenText;
                                foreach (FunctionDeclaration functionAstNode in compilationUnit.Members)
                                {
                                    // If the name matches the function's name...
                                    if (functionAstNode.Name == functionName)
                                    {
                                        // Update the target function
                                        context.TargetFunction = functionAstNode;
                                        break;
                                    }
                                }
                            }
                            return;

                        default:
                            // Quit if any tokens are found that aren't allowed in invocations
                            if (!IsTokenAllowedInInvocation(token.Id))
                            {
                                return;
                            }
                            else
                            {
                                break;
                            }
                        }

                        // Move back a token
                        token = reader.ReadTokenReverse();
                    }
                }
                else
                {
                    // If the current token is an identifier...
                    if ((token != null) && (token.Id == SimpleTokenId.Identifier))
                    {
                        // Store the identifier snapshot range in case this is a function reference token
                        TextSnapshotRange identifierSnapshotRange = new TextSnapshotRange(reader.Snapshot, token.TextRange);

                        // Next, check to ensure the next non-whitespace token is a '('
                        token = reader.ReadToken();
                        while (!reader.IsAtSnapshotEnd)
                        {
                            if (token != null)
                            {
                                switch (token.Id)
                                {
                                case SimpleTokenId.Whitespace:
                                    // Continue
                                    break;

                                case SimpleTokenId.OpenParenthesis: {
                                    // Loop through the AST nodes to ensure that the identifier text matches a declared function name
                                    foreach (FunctionDeclaration functionAstNode in compilationUnit.Members)
                                    {
                                        // If the name matches the function's name...
                                        if (functionAstNode.Name == identifierSnapshotRange.Text)
                                        {
                                            // Update the context type
                                            context.Type = SimpleContextType.FunctionReference;
                                            context.InitializationSnapshotRange = identifierSnapshotRange;
                                            context.TargetFunction = functionAstNode;
                                            break;
                                        }
                                    }
                                    return;
                                }

                                default:
                                    // Quit
                                    return;
                                }
                            }

                            // Advance
                            token = reader.ReadToken();
                        }
                    }
                }
            }
        }
示例#9
0
        /////////////////////////////////////////////////////////////////////////////////////////////////////
        // PUBLIC PROCEDURES
        /////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Requests that an <see cref="ICompletionSession"/> be opened for the specified <see cref="IEditorView"/>.
        /// </summary>
        /// <param name="view">The <see cref="IEditorView"/> that will host the session.</param>
        /// <param name="canCommitWithoutPopup">Whether the session can immediately commit if a single match is made when the session is opened, commonly known as "complete word" functionality.</param>
        /// <returns>
        /// <c>true</c> if a session was opened; otherwise, <c>false</c>.
        /// </returns>
        public override bool RequestSession(IEditorView view, bool canCommitWithoutPopup)
        {
            // Get the context factory service
            SimpleContextFactory contextFactory = view.SyntaxEditor.Document.Language.GetService <SimpleContextFactory>();

            if (contextFactory != null)
            {
                // Get a context
                SimpleContext context = contextFactory.CreateContext(view.Selection.EndSnapshotOffset, false);

                // Create a session
                CompletionSession session = new CompletionSession();
                session.CanCommitWithoutPopup = canCommitWithoutPopup;

                switch (context.Type)
                {
                case SimpleContextType.Default:
                    // Add items for keywords
                    session.Items.Add(new CompletionItem("function", new CommonImageSourceProvider(CommonImageKind.Keyword),
                                                         new PlainTextContentProvider("Declares a function.")));
                    break;

                case SimpleContextType.FunctionDeclarationBlock:
                case SimpleContextType.FunctionReference: {
                    // Add items for keywords
                    session.Items.Add(new CompletionItem("var", new CommonImageSourceProvider(CommonImageKind.Keyword),
                                                         new PlainTextContentProvider("Declares a variable.")));
                    session.Items.Add(new CompletionItem("return", new CommonImageSourceProvider(CommonImageKind.Keyword),
                                                         new PlainTextContentProvider("Returns a value.")));

                    // Add items (one for each function name)
                    ILLParseData parseData = view.SyntaxEditor.Document.ParseData as ILLParseData;
                    if (parseData != null)
                    {
                        CompilationUnit compilationUnit = parseData.Ast as CompilationUnit;
                        if ((compilationUnit != null) && (compilationUnit.HasMembers))
                        {
                            // Loop through the AST nodes
                            foreach (FunctionDeclaration functionAstNode in compilationUnit.Members)
                            {
                                session.Items.Add(new CompletionItem(functionAstNode.Name, new CommonImageSourceProvider(CommonImageKind.MethodPublic),
                                                                     new FunctionContentProvider(view.HighlightingStyleRegistry, functionAstNode, false, view.DefaultBackgroundColor)));
                            }
                        }
                    }
                    break;
                }
                }

                if (session.Items.Count > 0)
                {
                    // Ensure the caret is visible
                    view.Scroller.ScrollToCaret();

                    // Ensure the items are sorted and open the session
                    session.SortItems();
                    session.Open(view);
                    return(true);
                }
            }
            return(false);
        }