/////////////////////////////////////////////////////////////////////////////////////////////////////
        // PUBLIC PROCEDURES
        /////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Attempts to locate structural text, such as bracket pairs, at the specified offset.
        /// </summary>
        /// <param name="snapshotOffset">The <see cref="TextSnapshotOffset"/> to examine.</param>
        /// <param name="options">The <see cref="IStructureMatchOptions"/> to use.</param>
        /// <returns>An <see cref="IStructureMatchResultSet"/> that contains the results that were found, if any.</returns>
        /// <remarks>
        /// When the result set is empty, no structural delimiter was next to the specified offset.
        /// When the result set contains a single result, a structural delimiter such as a bracket was found next to the specified offset,
        /// but no matching structural delimiter was found.
        /// The result set may contain multiple entries in cases such as when a language wishes to flag <c>#if...#else...#endif</c> blocks as a structure.
        /// </remarks>
        public override IStructureMatchResultSet Match(TextSnapshotOffset snapshotOffset, IStructureMatchOptions options)
        {
            if (snapshotOffset.IsDeleted)
            {
                throw new ArgumentNullException("snapshotOffset");
            }

            // Get a snapshot reader and configure it for quick initial lookup
            ITextSnapshotReader reader = snapshotOffset.Snapshot.GetReader(snapshotOffset.Offset);

            reader.Options.DefaultTokenLoadBufferLength = 250;
            reader.Options.InitialTokenLoadBufferLength = 4;

            IToken token = reader.Token;

            if (token != null)
            {
                // If the token is not a multi-line comment but is at the start of a token, check the previous token
                if ((token.Id != SimpleTokenId.MultiLineCommentText) && (reader.IsAtTokenStart))
                {
                    token = reader.ReadTokenReverse();
                }

                // If the token is a multi-line comment...
                if (token.Id == SimpleTokenId.MultiLineCommentText)
                {
                    // The Simple language programmatic lexer variant only has a single token for the entire comment so
                    //   ensure the target offset is at a delimiter (and not within the body of the comment)...
                    //   For most other languages, you'd want to scan tokens to find a matching delimiter token instead
                    bool isAtStart = (snapshotOffset.Offset <= token.StartOffset + 2);
                    bool isAtEnd   = (snapshotOffset.Offset >= token.EndOffset - 2);
                    if (isAtStart || isAtEnd)
                    {
                        // Get the token's text and ensure it ends with a proper delimiter
                        string tokenText = reader.TokenText;
                        if ((token.Length >= 4) && (tokenText.EndsWith("*/", StringComparison.Ordinal)))
                        {
                            // Found a valid match
                            StructureMatchResultCollection results = new StructureMatchResultCollection();
                            results.Add(new StructureMatchResult(new TextSnapshotRange(reader.Snapshot, token.StartOffset, token.StartOffset + 2))
                            {
                                IsSource = isAtStart,
                                NavigationSnapshotOffset = new TextSnapshotOffset(reader.Snapshot, token.StartOffset)
                            });
                            results.Add(new StructureMatchResult(new TextSnapshotRange(reader.Snapshot, token.EndOffset - 2, token.EndOffset))
                            {
                                IsSource = !isAtStart,
                                NavigationSnapshotOffset = new TextSnapshotOffset(reader.Snapshot, token.EndOffset)
                            });
                            return(new StructureMatchResultSet(results));
                        }
                    }
                }
            }

            // Call the base method
            return(base.Match(snapshotOffset, options));
        }
Beispiel #2
0
        /////////////////////////////////////////////////////////////////////////////////////////////////////
        // PUBLIC PROCEDURES
        /////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Returns the tag ranges that intersect with the specified normalized snapshot ranges.
        /// </summary>
        /// <param name="snapshotRanges">The collection of normalized snapshot ranges.</param>
        /// <param name="parameter">An optional parameter that provides contextual information about the tag request.</param>
        /// <returns>The tag ranges that intersect with the specified normalized snapshot ranges.</returns>
        public override IEnumerable <TagSnapshotRange <IClassificationTag> > GetTags(NormalizedTextSnapshotRangeCollection snapshotRanges, object parameter)
        {
            // Loop through the requested snapshot ranges...
            foreach (TextSnapshotRange snapshotRange in snapshotRanges)
            {
                // If the snapshot range is not zero-length...
                if (!snapshotRange.IsZeroLength)
                {
                    // Get a snapshot reader
                    ITextSnapshotReader reader = snapshotRange.Snapshot.GetReader(snapshotRange.StartOffset);

                    // If not already at the start of a line, back up to the start
                    if (!reader.IsAtSnapshotLineStart)
                    {
                        reader.GoToCurrentSnapshotLineStart();
                    }

                    // Read through the snapshot until the end of the target range is reached
                    while ((!reader.IsAtSnapshotEnd) && (reader.Offset < snapshotRange.EndOffset))
                    {
                        // Save the start of the line offset
                        int lineStartOffset = reader.Offset;

                        // Get the line start text (we need at most 6 chars for this sample)
                        string lineStartText = reader.PeekText(6);

                        // Go to the end of the line
                        reader.GoToCurrentSnapshotLineEnd();

                        // Add a range for the line if it starts with one of the defined strings...
                        //   The StyleRegistryClassificationTag is a special ClassificationTag that allows you to indicate
                        //   an alternate IHighlightingStyleRegistry to use for syntax highlighting... if using the
                        //   normal AmbientHighlightingStyleRegistry, you'd just use a regular ClassificationTag instead
                        if (lineStartText.StartsWith("---"))
                        {
                            // Apply green to lines that start with "---"
                            yield return(new TagSnapshotRange <IClassificationTag>(
                                             new TextSnapshotRange(snapshotRange.Snapshot, new TextRange(lineStartOffset, reader.Offset)),
                                             new StyleRegistryClassificationTag(commentCT, styleRegistry)
                                             ));
                        }
                        else if (lineStartText.StartsWith("Error:"))
                        {
                            // Apply maroon to lines that start with "Error:"
                            yield return(new TagSnapshotRange <IClassificationTag>(
                                             new TextSnapshotRange(snapshotRange.Snapshot, new TextRange(lineStartOffset, reader.Offset)),
                                             new StyleRegistryClassificationTag(errorCT, styleRegistry)
                                             ));
                        }

                        // Consume the newline
                        reader.GoToNextSnapshotLineStart();
                    }
                }
            }
        }
        /// <summary>
        /// Requests that an <see cref="IQuickInfoSession"/> be opened for the specified <see cref="IEditorView"/>.
        /// </summary>
        /// <param name="view">The <see cref="IEditorView"/> that will host the session.</param>
        /// <param name="context">A context object returned by <see cref="GetContext"/>.</param>
        /// <returns>
        /// <c>true</c> if a session was opened; otherwise, <c>false</c>.
        /// </returns>
        protected override bool RequestSession(IEditorView view, object context)
        {
            // Create a session and assign a context that can be used to identify it
            QuickInfoSession session = new QuickInfoSession();

            session.Context = context;

            TextRangeContext textRangeContext = context as TextRangeContext;

            if (textRangeContext != null)
            {
                // Get a reader initialized to the offset
                ITextSnapshotReader reader = view.CurrentSnapshot.GetReader(textRangeContext.Range.StartOffset);
                IToken token = reader.Token;
                if (token != null)
                {
                    // Create some marked-up content indicating the token at the offset and the line it's on
                    session.Content = new HtmlContentProvider(
                        String.Format("Target word: <b>{0}</b><br/>Token: <b>{1}</b><br/><span style=\"color: Green;\">Found on line {2}.</span>",
                                      HtmlContentProvider.Escape(view.CurrentSnapshot.GetSubstring(textRangeContext.Range)),
                                      token.Key,
                                      view.OffsetToPosition(textRangeContext.Range.StartOffset).Line + 1), view.DefaultBackgroundColor).GetContent();

                    // Open the session
                    session.Open(view, textRangeContext.Range);
                    return(true);
                }
            }
            else
            {
                LineNumberMarginContext marginContext = context as LineNumberMarginContext;
                if (marginContext != null)
                {
                    // Create some marked-up content indicating the line number
                    session.Content = new HtmlContentProvider(String.Format("Line number: <b>{0}</b>", marginContext.LineIndex + 1), view.DefaultBackgroundColor).GetContent();

                    // Get the margin
                    IEditorViewMargin margin = view.Margins[EditorViewMarginKeys.LineNumber];

                    // Get the view line that contains the line
                    ITextViewLine viewLine = view.GetViewLine(new TextPosition(marginContext.LineIndex, 0));
                    if ((margin != null) && (viewLine != null))
                    {
                        // Get line bounds relative to the margin
                        Rect bounds = view.TransformFromTextArea(viewLine.Bounds);
                        bounds.X     = 0;
                        bounds.Width = margin.VisualElement.RenderSize.Width;

                        // Open the session
                        session.Open(view, PlacementMode.Bottom, view.VisualElement, bounds);
                        return(true);
                    }
                }
            }
            return(false);
        }
Beispiel #4
0
        /////////////////////////////////////////////////////////////////////////////////////////////////////
        // OBJECT
        /////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Initializes an instance of the <c>MainControl</c> class.
        /// </summary>
        public MainControl()
        {
            InitializeComponent();

            // Load a language from a language definition
            editor.Document.Language = ActiproSoftware.ProductSamples.SyntaxEditorSamples.Common.SyntaxEditorHelper.LoadLanguageDefinitionFromResourceStream("CSharp.langdef");

            // Create a reader
            reader = editor.Document.CurrentSnapshot.GetReader(0);
        }
        /////////////////////////////////////////////////////////////////////////////////////////////////////
        // PUBLIC PROCEDURES
        /////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Performs a parsing operation using the parameters specified in the supplied <see cref="IParseRequest"/>
        /// and returns the resulting parse data.
        /// </summary>
        /// <param name="request">The <see cref="IParseRequest"/> that contains data about the requested parsing operation.</param>
        /// <returns>An <see cref="IParseData"/> that is the result of the parsing operation.</returns>
        /// <remarks>
        /// A <see cref="IParseRequestDispatcher"/> typically calls this method when a queued parse request is ready to be processed.
        /// </remarks>
        public override IParseData Parse(IParseRequest request)
        {
            SimpleParseData parseData = new SimpleParseData();

            //
            // NOTE: Make sure that you've set up an ambient parse request dispatcher for your application
            //   (see documentation on 'Parse Requests and Dispatchers') so that this parser is called in
            //   a worker thread as the editor is updated
            //

            // Most parsers will use the request.TextBufferReader property in some fashion to scan through
            //   text and not a snapshot directly... in this basic sample though, we're going to use the
            //   tokenization provided by the snapshot's reader so we can only proceed if there is a
            //   snapshot passed to us
            if (request.Snapshot != null)
            {
                ITextSnapshotReader reader = request.Snapshot.GetReader(0);

                bool isFunctionStart = false;
                while (!reader.IsAtSnapshotEnd)
                {
                    IToken token = reader.ReadToken();
                    if (token != null)
                    {
                        switch (token.Key)
                        {
                        case "Keyword":
                            // If a function token, mark that this is a function start... the next identifier should be the function name
                            isFunctionStart = (reader.Snapshot.GetSubstring(token.TextRange) == "function");
                            break;

                        case "Identifier":
                            // If this is the function name...
                            if (isFunctionStart)
                            {
                                parseData.Functions.Add(new TextSnapshotRange(reader.Snapshot, token.TextRange));
                                isFunctionStart = false;
                            }
                            break;

                        case "Whitespace":
                            // Ignore
                            break;

                        default:
                            // Flag as no longer in a function start
                            isFunctionStart = false;
                            break;
                        }
                    }
                }
            }

            return(parseData);
        }
Beispiel #6
0
        private string ReadArrayIndexExpression(ITextSnapshotReader reader)
        {
            // we're looking for an expression of the form [123] or [myVariable]
            // if we don't find one, return false.
            string indexValue = null;

            var token = reader.ReadTokenReverse();

            while (token != null)
            {
                var text = reader.PeekText(token.Length);

                if (token.Key == "Identifier" && indexValue == null)
                {
                    // substitute 0 for the name of the variable to give us
                    // the best chance of matching something when we look up the path in a document
                    indexValue = "0";
                }
                else if ((token.Key == "IntegerNumber") && indexValue == null)
                {
                    indexValue = text;
                }
                else if (token.Key == "Whitespace")
                {
                    // skip it
                }
                else if (token.Key == "OpenSquareBrace")
                {
                    if (indexValue == null)
                    {
                        // we didn't find a properly formed (and simple) index expression
                        // before hitting the square brace
                        return(null);
                    }
                    else
                    {
                        break;
                    }
                }

                token = reader.ReadTokenReverse();
            }

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

            return("[" + indexValue + "]");
        }
Beispiel #7
0
        public void OnEditorDocumentTextChanged(object sender, EditorSnapshotChangedEventArgs e)
        {
            string typedText = e.TypedText;

            if (typedText != null && typedText == ".")
            {
                ITextSnapshotReader reader = this.nitriqSyntaxEditor_0.ActiveView.GetReader();
                reader.ReadCharacterReverseThrough('.');
                IToken token = reader.ReadTokenReverse();
                if (token != null)
                {
                    this.method_2(false, reader.TokenText);
                }
            }
        }
        private static string FindPrecedingField(ITextSnapshotReader reader)
        {
            while (true)
            {
                var token = reader.ReadTokenReverse();

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

                if (token.Key == "Field")
                {
                    return(GetFieldName(reader.PeekText(token.Length)));
                }
            }
        }
        public TextRange Parse(TextSnapshotRange snapshotRange, ActiproLex.ILexerTarget parseTarget)
        {
            //System.Diagnostics.Debug.WriteLine("LexParse " + snapshotRange.ToString());
            int index = snapshotRange.StartOffset;
            int ix    = index;

            parseTarget.OnPreParse(ref ix);

            if (parseTarget.HasInitialContext)
            {
            }
            else
            {
                int l = snapshotRange.EndOffset - index;
                ITextSnapshotReader reader = snapshotRange.Snapshot.GetReader(index);
                if (reader.Offset != index)
                {
                    throw new System.Exception("What??!!?");
                }
                StringBuilder text = new StringBuilder();
                var           read = reader.ReadText(l);
                text.Append(read);
                //System.Diagnostics.Debug.WriteLine($"Parse read {read.Length} chars: {text.ToString()}");

                var lexer  = new SBLexer(new Antlr.AntlrInputStream(text.ToString()));
                var tokens = new Antlr.CommonTokenStream(lexer);
                tokens.Fill();
                foreach (var token in tokens.GetTokens())
                {
                    if (token.Type >= 0)
                    {
                        parseTarget.OnTokenParsed(new SyntaxEditorAntlrToken(token, index, snapshotRange.StartLine.Index), null);
                    }
                }
            }

            parseTarget.OnPostParse(snapshotRange.EndOffset);

            return(snapshotRange.TextRange);
        }
Beispiel #10
0
        private string DetermineFullMemberExpression(string tokenText, ITextSnapshotReader reader)
        {
            var sb    = new StringBuilder(tokenText);
            var token = reader.ReadTokenReverse();

            while (token != null)
            {
                var text = reader.PeekText(token.Length);

                if (token.Key == "Identifier" || (token.Key == "Punctuation" && text == ".") || (token.Key == "Keyword" && text == "this"))
                {
                    sb.Insert(0, text);
                }
                else if (token.Key == "CloseSquareBrace")
                {
                    var indexExpression = ReadArrayIndexExpression(reader);
                    if (indexExpression == null)
                    {
                        // we're not going to be able to make sense
                        // of the rest of the expression, so bail out.
                        break;
                    }

                    sb.Insert(0, indexExpression);
                }
                else if (token.Key == "Whitespace")
                {
                    // skip it
                }
                else
                {
                    break;
                }

                token = reader.ReadTokenReverse();
            }

            return(sb.ToString());
        }
        /// <summary>
        /// Occurs when the <c>SyntaxEditor.ViewSelectionChanged</c> event occurs.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">An <see cref="EditorViewSelectionEventArgs"/> that contains the event data.</param>
        private void OnSyntaxEditorViewSelectionChanged(object sender, EditorViewSelectionEventArgs e)
        {
            // Quit if this event is not for the active view
            if (!e.View.IsActive)
            {
                return;
            }

            // Update line, col, and character display
            linePanel.Text      = String.Format("Ln {0}", e.CaretPosition.DisplayLine);
            columnPanel.Text    = String.Format("Col {0}", e.CaretDisplayCharacterColumn);
            characterPanel.Text = String.Format("Ch {0}", e.CaretPosition.DisplayCharacter);

            // If token info should be displayed in the statusbar...
            if (toggleTokenInfoMenuItem.IsChecked)
            {
                // Get a snapshot reader
                ITextSnapshotReader reader = e.View.CurrentSnapshot.GetReader(e.View.Selection.EndOffset);
                IToken token = reader.Token;
                if (token != null)
                {
                    IMergableToken mergableToken = token as IMergableToken;
                    if (mergableToken != null)
                    {
                        tokenPanel.Content = String.Format("{0} / {1} / {2}{3}",
                                                           mergableToken.Lexer.Key, mergableToken.LexicalState.Key,
                                                           token.Key, (e.View.Selection.EndOffset == token.StartOffset ? "*" : String.Empty));
                    }
                    else
                    {
                        tokenPanel.Content = String.Format("{0} / {1}{2}", e.View.SyntaxEditor.Document.Language.Key,
                                                           token.Key, (e.View.Selection.EndOffset == token.StartOffset ? "*" : String.Empty));
                    }
                    return;
                }
            }
            tokenPanel.Content = null;
        }
Beispiel #12
0
        /////////////////////////////////////////////////////////////////////////////////////////////////////
        // PUBLIC PROCEDURES
        /////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Returns the ideal amount of indent, always in columns, for the line containing the snapshot offset.
        /// </summary>
        /// <param name="snapshotOffset">The <see cref="TextSnapshotOffset"/> whose line should be examined.</param>
        /// <param name="defaultAmount">The default indent amount, which is the amount used in <c>Block</c> mode.</param>
        /// <returns>The ideal amount of indent, always in columns, for the line containing the snapshot offset.</returns>
        /// <remarks>
        /// This method is called when the <see cref="IndentMode"/> is <c>Smart</c>.
        /// The containing <see cref="ITextDocument"/> is accessible via the snapshot range's <see cref="ITextSnapshot"/>.
        /// </remarks>
        public override int GetIndentAmount(TextSnapshotOffset snapshotOffset, int defaultAmount)
        {
            // If the snapshot offset is deleted, return the default amount
            if (snapshotOffset.IsDeleted)
            {
                return(defaultAmount);
            }

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

            if (document == null)
            {
                return(defaultAmount);
            }

            // Get the tab size
            int tabSize = document.TabSize;

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

            if (reader == null)
            {
                return(defaultAmount);
            }

            // Get the indentation base line index
            int indentationBaseLineIndex = Math.Max(0, snapshotOffset.Line.Index - 1);

            // Ensure we are at the start of the current token
            if (!reader.IsAtTokenStart)
            {
                reader.GoToCurrentTokenStart();
            }

            // If finding indentation for an open curly brace, move back a token
            bool isForOpenCurlyBrace = (reader.Token.Id == SimpleTokenId.OpenCurlyBrace);

            if (isForOpenCurlyBrace)
            {
                reader.GoToPreviousToken();
            }

            // Loop backwards
            bool keywordFoundAfterStatement = false;
            bool statementFound             = false;

            while (true)
            {
                switch (reader.Token.Id)
                {
                case SimpleTokenId.OpenCurlyBrace: {
                    // Indent from this open curly brace
                    return(reader.SnapshotLine.IndentAmount + tabSize);
                }

                case SimpleTokenId.CloseCurlyBrace:
                    // Return the indent level of the matching {
                    reader.GoToPreviousMatchingTokenById(SimpleTokenId.CloseCurlyBrace, SimpleTokenId.OpenCurlyBrace);
                    return(reader.SnapshotLine.IndentAmount);

                case SimpleTokenId.CloseParenthesis:
                case SimpleTokenId.SemiColon:
                    if (!statementFound)
                    {
                        // Flag that a statement was found
                        statementFound = true;

                        if (!keywordFoundAfterStatement)
                        {
                            // Use this line as indentation base
                            indentationBaseLineIndex = reader.SnapshotLine.Index;
                        }
                    }
                    break;

                default:
                    if ((!keywordFoundAfterStatement) && (!statementFound) && (reader.Offset < snapshotOffset.Offset) &&
                        (reader.Token.Id >= SimpleTokenId.Function) && (reader.Token.Id <= SimpleTokenId.Var))
                    {
                        // Flag that a keyword was found
                        keywordFoundAfterStatement = true;

                        // Use this line as indentation base
                        indentationBaseLineIndex = reader.SnapshotLine.Index;
                    }
                    break;
                }

                // Go to the previous token
                if (!reader.GoToPreviousToken())
                {
                    break;
                }
            }

            // Indent a level if on the statement after the keyword
            return(reader.Snapshot.Lines[indentationBaseLineIndex].IndentAmount + (keywordFoundAfterStatement && isForOpenCurlyBrace ? tabSize : 0));
        }
 public SyntaxEditorAntlrStream(ITextSnapshotReader reader)
 {
     m_reader = reader;
 }
Beispiel #14
0
        /// <summary>
        /// Updates the hit test info.
        /// </summary>
        /// <param name="result">The hit test result.</param>
        private void UpdateHitTestInfo(IHitTestResult result)
        {
            StringBuilder text = new StringBuilder();

            if (result != null)
            {
                text.AppendFormat("Snapshot version {0}{1}", result.Snapshot.Version.Number, Environment.NewLine);

                if (result.View != null)
                {
                    text.AppendFormat("Over '{0}' view{1}", this.GetPlacementName(result.View), Environment.NewLine);
                }

                switch (result.Type)
                {
                case HitTestResultType.Splitter: {
                    EditorViewSplitter splitter = result.VisualElement as EditorViewSplitter;
                    if (splitter != null)
                    {
                        text.AppendLine("Over view splitter");
                    }
                    break;
                }

                case HitTestResultType.ViewMargin:
                    text.AppendFormat("Over '{0}' margin{1}", result.ViewMargin.Key, Environment.NewLine);
                    text.AppendFormat("Closest text position is ({0},{1}){2}", result.Position.Line, result.Position.Character, Environment.NewLine);
                    break;

                case HitTestResultType.ViewScrollBar: {
                    System.Windows.Controls.Primitives.ScrollBar scrollBar = result.VisualElement as System.Windows.Controls.Primitives.ScrollBar;
                    if (scrollBar != null)
                    {
                        text.AppendFormat("Over '{0}' scrollbar{1}", scrollBar.Orientation, Environment.NewLine);
                    }
                    break;
                }

                case HitTestResultType.ViewScrollBarBlock:
                    text.AppendLine("Over scroll bar block");
                    break;

                case HitTestResultType.ViewScrollBarSplitter: {
                    ScrollBarSplitter splitter = result.VisualElement as ScrollBarSplitter;
                    if (splitter != null)
                    {
                        text.AppendLine("Over scroll bar splitter");
                    }
                    break;
                }

                case HitTestResultType.ViewScrollBarTray:
                    text.AppendLine("Over scroll bar tray (that can contain other controls like buttons)");
                    break;

                case HitTestResultType.ViewTextArea:
                    text.AppendFormat("Not directly over any view line or character{0}", Environment.NewLine);
                    text.AppendFormat("Closest text position is ({0},{1}){2}", result.Position.Line, result.Position.Character, Environment.NewLine);
                    break;

                case HitTestResultType.ViewTextAreaOverCharacter: {
                    ITextSnapshotReader reader = result.GetReader();
                    text.AppendFormat("Directly over offset {0} and text position ({1},{2}){3}", result.Offset, result.Position.Line, result.Position.Character, Environment.NewLine);
                    text.AppendFormat("Directly over character '{0}'{1}", reader.Character, Environment.NewLine);

                    IToken token = reader.Token;
                    if (token != null)
                    {
                        text.AppendFormat("Directly over token '{0}' with range ({1},{2})-({3},{4}){5}", token.Key,
                                          token.StartPosition.Line, token.StartPosition.Character,
                                          token.EndPosition.Line, token.EndPosition.Character, Environment.NewLine);
                        text.AppendFormat("Directly over token text '{0}'{1}", reader.TokenText, Environment.NewLine);
                    }
                    break;
                }

                case HitTestResultType.ViewTextAreaOverIntraTextSpacer:
                    text.AppendFormat("Over spacer '{0}' on document line {1}{2}", result.IntraTextSpacerTag, result.Position.Line, Environment.NewLine);
                    break;

                case HitTestResultType.ViewTextAreaOverLine:
                    text.AppendFormat("Over whitespace at the end of document line {0}{1}", result.Position.Line, Environment.NewLine);
                    break;

                default:
                    if (result.VisualElement != null)
                    {
                        text.AppendFormat("Over a '{0}' element{1}", result.VisualElement.GetType().FullName, Environment.NewLine);
                    }
                    else
                    {
                        text.AppendLine("No other hit test details available");
                    }
                    break;
                }
            }
            else
            {
                text.AppendLine("Not over the SyntaxEditor");
            }

            resultsTextBox.Text = text.ToString();
        }
Beispiel #15
0
        /////////////////////////////////////////////////////////////////////////////////////////////////////
        // PUBLIC PROCEDURES
        /////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Performs a parsing operation using the parameters specified in the supplied <see cref="IParseRequest"/>
        /// and returns the resulting parse data.
        /// </summary>
        /// <param name="request">The <see cref="IParseRequest"/> that contains data about the requested parsing operation.</param>
        /// <returns>An <see cref="IParseData"/> that is the result of the parsing operation.</returns>
        /// <remarks>
        /// A <see cref="IParseRequestDispatcher"/> typically calls this method when a queued parse request is ready to be processed.
        /// </remarks>
        public override IParseData Parse(IParseRequest request)
        {
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            // Create parse data
            ParentParseData parseData = new ParentParseData();

            parseData.Snapshot = request.Snapshot;

            // Initialize generated text
            StringBuilder generatedText = new StringBuilder();

            generatedText.Append("using System;\n");
            generatedText.Append("using System.Collections.Generic;\n\n");
            generatedText.Append("using System.Linq;\n\n");
            generatedText.Append("[EditorBrowsable(EditorBrowsableState.Never)]\n");
            generatedText.Append("class __Generated {\n");
            generatedText.Append("\t[EditorBrowsable(EditorBrowsableState.Never)]\n");
            generatedText.Append("\tvoid __WriteOutput() {\n");

            ITextSnapshotReader sourceReader = request.Snapshot.GetReader(0);
            int  lastDelimiterOffset         = 0;
            bool lastDelimiterWasStart       = false;

            while (!sourceReader.IsAtSnapshotEnd)
            {
                IToken token = sourceReader.ReadToken();
                if (token != null)
                {
                    switch (token.Id)
                    {
                    case ParentTokenId.ChildCodeBlockStart:
                    case ParentTokenId.ChildOutputBlockStart:
                        if (token.StartOffset - lastDelimiterOffset > 0)
                        {
                            // Append generated text
                            string text = sourceReader.Snapshot.GetSubstring(new TextRange(lastDelimiterOffset, token.StartOffset), LineTerminator.Newline);
                            generatedText.Append("\t\tResponse.Write(@\"");
                            generatedText.Append(text.Replace("\"", "\"\""));
                            generatedText.Append("\");\n");
                        }

                        // Store the last delimiter offset
                        lastDelimiterOffset   = token.EndOffset;
                        lastDelimiterWasStart = true;
                        break;

                    case ParentTokenId.ChildCodeBlockEnd:
                        if ((lastDelimiterWasStart) && (token.StartOffset - lastDelimiterOffset > 0))
                        {
                            // Get the text between the delimiters
                            string text = sourceReader.Snapshot.GetSubstring(new TextRange(lastDelimiterOffset, token.StartOffset), LineTerminator.Newline);
                            generatedText.Append("\t\t");

                            // Add a mapping
                            parseData.TextRangeMappings.Add(Tuple.Create(new TextRange(lastDelimiterOffset, token.StartOffset), TextRange.FromSpan(generatedText.Length, text.Length)));

                            // Append the text directly
                            generatedText.Append(text);
                            generatedText.Append("\n");
                        }

                        // Store the last delimiter offset
                        lastDelimiterOffset   = token.EndOffset;
                        lastDelimiterWasStart = false;
                        break;

                    case ParentTokenId.ChildOutputBlockEnd:
                        if ((lastDelimiterWasStart) && (token.StartOffset - lastDelimiterOffset > 0))
                        {
                            // Get the text between the delimiters and append a Response.Write
                            string text = sourceReader.Snapshot.GetSubstring(new TextRange(lastDelimiterOffset, token.StartOffset), LineTerminator.Newline);
                            generatedText.Append("\t\tResponse.Write(");

                            // Add a mapping
                            parseData.TextRangeMappings.Add(Tuple.Create(new TextRange(lastDelimiterOffset, token.StartOffset), TextRange.FromSpan(generatedText.Length, text.Length)));

                            // Append the text directly
                            generatedText.Append(text);
                            generatedText.Append(");\n");
                        }

                        // Store the last delimiter offset
                        lastDelimiterOffset   = token.EndOffset;
                        lastDelimiterWasStart = false;
                        break;
                    }
                }
            }

            if (lastDelimiterOffset < sourceReader.Snapshot.Length)
            {
                // Append generated text
                string text = sourceReader.Snapshot.GetSubstring(new TextRange(lastDelimiterOffset, sourceReader.Snapshot.Length), LineTerminator.Newline);
                generatedText.Append("\t\tResponse.Write(@\"");
                generatedText.Append(text.Replace("\"", "\"\""));
                generatedText.Append("\");\n");
            }

            // Store the generated text
            generatedText.Append("\t}\n");
            generatedText.Append("}\n");

            // Get parse data for the translated code
            CodeDocument generatedDocument = new CodeDocument();

            generatedDocument.Language = childLanguage;
            generatedDocument.SetText(generatedText.ToString());

            // Get a reader
            ITextBufferReader generatedReader = generatedDocument.CurrentSnapshot.GetReader(0).BufferReader;

            // Create a request
            ParseRequest generatedRequest = new ParseRequest(Guid.NewGuid().ToString(), generatedReader, childParser, generatedDocument);

            generatedRequest.Snapshot = generatedDocument.CurrentSnapshot;

            // Parse
            generatedDocument.ParseData  = childParser.Parse(generatedRequest);
            parseData.GeneratedParseData = generatedDocument.ParseData as ILLParseData;

            return(parseData);
        }
        /// <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();
                        }
                    }
                }
            }
        }
        /////////////////////////////////////////////////////////////////////////////////////////////////////
        // NON-PUBLIC PROCEDURES
        /////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Formats the specified snapshot range.
        /// </summary>
        /// <param name="change">The <see cref="ITextChange"/> to use.</param>
        /// <param name="snapshotRange">The snapshot range.</param>
        private void FormatCore(ITextChange change, TextSnapshotRange snapshotRange)
        {
            // Get the snapshot
            ITextSnapshot snapshot = snapshotRange.Snapshot;

            if (snapshot == null)
            {
                return;
            }

            // Get the snapshot reader
            ITextSnapshotReader reader = snapshot.GetReader(snapshotRange.StartOffset);

            // Optimize reader options
            reader.Options.PrimaryScanDirection         = TextScanDirection.Forward;
            reader.Options.InitialTokenLoadBufferLength = Math.Min(100000, snapshotRange.Length);
            reader.Options.DefaultTokenLoadBufferLength = reader.Options.InitialTokenLoadBufferLength;

            // Get the code document
            ICodeDocument document = snapshot.Document as ICodeDocument;

            if (document == null)
            {
                return;
            }

            // Get the tab size
            int tabSize = document.TabSize;

            // Keep track of the last non whitespace token Id
            int lastNonWhitespaceTokenId = -1;

            // Keep track of the indent level
            int indentLevel = 0;

            // Loop through the document
            while ((reader.Token != null) && (reader.Offset < snapshotRange.EndOffset))
            {
                // If the token is whitespace, delete the text
                if (reader.Token.Id == SimpleTokenId.Whitespace)
                {
                    change.DeleteText(reader.Token.TextRange);
                }
                // The token is not whitespace
                else
                {
                    // Create a variable that will contain the text to be inserted
                    string insertText = "";

                    // Determine the insertText value based on the previous non-whitespace token and the current token
                    switch (lastNonWhitespaceTokenId)
                    {
                    case SimpleTokenId.CloseCurlyBrace:
                        // If the token is a close curly brace, decrement the indent level
                        if (reader.Token.Id == SimpleTokenId.CloseCurlyBrace)
                        {
                            indentLevel = Math.Max(0, indentLevel - 1);
                            insertText  = "\n" + StringHelper.GetIndentText(document.AutoConvertTabsToSpaces, tabSize, indentLevel * tabSize);
                        }
                        else
                        {
                            // If the indent level is zero, a function declaration just finished, which means we want an extra newline
                            if (indentLevel == 0)
                            {
                                insertText = "\n\n";
                            }
                            else
                            {
                                insertText = "\n" + StringHelper.GetIndentText(document.AutoConvertTabsToSpaces, tabSize, indentLevel * tabSize);
                            }
                        }
                        break;

                    case SimpleTokenId.CloseParenthesis:
                        // If the current token is an OpenCurlyBrace, determine whether the brace goes on a new line or not
                        if (reader.Token.Id == SimpleTokenId.OpenCurlyBrace)
                        {
                            if (openingBraceOnNewLine)
                            {
                                insertText = "\n" + StringHelper.GetIndentText(document.AutoConvertTabsToSpaces, tabSize, indentLevel * tabSize);
                            }
                            else
                            {
                                insertText = " ";
                            }
                        }
                        break;

                    case SimpleTokenId.Identifier:
                    case SimpleTokenId.Number:
                        // Sometimes a space should be added after an identifier or number, sometimes not
                        if (reader.Token.Id != SimpleTokenId.SemiColon &&
                            reader.Token.Id != SimpleTokenId.CloseParenthesis &&
                            reader.Token.Id != SimpleTokenId.OpenParenthesis &&
                            reader.Token.Id != SimpleTokenId.Comma)
                        {
                            insertText = " ";
                        }
                        break;

                    case SimpleTokenId.Comma:
                    case SimpleTokenId.Function:
                    case SimpleTokenId.Return:
                    case SimpleTokenId.Var:
                    case SimpleTokenId.Multiplication:
                    case SimpleTokenId.Equality:
                    case SimpleTokenId.Inequality:
                    case SimpleTokenId.Assignment:
                    case SimpleTokenId.Subtraction:
                    case SimpleTokenId.Addition:
                    case SimpleTokenId.Division:
                        // Keywords and operators get a space
                        insertText = " ";
                        break;

                    case SimpleTokenId.MultiLineCommentText:
                        // Multiline comments get an extra newline
                        insertText = "\n\n" + StringHelper.GetIndentText(document.AutoConvertTabsToSpaces, tabSize, indentLevel * tabSize);
                        break;

                    case SimpleTokenId.OpenCurlyBrace:
                        // If the token is not a close curly brace, increment the indent level
                        if (reader.Token.Id != SimpleTokenId.CloseCurlyBrace)
                        {
                            indentLevel++;
                        }
                        insertText = "\n" + StringHelper.GetIndentText(document.AutoConvertTabsToSpaces, tabSize, indentLevel * tabSize);
                        break;

                    case SimpleTokenId.SemiColon:
                    case SimpleTokenId.SingleLineCommentText:
                        // If the token is a close curly brace, decrement the indent level
                        if (reader.Token.Id == SimpleTokenId.CloseCurlyBrace)
                        {
                            indentLevel = Math.Max(0, indentLevel - 1);
                        }
                        insertText = "\n" + StringHelper.GetIndentText(document.AutoConvertTabsToSpaces, tabSize, indentLevel * tabSize);
                        break;
                    }
                    // Insert the replacement text
                    change.InsertText(reader.Token.StartOffset, insertText);

                    // Update the last non-whitespace token Id
                    lastNonWhitespaceTokenId = reader.Token.Id;
                }
                // Go to the next token
                reader.GoToNextToken();
            }

            // If the entire document was formatted, add a newline to the end
            if ((snapshot.SnapshotRange.StartOffset == snapshotRange.StartOffset) &&
                (snapshot.SnapshotRange.EndOffset == snapshotRange.EndOffset))
            {
                change.InsertText(snapshotRange.EndOffset, "\n");
            }
        }
		private string DetermineFullMemberExpression(string tokenText, ITextSnapshotReader reader)
		{
			var sb = new StringBuilder(tokenText);
			var token = reader.ReadTokenReverse();
			while (token != null)
			{
				var text = reader.PeekText(token.Length);

				if (token.Key == "Identifier" || (token.Key == "Punctuation" && text == ".") || (token.Key == "Keyword" && text == "this"))
				{
					sb.Insert(0, text);
				}
				else if (token.Key == "CloseSquareBrace")
				{
					var indexExpression = ReadArrayIndexExpression(reader);
					if (indexExpression == null)
					{
						// we're not going to be able to make sense
						// of the rest of the expression, so bail out.
						break;
					}

					sb.Insert(0, indexExpression);
				}
				else if (token.Key == "Whitespace")
				{
					// skip it
				}
				else
				{
					break;
				}

				token = reader.ReadTokenReverse();
			}

			return sb.ToString();
		}
        /// <summary>
        /// Initializes a new instance of the <c>JavascriptOutliningSource</c> class.
        /// </summary>
        /// <param name="snapshot">The <see cref="ITextSnapshot"/> to use for this outlining source.</param>
        public JavascriptOutliningSource(ITextSnapshot snapshot) : base(snapshot)
        {
            int    curlyBraceStartOffset = -1;
            int    curlyBraceLevel       = 0;
            int    commentStartOffset    = -1;
            IToken token;

            // Get a text snapshot reader so that we can read tokens
            ITextSnapshotReader reader = snapshot.GetReader(0);

            // Read through the entire snapshot
            while (!reader.IsAtSnapshotEnd)
            {
                // Get the next token
                token = reader.ReadToken();
                if (token != null)
                {
                    switch (token.Key)
                    {
                    case "MultiLineCommentStartDelimiter":
                        // A multi-line comment is starting... save its start offset
                        if (commentStartOffset == -1)
                        {
                            commentStartOffset = token.StartOffset;
                        }
                        break;

                    case "MultiLineCommentEndDelimiter":
                        // A multi-line comment is ending... add its range to the outlining source
                        if (commentStartOffset != -1)
                        {
                            this.AddNode(new TextRange(commentStartOffset, token.EndOffset), multiLineCommentDefinition);
                            commentStartOffset = -1;
                        }
                        break;

                    case "OpenCurlyBrace":
                        // An open curly brace... save its start offset if it's a top-level brace
                        if (curlyBraceLevel++ == 0)
                        {
                            if (curlyBraceStartOffset == -1)
                            {
                                curlyBraceStartOffset = token.StartOffset;
                            }
                        }
                        break;

                    case "CloseCurlyBrace":
                        // A close curly brace... add its range to the outlining source if it's a top-level brace
                        if (curlyBraceLevel > 0)
                        {
                            curlyBraceLevel--;
                            if (curlyBraceLevel == 0)
                            {
                                this.AddNode(new TextRange(curlyBraceStartOffset, token.EndOffset), curlyBraceDefinition);
                                curlyBraceStartOffset = -1;
                            }
                        }
                        break;
                    }
                }
                else
                {
                    break;
                }
            }

            // If there are any "open" nodes (never found a matching end), add them too
            if (commentStartOffset != -1)
            {
                this.AddOpenNode(commentStartOffset, multiLineCommentDefinition);
            }
            if (curlyBraceStartOffset != -1)
            {
                this.AddOpenNode(curlyBraceStartOffset, curlyBraceDefinition);
            }
        }
		private string ReadArrayIndexExpression(ITextSnapshotReader reader)
		{
			// we're looking for an expression of the form [123] or [myVariable]
			// if we don't find one, return false.
			string indexValue = null;

			var token = reader.ReadTokenReverse();
			while (token != null)
			{
				var text = reader.PeekText(token.Length);

				if (token.Key == "Identifier" && indexValue == null)
				{
					// substitute 0 for the name of the variable to give us 
					// the best chance of matching something when we look up the path in a document
					indexValue = "0";
				}
				else if ((token.Key == "IntegerNumber") && indexValue == null)
				{
					indexValue = text;
				}
				else if (token.Key == "Whitespace")
				{
					// skip it
				}
				else if (token.Key == "OpenSquareBrace")
				{
					if (indexValue == null)
					{
						// we didn't find a properly formed (and simple) index expression
						// before hitting the square brace
						return null;
					}

					break;
				}

				token = reader.ReadTokenReverse();
			}

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

			return "[" + indexValue + "]";
		}
	    private static string FindPrecedingField(ITextSnapshotReader reader)
	    {
	        while (true)
	        {
                var token = reader.ReadTokenReverse();

                if (token == null)
                    return null;
                
                if (token.Key == "Field")
                    return GetFieldName(reader.PeekText(token.Length));
	        }
	    }