示例#1
0
文件: RParser.cs 项目: Microsoft/RTVS
        public static AstRoot Parse(ITextProvider textProvider, ITextRange range, IExpressionTermFilter filter) {
            var tokenizer = new RTokenizer(separateComments: true);

            IReadOnlyTextRangeCollection<RToken> tokens = tokenizer.Tokenize(textProvider, range.Start, range.Length);
            TokenStream<RToken> tokenStream = new TokenStream<RToken>(tokens, new RToken(RTokenType.EndOfStream, TextRange.EmptyRange));

            return Parse(textProvider, range, tokenStream, tokenizer.CommentTokens, filter);
        }
示例#2
0
        public override CommandResult Invoke(Guid group, int id, object inputArg, ref object outputArg) {
            string originalText = TargetBuffer.CurrentSnapshot.GetText();
            string formattedText = string.Empty;
            var formatter = new RFormatter(REditorSettings.FormatOptions);

            try {
                formattedText = formatter.Format(originalText);
            } catch (Exception ex) {
                Debug.Assert(false, "Formatter exception: ", ex.Message);
            }

            if (!string.IsNullOrEmpty(formattedText) && !string.Equals(formattedText, originalText, StringComparison.Ordinal)) {
                var selectionTracker = new RSelectionTracker(TextView, TargetBuffer, new TextRange(0, TargetBuffer.CurrentSnapshot.Length));
                selectionTracker.StartTracking(automaticTracking: false);

                try {
                    using (var massiveChange = new MassiveChange(TextView, TargetBuffer, EditorShell, Resources.FormatDocument)) {
                        IREditorDocument document = REditorDocument.TryFromTextBuffer(TargetBuffer);
                        if (document != null) {
                            document.EditorTree.Invalidate();
                        }

                        var caretPosition = TextView.Caret.Position.BufferPosition;
                        var viewPortLeft = TextView.ViewportLeft;

                        RTokenizer tokenizer = new RTokenizer();
                        string oldText = TargetBuffer.CurrentSnapshot.GetText();
                        IReadOnlyTextRangeCollection<RToken> oldTokens = tokenizer.Tokenize(oldText);
                        IReadOnlyTextRangeCollection<RToken> newTokens = tokenizer.Tokenize(formattedText);

#if DEBUG
                        //if (oldTokens.Count != newTokens.Count) {
                        //    for (int i = 0; i < Math.Min(oldTokens.Count, newTokens.Count); i++) {
                        //        if (oldTokens[i].TokenType != newTokens[i].TokenType) {
                        //            Debug.Assert(false, Invariant($"Token type difference at {i}"));
                        //            break;
                        //        } else if (oldTokens[i].Length != newTokens[i].Length) {
                        //            Debug.Assert(false, Invariant($"token length difference at {i}"));
                        //            break;
                        //        }
                        //    }
                        //}
#endif
                        IncrementalTextChangeApplication.ApplyChangeByTokens(
                            TargetBuffer,
                            new TextStream(oldText), new TextStream(formattedText),
                            oldTokens, newTokens,
                            TextRange.FromBounds(0, oldText.Length),
                            Resources.FormatDocument, selectionTracker, EditorShell);
                    }
                } finally {
                    selectionTracker.EndTracking();
                }
                return new CommandResult(CommandStatus.Supported, 0);
            }
            return CommandResult.NotSupported;
        }
示例#3
0
 public static string BacktickName(string name) {
     if (!string.IsNullOrEmpty(name)) {
         var t = new RTokenizer();
         var tokens = t.Tokenize(name);
         if (tokens.Count > 1) {
             return Invariant($"`{name}`");
         }
     }
     return name;
 }
示例#4
0
        /// <summary>
        /// Parse text from a text provider within a given range
        /// </summary>
        /// <param name="textProvider">Text provider</param>
        /// <param name="range">Range to parse</param>
        public static AstRoot Parse(ITextProvider textProvider, ITextRange range) {
            var tokenizer = new RTokenizer(separateComments: true);

            IReadOnlyTextRangeCollection<RToken> tokens = tokenizer.Tokenize(textProvider, range.Start, range.Length);
            TokenStream<RToken> tokenStream = new TokenStream<RToken>(tokens, new RToken(RTokenType.EndOfStream, TextRange.EmptyRange));

            ParseContext context = new ParseContext(textProvider, range, tokenStream, tokenizer.CommentTokens);

            context.AstRoot.Parse(context, context.AstRoot);
            context.AstRoot.Errors = new TextRangeCollection<IParseError>(context.Errors);

            return context.AstRoot;
        }
示例#5
0
        public static IReadOnlyList <ISignatureInfo> ParseSignatures(string usageContent, IReadOnlyDictionary <string, string> argumentsDescriptions = null)
        {
            // RD signature text may contain \dots sequence  which denotes ellipsis.
            // R parser does not know  about it and hence we will replace \dots by ...
            // Also, signatures may contain S3 method info like
            // '\method{as.matrix}{data.frame}(x, rownames.force = NA, \dots)'
            // which we need to filter out since they are irrelevant to intellisense.

            List <ISignatureInfo> signatures = new List <ISignatureInfo>();

            usageContent = usageContent.Replace(@"\dots", "...");

            RTokenizer tokenizer = new RTokenizer(separateComments: true);
            IReadOnlyTextRangeCollection <RToken> collection = tokenizer.Tokenize(usageContent);
            ITextProvider        textProvider = new TextStream(usageContent);
            TokenStream <RToken> tokens       = new TokenStream <RToken>(collection, RToken.EndOfStreamToken);

            var parseContext = new ParseContext(textProvider,
                                                TextRange.FromBounds(tokens.CurrentToken.Start, textProvider.Length),
                                                tokens, tokenizer.CommentTokens);

            while (!tokens.IsEndOfStream())
            {
                // Filter out '\method{...}{}(signature)
                if (tokens.CurrentToken.TokenType == RTokenType.OpenCurlyBrace)
                {
                    // Check if { is preceded by \method
                }

                if (tokens.CurrentToken.TokenType != RTokenType.Identifier)
                {
                    break;
                }

                string functionName = textProvider.GetText(tokens.CurrentToken);
                tokens.MoveToNextToken();

                ISignatureInfo info = ParseSignature(functionName, parseContext, argumentsDescriptions);
                if (info != null)
                {
                    signatures.Add(info);
                }
            }

            return(signatures);
        }
示例#6
0
        /// <summary>
        /// Extracts complete variable name under the caret. In '`abc`$`def` returns
        /// the complete expression rather than its parts. Typically used to get data
        /// for completion of variable members as in when user typed 'abc$def$'
        /// Since method does not perform semantic analysis, it does not guaratee
        /// syntactically correct expression, it may return 'a$$b'.
        /// </summary>
        public static string GetVariableNameBeforeCaret(this ITextView textView)
        {
            if (textView.Caret.InVirtualSpace)
            {
                return(string.Empty);
            }
            var position = textView.Caret.Position.BufferPosition;
            var line     = position.GetContainingLine();

            // For performance reasons we won't be using AST here
            // since during completion it is most probably damaged.
            string lineText  = line.GetText();
            var    tokenizer = new RTokenizer();
            var    tokens    = tokenizer.Tokenize(lineText);

            var tokenPosition = position - line.Start;
            var index         = tokens.GetFirstItemBeforePosition(tokenPosition);

            // Preceding token must be right next to caret position
            if (index < 0 || tokens[index].End < tokenPosition || !IsVariableNameToken(lineText, tokens[index]))
            {
                return(string.Empty);
            }

            if (index == 0)
            {
                return(IsVariableNameToken(lineText, tokens[0]) ? lineText.Substring(tokens[0].Start, tokens[0].Length) : string.Empty);
            }

            // Walk back through tokens allowing identifier and specific
            // operator tokens. No whitespace is permitted between tokens.
            // We have at least 2 tokens here.
            int i = index;

            for (; i > 0; i--)
            {
                var precedingToken = tokens[i - 1];
                var currentToken   = tokens[i];
                if (precedingToken.End < currentToken.Start || !IsVariableNameToken(lineText, precedingToken))
                {
                    break;
                }
            }

            return(lineText.Substring(tokens[i].Start, tokens[index].End - tokens[i].Start));
        }
示例#7
0
        /// <summary>
        /// Called before character type is passed down to the core editor
        /// along the controll chain. Gives language-specific controller
        /// a chance to initiate different action and potentially 'eat'
        /// the character. For example, in R typing 'abc[TAB] should bring
        /// up intellisense list rather than actually insert the tab character.
        /// </summary>
        /// <returns>
        /// True if character was handled and should not be
        /// passed down to core editor or false otherwise.
        /// </returns>
        public override bool OnPreTypeChar(char typedCharacter)
        {
            // Allow tab to bring intellisense if
            //  a) REditorSettings.ShowCompletionOnTab true
            //  b) Position is at the end of a string so we bring completion for files
            //  c) There is no selection
            if (typedCharacter == '\t' && !HasActiveCompletionSession && TextView.Selection.StreamSelectionSpan.Length == 0)
            {
                // if previous character is identifier character, bring completion list
                SnapshotPoint?position = REditorDocument.MapCaretPositionFromView(TextView);
                if (position.HasValue)
                {
                    int pos = position.Value;
                    var doc = REditorDocument.FromTextBuffer(position.Value.Snapshot.TextBuffer);
                    if (!doc.IsPositionInComment(pos))
                    {
                        if (pos > 0 && pos <= position.Value.Snapshot.Length)
                        {
                            bool endOfIdentifier = RTokenizer.IsIdentifierCharacter(position.Value.Snapshot[pos - 1]);
                            bool showCompletion  = endOfIdentifier && _settings.ShowCompletionOnTab;
                            if (!showCompletion)
                            {
                                var    document = REditorDocument.FromTextBuffer(position.Value.Snapshot.TextBuffer);
                                string directory;
                                showCompletion = RCompletionEngine.CanShowFileCompletion(document.EditorTree.AstRoot, pos, out directory);
                            }
                            if (showCompletion)
                            {
                                ShowCompletion(autoShownCompletion: false);
                                return(true); // eat the character
                            }
                        }
                    }
                }
            }
            else if (typedCharacter == '#')
            {
                if (TryInsertRoxygenBlock())
                {
                    return(true);
                }
            }

            return(base.OnPreTypeChar(typedCharacter));
        }
示例#8
0
        /// <summary>
        /// Calculates span in the text buffer that contains data
        /// applicable to the current completion session. A tracking
        /// span will be created over it and editor will grow and shrink
        /// tracking span as user types and filter completion session
        /// based on the data inside the tracking span.
        /// </summary>
        private Span GetApplicableSpan(int position, ICompletionSession session)
        {
            var selectedSpans = session.TextView.Selection.SelectedSpans;

            if (selectedSpans.Count == 1 && selectedSpans[0].Span.Length > 0)
            {
                return(selectedSpans[0].Span);
            }

            ITextSnapshot     snapshot = _textBuffer.CurrentSnapshot;
            ITextSnapshotLine line     = snapshot.GetLineFromPosition(position);
            string            lineText = snapshot.GetText(line.Start, line.Length);
            int linePosition           = position - line.Start;

            int start = 0;
            int end   = line.Length;

            for (int i = linePosition - 1; i >= 0; i--)
            {
                char ch = lineText[i];
                if (!RTokenizer.IsIdentifierCharacter(ch))
                {
                    start = i + 1;
                    break;
                }
            }

            for (int i = linePosition; i < lineText.Length; i++)
            {
                char ch = lineText[i];
                if (!RTokenizer.IsIdentifierCharacter(ch))
                {
                    end = i;
                    break;
                }
            }

            if (start < end)
            {
                return(new Span(start + line.Start, end - start));
            }

            return(new Span(position, 0));
        }
示例#9
0
        public static void TokenizeFileImplementation(CoreTestFilesFixture fixture, string name) {
            string testFile = fixture.GetDestinationPath(name);
            string baselineFile = testFile + ".tokens";
            string text = fixture.LoadDestinationFile(name);

            ITextProvider textProvider = new TextStream(text);
            var tokenizer = new RTokenizer();

            var tokens = tokenizer.Tokenize(textProvider, 0, textProvider.Length);
            string actual = DebugWriter.WriteTokens<RToken, RTokenType>(tokens);

            if (_regenerateBaselineFiles) {
                // Update this to your actual enlistment if you need to update baseline
                baselineFile = Path.Combine(fixture.SourcePath, @"Tokenization\", Path.GetFileName(testFile)) + ".tokens";
                TestFiles.UpdateBaseline(baselineFile, actual);
            } else {
                TestFiles.CompareToBaseLine(baselineFile, actual);
            }
        }
示例#10
0
        public static bool FormatRangeExact(ITextView textView, ITextBuffer textBuffer,
                                            ITextRange formatRange, RFormatOptions options)
        {
            ITextSnapshot snapshot        = textBuffer.CurrentSnapshot;
            Span          spanToFormat    = new Span(formatRange.Start, formatRange.Length);
            string        spanText        = snapshot.GetText(spanToFormat.Start, spanToFormat.Length);
            string        trimmedSpanText = spanText.Trim();

            RFormatter formatter     = new RFormatter(options);
            string     formattedText = formatter.Format(trimmedSpanText);

            formattedText = formattedText.Trim(); // There may be inserted line breaks after {
            // Apply formatted text without indentation. We then will update the parse tree
            // so we can calculate proper line indents from the AST via the smart indenter.
            if (!spanText.Equals(formattedText, StringComparison.Ordinal))
            {
                // Extract existing indent before applying changes. Existing indent
                // may be used by the smart indenter for function argument lists.
                var startLine = snapshot.GetLineFromPosition(spanToFormat.Start);
                var originalIndentSizeInSpaces = IndentBuilder.TextIndentInSpaces(startLine.GetText(), options.IndentSize);

                var        selectionTracker = new RSelectionTracker(textView, textBuffer, formatRange);
                RTokenizer tokenizer        = new RTokenizer();
                IReadOnlyTextRangeCollection <RToken> oldTokens = tokenizer.Tokenize(spanText);
                IReadOnlyTextRangeCollection <RToken> newTokens = tokenizer.Tokenize(formattedText);

                IncrementalTextChangeApplication.ApplyChangeByTokens(
                    textBuffer,
                    new TextStream(spanText), new TextStream(formattedText),
                    oldTokens, newTokens,
                    formatRange,
                    Resources.AutoFormat, selectionTracker,
                    () => {
                    var ast = UpdateAst(textBuffer);
                    // Apply indentation
                    IndentLines(textView, textBuffer, new TextRange(formatRange.Start, formattedText.Length), ast, options, originalIndentSizeInSpaces);
                });

                return(true);
            }

            return(false);
        }
示例#11
0
        private int PositionFromTokens(ITextSnapshot snapshot, int itemIndex, int offset)
        {
            var lengthChange = snapshot.Length - _lengthBeforeChange;
            var tokenizer    = new RTokenizer();
            var tokens       = tokenizer.Tokenize(
                new TextProvider(snapshot), _changingRange.Start, _changingRange.Length + lengthChange, true);

            if (itemIndex >= 0 && itemIndex < tokens.Count)
            {
                var position = tokens[itemIndex].Start - offset;

                position = Math.Min(position, snapshot.Length);
                position = Math.Max(position, 0);

                return(position);
            }

            return(_changingRange.End + lengthChange);
        }
示例#12
0
        public static bool FormatRangeExact(ITextView textView, ITextBuffer textBuffer, ITextRange formatRange,
                                            AstRoot ast, RFormatOptions options,
                                            int scopeStatementPosition, bool respectUserIndent = true)
        {
            ITextSnapshot snapshot        = textBuffer.CurrentSnapshot;
            Span          spanToFormat    = new Span(formatRange.Start, formatRange.Length);
            string        spanText        = snapshot.GetText(spanToFormat.Start, spanToFormat.Length);
            string        trimmedSpanText = spanText.Trim();

            if (trimmedSpanText == "}")
            {
                // Locate opening { and its statement
                var scopeNode = ast.GetNodeOfTypeFromPosition <IAstNodeWithScope>(spanToFormat.Start);
                if (scopeNode != null)
                {
                    scopeStatementPosition = scopeNode.Start;
                }
            }

            RFormatter formatter     = new RFormatter(options);
            string     formattedText = formatter.Format(trimmedSpanText);

            formattedText = formattedText.Trim(); // there may be inserted line breaks after {
            formattedText = IndentLines(textBuffer, spanToFormat.Start, ast, formattedText, options, scopeStatementPosition, respectUserIndent);

            if (!spanText.Equals(formattedText, StringComparison.Ordinal))
            {
                var        selectionTracker = new RSelectionTracker(textView, textBuffer);
                RTokenizer tokenizer        = new RTokenizer();
                IReadOnlyTextRangeCollection <RToken> oldTokens = tokenizer.Tokenize(spanText);
                IReadOnlyTextRangeCollection <RToken> newTokens = tokenizer.Tokenize(formattedText);
                IncrementalTextChangeApplication.ApplyChangeByTokens(
                    textBuffer,
                    new TextStream(spanText), new TextStream(formattedText),
                    oldTokens, newTokens,
                    formatRange,
                    Resources.AutoFormat, selectionTracker);
                return(true);
            }

            return(false);
        }
示例#13
0
        private bool FormatRangeExact(IEditorView editorView, IEditorBuffer editorBuffer, ITextRange formatRange)
        {
            var snapshot        = editorBuffer.CurrentSnapshot;
            var spanText        = snapshot.GetText(formatRange);
            var trimmedSpanText = spanText.Trim();

            var formatter     = new RFormatter(_settings.FormatOptions);
            var formattedText = formatter.Format(trimmedSpanText);

            formattedText = formattedText.Trim(); // There may be inserted line breaks after {
            // Apply formatted text without indentation. We then will update the parse tree
            // so we can calculate proper line indents from the AST via the smart indenter.
            if (!spanText.Equals(formattedText, StringComparison.Ordinal))
            {
                // Extract existing indent before applying changes. Existing indent
                // may be used by the smart indenter for function argument lists.
                var startLine = snapshot.GetLineFromPosition(formatRange.Start);
                var originalIndentSizeInSpaces = IndentBuilder.TextIndentInSpaces(startLine.GetText(), _settings.IndentSize);

                var selectionTracker = GetSelectionTracker(editorView, editorBuffer, formatRange);
                var tokenizer        = new RTokenizer();
                var oldTokens        = tokenizer.Tokenize(spanText);
                var newTokens        = tokenizer.Tokenize(formattedText);

                var wsChangeHandler = _services.GetService <IIncrementalWhitespaceChangeHandler>();
                wsChangeHandler.ApplyChange(
                    editorBuffer,
                    new TextStream(spanText), new TextStream(formattedText),
                    oldTokens, newTokens,
                    formatRange,
                    Microsoft.R.Editor.Resources.AutoFormat, selectionTracker,
                    () => {
                    var ast = UpdateAst(editorBuffer);
                    // Apply indentation
                    IndentLines(editorBuffer, new TextRange(formatRange.Start, formattedText.Length), ast, originalIndentSizeInSpaces);
                });

                return(true);
            }

            return(false);
        }
示例#14
0
        private void TokenFromPosition(ITextSnapshot snapshot, int position, out int itemIndex, out int offset) {
            // Normally token stream does not change after formatting so we can simply rely on the fact 
            // that caret position is going to remain relative to the same token index
            itemIndex = -1;
            offset = 0;

            var tokenizer = new RTokenizer();
            IReadOnlyTextRangeCollection<RToken> tokens =
                tokenizer.Tokenize(new TextProvider(snapshot), _changingRange.Start, _changingRange.Length, true);

            // Check if position is adjacent to previous token
            int prevItemIndex = tokens.GetFirstItemBeforePosition(position);
            if (prevItemIndex >= 0 && tokens[prevItemIndex].End == position) {
                itemIndex = prevItemIndex;
                offset = -tokens[itemIndex].Length;
                return;
            }

            int nextItemIndex = tokens.GetFirstItemAfterOrAtPosition(position);
            if (nextItemIndex >= 0) {
                // If two tokens are adjacent, gravity is negative, i.e. caret travels
                // with preceding token so it won't just to aniother line if, say, 
                // formatter decides to insert a new line between tokens.

                if (nextItemIndex > 0 && tokens[nextItemIndex - 1].End == tokens[nextItemIndex].Start) {
                    nextItemIndex--;
                }

                offset = tokens[nextItemIndex].Start - position;
                itemIndex = nextItemIndex;
                return;
            }

            // We are past last token
            if (tokens.Count > 0) {
                itemIndex = tokens.Count - 1;
                offset = tokens[itemIndex].Start - position;
            } else {
                itemIndex = -1;
                offset = position;
            }
        }
示例#15
0
        public static string GetVariableName(ITextView textView, ITextSnapshot snapshot)
        {
            SnapshotPoint?pt = REditorDocument.MapCaretPositionFromView(textView);

            if (pt.HasValue && pt.Value > 0)
            {
                int i = pt.Value - 1;
                for (; i >= 0; i--)
                {
                    char ch = snapshot[i];
                    if (!RTokenizer.IsIdentifierCharacter(ch) && ch != '$' && ch != '@')
                    {
                        break;
                    }
                }

                return(snapshot.GetText(Span.FromBounds(i + 1, pt.Value)));
            }

            return(string.Empty);
        }
示例#16
0
        public static Span?GetWordSpan(ITextSnapshot snapshot, int position)
        {
            ITextSnapshotLine line = snapshot.GetLineFromPosition(position);

            if (line.Length == 0)
            {
                return(null);
            }

            var text           = line.GetText();
            var tokenizer      = new RTokenizer();
            var tokens         = tokenizer.Tokenize(text);
            var positionInLine = position - line.Start;

            var token = tokens.FirstOrDefault(t => t.Contains(positionInLine));

            if (token != null && token.TokenType != RTokenType.String && token.TokenType != RTokenType.Comment)
            {
                return(new Span(token.Start + line.Start, token.Length));
            }
            return(GetWordSpan(text, line.Start, positionInLine));
        }
示例#17
0
 private static IValidationError MultipleStatementsCheck(IAstNode node, LintOptions options, bool projectedBuffer)
 {
     if (options.MultipleStatements && node is TokenNode t && t.Token.TokenType == RTokenType.Semicolon)
     {
         var tp = node.Root.TextProvider;
         if (!tp.IsNewLineAfterPosition(node.End))
         {
             // # comment is OK but comments are not part of the AST.
             var lineBreakIndex  = tp.IndexOf('\n', node.End);
             var trailingTextEnd = lineBreakIndex >= 0 ? lineBreakIndex : tp.Length;
             var trailingText    = tp.GetText(TextRange.FromBounds(node.End, trailingTextEnd));
             var tokens          = new RTokenizer().Tokenize(trailingText);
             var offendingTokens = tokens.Where(x => x.TokenType != RTokenType.Comment);
             if (offendingTokens.Any())
             {
                 var squiggle = TextRange.FromBounds(node.End + offendingTokens.First().Start, node.End + offendingTokens.Last().End);
                 return(new ValidationWarning(squiggle, Resources.Lint_MultipleStatementsInLine, ErrorLocation.Token));
             }
         }
     }
     return(null);
 }
示例#18
0
        /// <summary>
        /// Determines if position is in object member. Typically used
        /// to suppress general intellisense when typing data member
        /// name such as 'mtcars$|'
        /// </summary>
        internal static bool IsInObjectMemberName(ITextProvider textProvider, int position)
        {
            if (position > 0)
            {
                for (int i = position - 1; i >= 0; i--)
                {
                    char ch = textProvider[i];

                    if (ch == '$' || ch == '@')
                    {
                        return(true);
                    }

                    if (!RTokenizer.IsIdentifierCharacter(ch))
                    {
                        break;
                    }
                }
            }

            return(false);
        }
示例#19
0
        public static bool FormatRangeExact(ITextView textView, ITextBuffer textBuffer, ITextRange formatRange,
                                            AstRoot ast, RFormatOptions options, 
                                            int scopeStatementPosition, bool respectUserIndent = true) {
            ITextSnapshot snapshot = textBuffer.CurrentSnapshot;
            Span spanToFormat = new Span(formatRange.Start, formatRange.Length);
            string spanText = snapshot.GetText(spanToFormat.Start, spanToFormat.Length);
            string trimmedSpanText = spanText.Trim();

            if (trimmedSpanText == "}") {
                // Locate opening { and its statement
                var scopeNode = ast.GetNodeOfTypeFromPosition<IAstNodeWithScope>(spanToFormat.Start);
                if (scopeNode != null) {
                    scopeStatementPosition = scopeNode.Start;
                }
            }

            RFormatter formatter = new RFormatter(options);
            string formattedText = formatter.Format(trimmedSpanText);

            formattedText = formattedText.Trim(); // there may be inserted line breaks after {
            formattedText = IndentLines(textBuffer, spanToFormat.Start, ast, formattedText, options, scopeStatementPosition, respectUserIndent);

            if (!spanText.Equals(formattedText, StringComparison.Ordinal)) {
                var selectionTracker = new RSelectionTracker(textView, textBuffer);
                RTokenizer tokenizer = new RTokenizer();
                IReadOnlyTextRangeCollection<RToken> oldTokens = tokenizer.Tokenize(spanText);
                IReadOnlyTextRangeCollection<RToken> newTokens = tokenizer.Tokenize(formattedText);
                IncrementalTextChangeApplication.ApplyChangeByTokens(
                    textBuffer,
                    new TextStream(spanText), new TextStream(formattedText),
                    oldTokens, newTokens,
                    formatRange,
                    Resources.AutoFormat, selectionTracker);
                return true;
            }

            return false;
        }
示例#20
0
        public static string GetItemAtPosition(ITextSnapshotLine line, int position, Func <RTokenType, bool> tokenTypeCheck, out Span span)
        {
            string lineText         = line.GetText();
            var    offset           = 0;
            var    positionInTokens = position - line.Start;
            var    tokenizer        = new RTokenizer();
            var    tokens           = tokenizer.Tokenize(lineText);
            var    tokenIndex       = tokens.GetItemContaining(positionInTokens);

            if (tokenIndex >= 0)
            {
                var token = tokens[tokenIndex];
                if (token.TokenType == RTokenType.Comment)
                {
                    // Tokenize inside comment since we do want F1 to work inside
                    // commented out code, code samples or Roxygen blocks.
                    positionInTokens -= token.Start;
                    var positionAfterHash = token.Start + 1;
                    tokens     = tokenizer.Tokenize(lineText.Substring(positionAfterHash, token.Length - 1));
                    tokenIndex = tokens.GetItemContaining(positionInTokens);
                    if (tokenIndex >= 0)
                    {
                        token  = tokens[tokenIndex];
                        offset = positionAfterHash;
                    }
                }
                if (tokenTypeCheck(token.TokenType))
                {
                    var start = token.Start + offset;
                    var end   = Math.Min(start + token.Length, line.End);
                    span = Span.FromBounds(line.Start + start, line.Start + end); // return view span
                    return(lineText.Substring(start, end - start));
                }
            }

            span = Span.FromBounds(0, 0);
            return(string.Empty);
        }
示例#21
0
        public static void TokenizeFileImplementation(CoreTestFilesFixture fixture, string name)
        {
            string testFile     = fixture.GetDestinationPath(name);
            string baselineFile = testFile + ".tokens";
            string text         = fixture.LoadDestinationFile(name);

            ITextProvider textProvider = new TextStream(text);
            var           tokenizer    = new RTokenizer();

            var    tokens = tokenizer.Tokenize(textProvider, 0, textProvider.Length);
            string actual = DebugWriter.WriteTokens <RToken, RTokenType>(tokens);

            if (_regenerateBaselineFiles)
            {
                // Update this to your actual enlistment if you need to update baseline
                baselineFile = Path.Combine(fixture.SourcePath, @"Tokenization\", Path.GetFileName(testFile)) + ".tokens";
                TestFiles.UpdateBaseline(baselineFile, actual);
            }
            else
            {
                TestFiles.CompareToBaseLine(baselineFile, actual);
            }
        }
示例#22
0
        /// <summary>
        /// Should this key press trigger a completion session?
        /// </summary>
        public override bool IsTriggerChar(char typedCharacter)
        {
            if (!HasActiveCompletionSession)
            {
                switch (typedCharacter)
                {
                case '$':
                case '@':
                    return(true);

                case ':':
                    return(RCompletionContext.IsCaretInNamespace(TextView));

                case '(':
                    return(RCompletionContext.IsCaretInLibraryStatement(TextView));

                default:
                    if (_settings.ShowCompletionOnFirstChar)
                    {
                        SnapshotPoint?position = REditorDocument.MapCaretPositionFromView(TextView);
                        if (position.HasValue)
                        {
                            int pos      = position.Value;
                            var snapshot = position.Value.Snapshot;
                            // Trigger on first character
                            if (RTokenizer.IsIdentifierCharacter(typedCharacter) && !char.IsDigit(typedCharacter))
                            {
                                // Ignore if this is not the first character
                                return(pos <= 1 || (pos > 1 && !RTokenizer.IsIdentifierCharacter(snapshot[pos - 2])));
                            }
                        }
                    }
                    break;
                }
            }
            return(false);
        }
示例#23
0
        private static CompletionList OrderList(List <ICompletionEntry> completions)
        {
            // Place 'name =' at the top prioritizing argument names
            // Place items starting with non-alpha characters like .Call and &&
            // at the end of the list.

            var orderedCompletions = new List <Completion>();
            var specialNames       = new List <Completion>();
            var generalEntries     = new List <Completion>();

            foreach (var c in completions)
            {
                if (RTokenizer.IsIdentifierCharacter(c.DisplayText[0]) && c.DisplayText.EndsWith("=", StringComparison.Ordinal))
                {
                    // Place argument completions first
                    orderedCompletions.Add(new RCompletion(c));
                }
                else if (c.DisplayText.IndexOfIgnoreCase(".rtvs") < 0)
                {
                    // Exclude .rtvs
                    if (!char.IsLetter(c.DisplayText[0]))
                    {
                        // Special names will come last
                        specialNames.Add(new RCompletion(c));
                    }
                    else
                    {
                        generalEntries.Add(new RCompletion(c));
                    }
                }
            }

            orderedCompletions.AddRange(generalEntries);
            orderedCompletions.AddRange(specialNames);

            return(new CompletionList(orderedCompletions));
        }
示例#24
0
 public override CommandResult Invoke(Guid group, int id, object inputArg, ref object outputArg) {
     if (!TextView.Caret.InVirtualSpace) {
         int caretPosition = TextView.Caret.Position.BufferPosition.Position;
         SnapshotPoint? rPosition = TextView.MapDownToR(caretPosition);
         if (rPosition.HasValue) {
             int rCaretPosition = rPosition.Value.Position;
             ITextSnapshotLine line = rPosition.Value.Snapshot.GetLineFromPosition(rCaretPosition);
             // Tokenize current line
             if (line != null) {
                 Span? spanToSelect = null;
                 var text = line.GetText();
                 var tokenizer = new RTokenizer();
                 var tokens = tokenizer.Tokenize(text);
                 var positionInLine = rCaretPosition - line.Start;
                 var token = tokens.FirstOrDefault(t => t.Contains(positionInLine));
                 if (token != null) {
                     if (token.TokenType == RTokenType.String) {
                         // Select word inside string
                         spanToSelect = GetWordSpan(text, line.Start, positionInLine);
                     } else {
                         spanToSelect = new Span(token.Start + line.Start, token.Length);
                     }
                 }
                 if (spanToSelect.HasValue && spanToSelect.Value.Length > 0) {
                     NormalizedSnapshotSpanCollection spans = TextView.BufferGraph.MapUpToBuffer(
                         new SnapshotSpan(rPosition.Value.Snapshot, spanToSelect.Value),
                         SpanTrackingMode.EdgePositive, TextView.TextBuffer);
                     if (spans.Count == 1) {
                         TextView.Selection.Select(new SnapshotSpan(TextView.TextBuffer.CurrentSnapshot, spans[0]), isReversed: false);
                         return CommandResult.Executed;
                     }
                 }
             }
         }
     }
     return CommandResult.NotSupported;
 }
示例#25
0
        public static bool FormatRangeExact(ITextView textView, ITextBuffer textBuffer, ITextRange formatRange, RFormatOptions options, IEditorShell editorShell) {
            ITextSnapshot snapshot = textBuffer.CurrentSnapshot;
            Span spanToFormat = new Span(formatRange.Start, formatRange.Length);
            string spanText = snapshot.GetText(spanToFormat.Start, spanToFormat.Length);
            string trimmedSpanText = spanText.Trim();

            RFormatter formatter = new RFormatter(options);
            string formattedText = formatter.Format(trimmedSpanText);

            formattedText = formattedText.Trim(); // There may be inserted line breaks after {
            // Apply formatted text without indentation. We then will update the parse tree 
            // so we can calculate proper line indents from the AST via the smart indenter.
            if (!spanText.Equals(formattedText, StringComparison.Ordinal)) {
                // Extract existing indent before applying changes. Existing indent
                // may be used by the smart indenter for function argument lists.
                var startLine = snapshot.GetLineFromPosition(spanToFormat.Start);
                var originalIndentSizeInSpaces = IndentBuilder.TextIndentInSpaces(startLine.GetText(), options.IndentSize);

                var selectionTracker = new RSelectionTracker(textView, textBuffer, formatRange);
                RTokenizer tokenizer = new RTokenizer();
                IReadOnlyTextRangeCollection<RToken> oldTokens = tokenizer.Tokenize(spanText);
                IReadOnlyTextRangeCollection<RToken> newTokens = tokenizer.Tokenize(formattedText);

                IncrementalTextChangeApplication.ApplyChangeByTokens(
                    textBuffer,
                    new TextStream(spanText), new TextStream(formattedText),
                    oldTokens, newTokens,
                    formatRange,
                    Resources.AutoFormat, selectionTracker, editorShell,
                    () => {
                        var ast = UpdateAst(textBuffer);
                        // Apply indentation
                        IndentLines(textView, textBuffer, new TextRange(formatRange.Start, formattedText.Length), ast, options, originalIndentSizeInSpaces);
                    });

                return true;
            }

            return false;
        }
示例#26
0
        /// <summary>
        /// Given position in the buffer tries to detemine start of the expression.
        /// </summary>
        private static int FindStartOfExpression(ITextBuffer textBuffer, int position) {
            // Go up line by line, tokenize each line
            // and check if it starts or ends with an operator
            int lineNum = textBuffer.CurrentSnapshot.GetLineNumberFromPosition(position);
            var tokenizer = new RTokenizer(separateComments: true);

            var text = textBuffer.CurrentSnapshot.GetLineFromLineNumber(lineNum).GetText();
            var tokens = tokenizer.Tokenize(text);
            bool nextLineStartsWithOperator = tokens.Count > 0 && tokens[0].TokenType == RTokenType.Operator;

            for (int i = lineNum - 1; i >= 0; i--) {
                text = textBuffer.CurrentSnapshot.GetLineFromLineNumber(i).GetText();
                tokens = tokenizer.Tokenize(text);

                if (tokens.Count > 0) {
                    if (!nextLineStartsWithOperator && tokens[tokens.Count - 1].TokenType != RTokenType.Operator) {
                        break;
                    }
                    position = tokens[0].Start;
                    nextLineStartsWithOperator = tokens[0].TokenType == RTokenType.Operator;
                }
            }

            return position;
        }
        public static IReadOnlyList<ISignatureInfo> ParseSignatures(string usageContent, IReadOnlyDictionary<string, string> argumentsDescriptions = null) {
            // RD signature text may contain \dots sequence  which denotes ellipsis.
            // R parser does not know  about it and hence we will replace \dots by ...
            // Also, signatures may contain S3 method info like 
            // '\method{as.matrix}{data.frame}(x, rownames.force = NA, \dots)'
            // which we need to filter out since they are irrelevant to intellisense.

            List<ISignatureInfo> signatures = new List<ISignatureInfo>();
            usageContent = usageContent.Replace(@"\dots", "...");

            RTokenizer tokenizer = new RTokenizer(separateComments: true);
            IReadOnlyTextRangeCollection<RToken> collection = tokenizer.Tokenize(usageContent);
            ITextProvider textProvider = new TextStream(usageContent);
            TokenStream<RToken> tokens = new TokenStream<RToken>(collection, RToken.EndOfStreamToken);

            var parseContext = new ParseContext(textProvider,
                         TextRange.FromBounds(tokens.CurrentToken.Start, textProvider.Length),
                         tokens, tokenizer.CommentTokens);

            while (!tokens.IsEndOfStream()) {
                // Filter out '\method{...}{}(signature)
                if (tokens.CurrentToken.TokenType == RTokenType.OpenCurlyBrace) {
                    // Check if { is preceded by \method
                }

                if (tokens.CurrentToken.TokenType != RTokenType.Identifier) {
                    break;
                }

                string functionName = textProvider.GetText(tokens.CurrentToken);
                tokens.MoveToNextToken();

                ISignatureInfo info = ParseSignature(functionName, parseContext, argumentsDescriptions);
                if (info != null) {
                    signatures.Add(info);
                }
            }

            return signatures;
        }
示例#28
0
        /// <summary>
        /// Provides list of completion entries for a given location in the AST.
        /// </summary>
        /// <param name="tree">Document tree</param>
        /// <param name="position">Caret position in the document</param>
        /// <param name="autoShownCompletion">True if completion is forced (like when typing Ctrl+Space)</param>
        /// <returns>List of completion entries for a given location in the AST</returns>
        public static IReadOnlyCollection <IRCompletionListProvider> GetCompletionForLocation(RCompletionContext context, bool autoShownCompletion)
        {
            List <IRCompletionListProvider> providers = new List <IRCompletionListProvider>();

            if (context.AstRoot.Comments.Contains(context.Position))
            {
                // No completion in comments
                return(providers);
            }

            // First check file completion - it happens inside strings
            string directory;

            if (CanShowFileCompletion(context.AstRoot, context.Position, out directory))
            {
                if (!string.IsNullOrEmpty(directory))
                {
                    providers.Add(new FilesCompletionProvider(directory));
                }
                return(providers);
            }

            // Now check if position is inside a string and if so, suppress completion list
            var tokenNode = context.AstRoot.GetNodeOfTypeFromPosition <TokenNode>(context.Position);

            if (tokenNode != null && tokenNode.Token.TokenType == RTokenType.String)
            {
                // No completion in string
                return(providers);
            }

            // Identifier character is a trigger but only as a first character so it doesn't suddenly
            // bring completion back on a second character if user dismissed it after the first one,
            // or in a middle of 'install.packages' when user types dot or in floating point numbers.
            if (context.Position > 1 && autoShownCompletion)
            {
                char triggerChar      = context.TextBuffer.CurrentSnapshot.GetText(context.Position - 1, 1)[0];
                char charBeforeTigger = context.TextBuffer.CurrentSnapshot.GetText(context.Position - 2, 1)[0];
                if (RTokenizer.IsIdentifierCharacter(triggerChar) && RTokenizer.IsIdentifierCharacter(charBeforeTigger))
                {
                    return(providers);
                }
            }

            if (IsInFunctionArgumentName <FunctionDefinition>(context.AstRoot, context.Position))
            {
                // No completion in function definition argument names
                return(providers);
            }

            if (IsInObjectMemberName(context.AstRoot.TextProvider, context.Position))
            {
                providers.Add(new WorkspaceVariableCompletionProvider());
                return(providers);
            }

            if (IsPackageListCompletion(context.TextBuffer, context.Position))
            {
                providers.Add(new PackagesCompletionProvider());
            }
            else
            {
                if (IsInFunctionArgumentName <FunctionCall>(context.AstRoot, context.Position))
                {
                    providers.Add(new ParameterNameCompletionProvider());
                }

                foreach (var p in CompletionProviders)
                {
                    providers.Add(p.Value);
                }

                if (!context.IsInNameSpace())
                {
                    providers.Add(new PackagesCompletionProvider());
                }
            }

            providers.Add(new WorkspaceVariableCompletionProvider());

            return(providers);
        }
示例#29
0
        private static bool IsStandaloneOperator(string text)
        {
            var tokens = new RTokenizer().Tokenize(text);

            return(tokens.Count == 1 && tokens[0].TokenType == RTokenType.Operator);
        }
示例#30
0
        public static string GetItemAtPosition(ITextSnapshotLine line, int position, Func<RTokenType, bool> tokenTypeCheck, out Span span) {
            string lineText = line.GetText();
            var offset = 0;
            var positionInTokens = position - line.Start;
            var tokenizer = new RTokenizer();
            var tokens = tokenizer.Tokenize(lineText);
            var tokenIndex = tokens.GetItemContaining(positionInTokens);
            if (tokenIndex >= 0) {
                var token = tokens[tokenIndex];
                if (token.TokenType == RTokenType.Comment) {
                    // Tokenize inside comment since we do want F1 to work inside 
                    // commented out code, code samples or Roxygen blocks.
                    positionInTokens -= token.Start;
                    offset = token.Start + 1;
                    tokens = tokenizer.Tokenize(lineText.Substring(offset, token.Length - 1));
                    tokenIndex = tokens.GetItemContaining(positionInTokens);
                    if (tokenIndex >= 0) {
                        token = tokens[tokenIndex];
                    }
                }
                if (tokenTypeCheck(token.TokenType)) {
                    var start = token.Start + offset;
                    var end = Math.Min(start + token.Length, line.End);
                    span = Span.FromBounds(line.Start + start, line.Start + end); // return view span
                    return lineText.Substring(start, end - start);
                }
            }

            span = Span.FromBounds(0, 0);
            return string.Empty;
        }
示例#31
0
        private static int PositionFromTokens(ITextSnapshot snapshot, int itemIndex, int offset) {
            var tokenizer = new RTokenizer();
            var tokens = tokenizer.Tokenize(new TextProvider(snapshot), 0, snapshot.Length, true);

            if (itemIndex >= 0 && itemIndex < tokens.Count) {
                int position = tokens[itemIndex].Start - offset;

                position = Math.Min(position, snapshot.Length);
                position = Math.Max(position, 0);

                return position;
            }

            return snapshot.Length;
        }
        private static bool IsMultiLineCandidate(string text) {
            if (text.IndexOfAny(new[] { '\n', '\r' }) != -1) {
                // if we already have newlines then we're multiline
                return true;
            }

            var tokenizer = new RTokenizer();
            IReadOnlyTextRangeCollection<RToken> tokens = tokenizer.Tokenize(new TextStream(text), 0, text.Length);
            return tokens.Any(t => t.TokenType == RTokenType.OpenCurlyBrace);
        }
示例#33
0
        /// <summary>
        /// Tokenizes provided string that contains R code
        /// </summary>
        private void Tokenize(string text) {
            _textProvider = new TextStream(text);

            var tokenizer = new RTokenizer(separateComments: false);
            var tokens = tokenizer.Tokenize(_textProvider, 0, _textProvider.Length);
            _tokens = new TokenStream<RToken>(tokens, RToken.EndOfStreamToken);

            _braceHandler = new BraceHandler(_tokens, _tb);
            _expressionHelper = new ExpressionHelper(_tokens, _textProvider);
        }
示例#34
0
        private static bool IsValidFunctionName(string functionName)
        {
            var tokens = new RTokenizer().Tokenize(functionName);

            return(tokens.Count == 1 && tokens[0].TokenType == RTokenType.Identifier);
        }
示例#35
0
        /// <summary>
        /// Tokenizes provided string that contains R code
        /// </summary>
        private void Tokenize(string text) {
            _textProvider = new TextStream(text);

            var tokenizer = new RTokenizer(separateComments: false);
            var tokens = tokenizer.Tokenize(_textProvider, 0, _textProvider.Length);
            _tokens = new TokenStream<RToken>(tokens, RToken.EndOfStreamToken);
        }
示例#36
0
        private int PositionFromTokens(ITextSnapshot snapshot, int itemIndex, int offset) {
            int lengthChange = snapshot.Length - _lengthBeforeChange;
            var tokenizer = new RTokenizer();
            var tokens = tokenizer.Tokenize(
                new TextProvider(snapshot), _changingRange.Start, _changingRange.Length + lengthChange, true);

            if (itemIndex >= 0 && itemIndex < tokens.Count) {
                int position = tokens[itemIndex].Start - offset;

                position = Math.Min(position, snapshot.Length);
                position = Math.Max(position, 0);

                return position;
            }

            return _changingRange.End + lengthChange;
        }
示例#37
0
        /// <summary>
        /// Handles R-like content in RD. This includes handling # and ##
        /// as comments, counting brace nesting, handling "..." as string
        /// (not true in plain RD LaTeX-like content) and colorizing numbers
        /// by using actual R tokenizer. Now. there is a confusing part:
        /// "There are two types of comments in R-like mode. As elsewhere in
        /// Rd files, Rd comments start with %, and run to the end of the line."
        /// If that is so then $ in sprintf will beging RD comment which frankly
        /// doesn't make any sense fron the authoring/editing point of view.
        /// "% characters must be escaped even within strings, or they will be
        /// taken as Rd comments." Sure, but R engine doesn't do that when
        /// requesting help in Rd format.
        /// </summary>
        private void HandleRContent()
        {
            var braceCounter = new BraceCounter <char>(new[] { '{', '}', '[', ']' });

            while (!_cs.IsEndOfStream())
            {
                var handled = false;
                switch (_cs.CurrentChar)
                {
                case '\"':
                case '\'':
                    handled = HandleRString(_cs.CurrentChar);
                    break;

                case '\\':
                    handled = IsEscape();
                    if (handled)
                    {
                        _cs.Advance(2);
                    }
                    else
                    {
                        handled = HandleKeyword();
                    }
                    break;

                case '#':
                    handled = HandlePragma();
                    if (!handled)
                    {
                        if (_cs.NextChar == '#')
                        {
                            // ## is always comment in R-like content
                            handled = HandleComment();
                        }
                        else
                        {
                            // With a sinle # it may or may not be comment.
                            // For example, there are statements like \code{#}.
                            // Heuristic is to treat text that contains {} or \
                            // as NOT a comment.
                            var commentStart = _cs.Position;
                            _cs.SkipToEol();

                            var commentText = _cs.Text.GetText(TextRange.FromBounds(commentStart, _cs.Position));
                            _cs.Position = commentStart;

                            if (commentText.IndexOfAny(new[] { '{', '\\', '}' }) < 0)
                            {
                                handled = HandleComment();
                            }
                        }
                    }
                    break;

                default:
                    if (braceCounter.CountBrace(_cs.CurrentChar))
                    {
                        handled = AddBraceToken();

                        if (braceCounter.Count == 0)
                        {
                            return;
                        }
                    }
                    else
                    {
                        // Check if sequence is a candidate for a number.
                        // The check is not perfect but numbers in R-like content
                        // are typically very simple as R blocks are usually
                        // code examples and do not contain exotic sequences.

                        if (!char.IsLetter(_cs.PrevChar) && (_cs.IsDecimal() || _cs.CurrentChar == '-' || _cs.CurrentChar == '.'))
                        {
                            var sequenceStart = _cs.Position;
                            _cs.SkipToWhitespace();

                            if (_cs.Position > sequenceStart)
                            {
                                var rt = new RTokenizer();

                                var candidate = _cs.Text.GetText(TextRange.FromBounds(sequenceStart, _cs.Position));
                                var rTokens   = rt.Tokenize(candidate);

                                if (rTokens.Count > 0 && rTokens[0].TokenType == RTokenType.Number)
                                {
                                    if (_tokenizeRContent)
                                    {
                                        AddToken(RdTokenType.Number, sequenceStart + rTokens[0].Start, rTokens[0].Length);
                                    }
                                    _cs.Position = sequenceStart + rTokens[0].End;
                                    continue;
                                }
                            }

                            _cs.Position = sequenceStart;
                        }
                    }
                    break;
                }

                if (!handled)
                {
                    _cs.MoveToNextChar();
                }
            }
        }
示例#38
0
        public override bool Parse(ParseContext context, IAstNode parent)
        {
            RToken currentToken  = context.Tokens.CurrentToken;
            string text          = context.TextProvider.GetText(currentToken);
            double realPart      = Double.NaN;
            double imaginaryPart = Double.NaN;

            Debug.Assert(currentToken.TokenType == RTokenType.Complex);

            // Split into real and imaginary parts. Imaginary part
            // should always be there since otherwise tokenizer would
            // not have idenfified the number as complex. Note that
            // real part may be missing as in '+0i'. Operator may also
            // be missing: 1i is a legal complex number.

            Debug.Assert(text[text.Length - 1] == 'i');

            // Drop trailing i and retokenize as two numbers
            RTokenizer tokenizer = new RTokenizer(separateComments: false);
            IReadOnlyTextRangeCollection <RToken> tokens = tokenizer.Tokenize(text.Substring(0, text.Length - 1));

            if (tokens.Count == 1)
            {
                // Only imaginary part is present
                Debug.Assert(tokens[0].TokenType == RTokenType.Number);
                if (!Number.TryParse(text.Substring(tokens[0].Start, tokens[0].Length), out imaginaryPart))
                {
                    imaginaryPart = Double.NaN;
                }
            }
            else if (tokens.Count == 3)
            {
                // Real and imaginary parts present
                Debug.Assert(tokens[0].TokenType == RTokenType.Number);
                Debug.Assert(tokens[1].TokenType == RTokenType.Operator);
                Debug.Assert(tokens[2].TokenType == RTokenType.Number);

                string real = text.Substring(tokens[0].Start, tokens[0].Length);
                if (!Number.TryParse(real, out realPart))
                {
                    realPart = Double.NaN;
                }
                // Imaginary does not allow 'L' suffix
                string imaginary = text.Substring(tokens[2].Start, tokens[2].Length);
                if (!Number.TryParse(imaginary, out imaginaryPart, allowLSuffix: false))
                {
                    imaginaryPart = Double.NaN;
                }
            }

            if (realPart == Double.NaN || imaginaryPart == Double.NaN)
            {
                context.AddError(new MissingItemParseError(ParseErrorType.NumberExpected, context.Tokens.PreviousToken));
                return(false);
            }

            Complex complex = new Complex(realPart, imaginaryPart);

            Value = new RComplex(complex);
            return(base.Parse(context, parent));
        }
示例#39
0
        /// <summary>
        /// Extracts complete variable name under the caret. In '`abc`$`def` returns 
        /// the complete expression rather than its parts. Typically used to get data 
        /// for completion of variable members as in when user typed 'abc$def$'
        /// Since method does not perform semantic analysis, it does not guaratee 
        /// syntactically correct expression, it may return 'a$$b'.
        /// </summary>
        public static string GetVariableNameBeforeCaret(this ITextView textView) {
            if (textView.Caret.InVirtualSpace) {
                return string.Empty;
            }
            var position = textView.Caret.Position.BufferPosition;
            var line = position.GetContainingLine();

            // For performance reasons we won't be using AST here
            // since during completion it is most probably damaged.
            string lineText = line.GetText();
            var tokenizer = new RTokenizer();
            var tokens = tokenizer.Tokenize(lineText);

            var tokenPosition = position - line.Start;
            var index = tokens.GetFirstItemBeforePosition(tokenPosition);
            // Preceding token must be right next to caret position
            if (index < 0 || tokens[index].End < tokenPosition || !IsVariableNameToken(lineText, tokens[index])) {
                return string.Empty;
            }

            if (index == 0) {
                return IsVariableNameToken(lineText, tokens[0]) ? lineText.Substring(tokens[0].Start, tokens[0].Length) : string.Empty;
            }

            // Walk back through tokens allowing identifier and specific
            // operator tokens. No whitespace is permitted between tokens.
            // We have at least 2 tokens here.
            int i = index;
            for (; i > 0; i--) {
                var precedingToken = tokens[i - 1];
                var currentToken = tokens[i];
                if (precedingToken.End < currentToken.Start || !IsVariableNameToken(lineText, precedingToken)) {
                    break;
                }
            }

            return lineText.Substring(tokens[i].Start, tokens[index].End - tokens[i].Start);
        }