コード例 #1
0
ファイル: RCompletionController.cs プロジェクト: Fooway/RTVS
 /// <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;
             if (pos > 0 && pos <= position.Value.Snapshot.Length)
             {
                 bool endOfIdentifier = RTokenizer.IsIdentifierCharacter(position.Value.Snapshot[pos - 1]);
                 bool showCompletion  = endOfIdentifier && REditorSettings.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
                 }
             }
         }
     }
     return(base.OnPreTypeChar(typedCharacter));
 }
コード例 #2
0
ファイル: RCompletionSet.cs プロジェクト: nomada2/RTVS
        private static CompletionList OrderList(IReadOnlyCollection <Completion> 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 argumentNames = completions.Where(x => RTokenizer.IsIdentifierCharacter(x.DisplayText[0]) && x.DisplayText.EndsWith("=", StringComparison.Ordinal));

            var rtvsNames    = completions.Where(x => x.DisplayText.IndexOf(".rtvs") >= 0);
            var specialNames = completions.Where(x => !char.IsLetter(x.DisplayText[0]));

            specialNames = specialNames.Except(rtvsNames);

            var generalEntries = completions.Except(argumentNames);

            generalEntries = generalEntries.Except(rtvsNames);
            generalEntries = generalEntries.Except(specialNames);

            List <Completion> orderedCompletions = new List <Completion>();

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

            return(new CompletionList(orderedCompletions));
        }
コード例 #3
0
ファイル: RCompletionSource.cs プロジェクト: skeptycal/RTVS
        /// <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(IRIntellisenseContext context, ICompletionSession session)
        {
            var selectedSpans = session.TextView.Selection.SelectedSpans;

            if (selectedSpans.Count == 1 && selectedSpans[0].Span.Length > 0)
            {
                var spans = session.TextView.MapDownToR(selectedSpans[0]);
                if (spans.Count > 0)
                {
                    return(spans[0].Span);
                }
                return(null);
            }

            var snapshot     = _textBuffer.CurrentSnapshot;
            var line         = snapshot.GetLineFromPosition(context.Position);
            var lineText     = snapshot.GetText(line.Start, line.Length);
            var linePosition = context.Position - line.Start;

            // If completion is in comment, such as in Roxygen,
            // allow any text since it is no longer language-specific
            var document = context.EditorBuffer.GetEditorDocument <IREditorDocument>();

            if (document.IsPositionInComment(context.Position))
            {
                var typedText = lineText.TextBeforePosition(linePosition);
                return(new Span(context.Position - typedText.Length, typedText.Length));
            }

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

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

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

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

            return(new Span(context.Position, 0));
        }
コード例 #4
0
 /// <summary>
 /// Called after character is typed. Gives language-specific completion
 /// controller has a chance to dismiss or initiate completion and paramenter
 /// help sessions depending on the current context.
 /// </summary>
 public override void OnPostTypeChar(char typedCharacter)
 {
     if (typedCharacter == '(' || typedCharacter == ',')
     {
         // Check if caret moved into a different functions such as when
         // user types a sequence of nested function calls. If so,
         // dismiss current signature session and start a new one.
         if (!SignatureHelper.IsSameSignatureContext(TextView, _textBuffer, SignatureBroker))
         {
             DismissAllSessions();
             TriggerSignatureHelp();
         }
     }
     else if (HasActiveSignatureSession(TextView) && typedCharacter == ')')
     {
         // Typing closing ) closes signature and completion sessions.
         DismissAllSessions();
         // However, when user types closing brace is an expression inside
         // function argument like in x = y * (z + 1) we need to re-trigger
         // signature session
         AstRoot      ast = REditorDocument.FromTextBuffer(TextView.TextBuffer).EditorTree.AstRoot;
         FunctionCall f   = ast.GetNodeOfTypeFromPosition <FunctionCall>(TextView.Caret.Position.BufferPosition);
         if (f != null)
         {
             TriggerSignatureHelp();
         }
     }
     else if (HasActiveSignatureSession(TextView) && typedCharacter == '\n')
     {
         // Upon ENTER we need to dismiss all sessions and re-trigger
         // signature help. Triggering signature help outside of
         // a function definition or call is a no-op so it is safe.
         DismissAllSessions();
         TriggerSignatureHelp();
     }
     else if (this.HasActiveCompletionSession)
     {
         if (typedCharacter == '\'' || typedCharacter == '\"')
         {
             // First handle completion of a string.
             base.OnPostTypeChar(typedCharacter);
             // Then re-trigger completion.
             DismissAllSessions();
             ShowCompletion(autoShownCompletion: true);
             return;
         }
         else
         {
             // Backspace does not dismiss completion. Characters that may be an identifier
             // name do not dismiss completion either allowing correction of typing.
             if (typedCharacter != '\b' && !RTokenizer.IsIdentifierCharacter(typedCharacter))
             {
                 DismissCompletionSession();
             }
         }
     }
     base.OnPostTypeChar(typedCharacter);
 }
コード例 #5
0
ファイル: RCompletionSource.cs プロジェクト: xoriath/RTVS
        /// <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)
            {
                var spans = session.TextView.MapDownToR(selectedSpans[0]);
                if (spans.Count > 0)
                {
                    return(spans[0].Span);
                }
                return(null);
            }

            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));
        }
コード例 #6
0
        /// <summary>
        /// Retrieves name of the package in 'package::' statement
        /// so intellisense can show list of functions available
        /// in the specific package.
        /// </summary>
        private IEnumerable <IPackageInfo> GetSpecificPackage(RCompletionContext context)
        {
            List <IPackageInfo> packages = new List <IPackageInfo>();
            ITextSnapshot       snapshot = context.TextBuffer.CurrentSnapshot;
            int colons = 0;

            for (int i = context.Position - 1; i >= 0; i--, colons++)
            {
                char ch = snapshot[i];
                if (ch != ':')
                {
                    break;
                }
            }

            if (colons > 1 && colons < 4)
            {
                string packageName = string.Empty;
                int    start       = 0;
                int    end         = context.Position - colons;

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

                if (start < end)
                {
                    packageName = snapshot.GetText(Span.FromBounds(start, end));
                    if (packageName.Length > 0)
                    {
                        context.InternalFunctions = colons == 3;
                        var package = GetPackageByName(packageName);
                        if (package != null)
                        {
                            packages.Add(package);
                        }
                    }
                }
            }

            return(packages);
        }
コード例 #7
0
        /// <summary>
        /// Retrieves name of the package in 'package::' statement
        /// so intellisense can show list of functions available
        /// in the specific package.
        /// </summary>
        private IEnumerable <IPackageInfo> GetSpecificPackage(IRIntellisenseContext context)
        {
            var packages = new List <IPackageInfo>();
            var snapshot = context.EditorBuffer.CurrentSnapshot;
            var colons   = 0;

            for (var i = context.Position - 1; i >= 0; i--, colons++)
            {
                var ch = snapshot[i];
                if (ch != ':')
                {
                    break;
                }
            }

            if (colons > 1 && colons < 4)
            {
                var packageName = string.Empty;
                var start       = 0;
                var end         = context.Position - colons;

                for (var i = end - 1; i >= 0; i--)
                {
                    var ch = snapshot[i];
                    if (!RTokenizer.IsIdentifierCharacter(ch))
                    {
                        start = i + 1;
                        break;
                    }
                }

                if (start < end)
                {
                    packageName = snapshot.GetText(TextRange.FromBounds(start, end));
                    if (packageName.Length > 0)
                    {
                        context.InternalFunctions = colons == 3;
                        var package = GetPackageByName(packageName);
                        if (package != null)
                        {
                            packages.Add(package);
                        }
                    }
                }
            }

            return(packages);
        }
コード例 #8
0
        private static string GetFilterPrefix(IRIntellisenseContext context)
        {
            var snapshot = context.EditorBuffer.CurrentSnapshot;
            var line     = snapshot.GetLineFromPosition(context.Position);
            var text     = line.GetText();

            int i;
            var offset = context.Position - line.Start;

            for (i = offset - 1; i >= 0 && RTokenizer.IsIdentifierCharacter(text[i]); i--)
            {
            }

            i = Math.Min(offset, i + 1);
            return(text.Substring(i, offset - i));
        }
コード例 #9
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
                var position = TextView.GetCaretPosition(_textBuffer);
                if (position.HasValue)
                {
                    int pos      = position.Value;
                    var document = position.Value.Snapshot.TextBuffer.GetEditorDocument <IREditorDocument>();
                    if (!document.IsPositionInComment(pos))
                    {
                        if (pos > 0 && pos <= position.Value.Snapshot.Length)
                        {
                            var endOfIdentifier = RTokenizer.IsIdentifierCharacter(position.Value.Snapshot[pos - 1]);
                            var showCompletion  = endOfIdentifier && _settings.ShowCompletionOnTab;
                            if (!showCompletion)
                            {
                                showCompletion = RCompletionEngine.CanShowFileCompletion(document.EditorTree.AstRoot, pos, out string directory);
                            }
                            if (showCompletion)
                            {
                                ShowCompletion(autoShownCompletion: false);
                                return(true); // eat the character
                            }
                        }
                    }
                }
            }
            else if (typedCharacter == '#')
            {
                if (TryInsertRoxygenBlock())
                {
                    return(true);
                }
            }

            return(base.OnPreTypeChar(typedCharacter));
        }
コード例 #10
0
ファイル: RCompletionContext.cs プロジェクト: fpcMotif/RTVS
        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);
        }
コード例 #11
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);
        }
コード例 #12
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);
        }
コード例 #13
0
ファイル: RCompletionSet.cs プロジェクト: skeptycal/RTVS
        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));
        }
コード例 #14
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);
        }