Exemple #1
0
        internal CompletionAnalysis GetCompletions(ICompletionSession session, ITextView view, ITextSnapshot snapshot, ITrackingPoint point)
        {
            if (IsSpaceCompletion(snapshot, point) && session.IsCompleteWordMode())
            {
                // Cannot complete a word immediately after a space
                session.ClearCompleteWordMode();
            }

            var bi    = EditorServices.GetBufferInfo(snapshot.TextBuffer);
            var entry = bi?.AnalysisEntry;

            if (entry == null)
            {
                return(CompletionAnalysis.EmptyCompletionContext);
            }

            var options = session.GetOptions(Site);

            if (ReverseExpressionParser.IsInGrouping(snapshot, bi.GetTokensInReverseFromPoint(point.GetPoint(snapshot))))
            {
                options = options.Clone();
                options.IncludeStatementKeywords = false;
            }

            return(new CompletionAnalysis(
                       EditorServices,
                       session,
                       view,
                       snapshot,
                       point,
                       options
                       ));
        }
Exemple #2
0
        public int GetDataTipText(TextSpan[] pSpan, out string pbstrText)
        {
            if (!_wpfTextView.TextBuffer.ContentType.IsOfType(PythonCoreConstants.ContentType))
            {
                pbstrText = null;
                return(VSConstants.E_NOTIMPL);
            }

            if (pSpan.Length != 1)
            {
                throw new ArgumentException("Array parameter should contain exactly one TextSpan", "pSpan");
            }

            // Adjust the span to expression boundaries.

            var snapshot = _wpfTextView.TextSnapshot;
            var start    = LineAndColumnNumberToSnapshotPoint(snapshot, pSpan[0].iStartLine, pSpan[0].iStartIndex);
            var end      = LineAndColumnNumberToSnapshotPoint(snapshot, pSpan[0].iEndLine, pSpan[0].iEndIndex);

            // If this is a zero-length span (which it usually is, unless there's selection), adjust it
            // to cover one char to the right, since an empty span at the beginning of the expression does
            // not count as belonging to that expression;
            if (start == end && start.Position != snapshot.Length)
            {
                end += 1;
            }

            var    snapshotSpan = new SnapshotSpan(start, end);
            var    trackingSpan = snapshot.CreateTrackingSpan(snapshotSpan.Span, SpanTrackingMode.EdgeExclusive);
            var    rep          = new ReverseExpressionParser(snapshot, _wpfTextView.TextBuffer, trackingSpan);
            var    exprSpan     = rep.GetExpressionRange(forCompletion: false);
            string expr         = null;

            if (exprSpan != null)
            {
                SnapshotPointToLineAndColumnNumber(exprSpan.Value.Start, out pSpan[0].iStartLine, out pSpan[0].iStartIndex);
                SnapshotPointToLineAndColumnNumber(exprSpan.Value.End, out pSpan[0].iEndLine, out pSpan[0].iEndIndex);
                expr = VsProjectAnalyzer.ExpressionForDataTipAsync(
                    _serviceProvider,
                    _wpfTextView,
                    exprSpan.Value,
                    TimeSpan.FromSeconds(1.0)
                    ).WaitAndUnwrapExceptions(Dispatcher.CurrentDispatcher);
            }
            else
            {
                // If it's not an expression, suppress the tip.
                pbstrText = null;
                return(VSConstants.E_FAIL);
            }

            return(_debugger.GetDataTipValue(_vsTextLines, pSpan, expr, out pbstrText));
        }
        internal static TextSpan?GetDataTipSpan(IWpfTextView wpfTextView, TextSpan selection)
        {
            // Adjust the span to expression boundaries.
            var snapshot = wpfTextView.TextSnapshot;
            var start    = LineAndColumnNumberToSnapshotPoint(snapshot, selection.iStartLine, selection.iStartIndex);
            var end      = LineAndColumnNumberToSnapshotPoint(snapshot, selection.iEndLine, selection.iEndIndex);

            // If this is a zero-length span (which it usually is, unless there's selection), adjust it
            // to cover one char to the right, since an empty span at the beginning of the expression does
            // not count as belonging to that expression;
            if (start == end && start.Position != snapshot.Length)
            {
                end += 1;
            }

            var snapshotSpan = new SnapshotSpan(start, end);
            var trackingSpan = snapshot.CreateTrackingSpan(snapshotSpan.Span, SpanTrackingMode.EdgeExclusive);
            var rep          = new ReverseExpressionParser(snapshot, wpfTextView.TextBuffer, trackingSpan);
            var exprSpan     = rep.GetExpressionRange(forCompletion: false);

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

            // Check whether this is an expression with side effects - if it does, we don't want to show a data tip for it.
            string text = exprSpan.Value.GetText();
            var    ast  = new JSParser(text).Parse(new CodeSettings());

            var sideEffectsDetectingVisitor = new SideEffectsDetectingVisitor();

            ast.Walk(sideEffectsDetectingVisitor);
            if (sideEffectsDetectingVisitor.HasSideEffects)
            {
                return(null);
            }

            TextSpan dataTipSpan;

            SnapshotPointToLineAndColumnNumber(exprSpan.Value.Start, out dataTipSpan.iStartLine, out dataTipSpan.iStartIndex);
            SnapshotPointToLineAndColumnNumber(exprSpan.Value.End, out dataTipSpan.iEndLine, out dataTipSpan.iEndIndex);
            return(dataTipSpan);
        }
        internal bool GetPrecedingExpression(out string text, out SnapshotSpan expressionExtent) {
            text = string.Empty;
            expressionExtent = default(SnapshotSpan);

            var startSpan = _snapshot.CreateTrackingSpan(Span.GetSpan(_snapshot).Start.Position, 0, SpanTrackingMode.EdgeInclusive);
            var parser = new ReverseExpressionParser(_snapshot, _snapshot.TextBuffer, startSpan);
            using (var e = parser.GetEnumerator()) {
                if (e.MoveNext() &&
                    e.Current != null &&
                    e.Current.ClassificationType.IsOfType(PredefinedClassificationTypeNames.Number)) {
                    return false;
                }
            }

            var sourceSpan = parser.GetExpressionRange();
            if (sourceSpan.HasValue && sourceSpan.Value.Length > 0) {
                text = sourceSpan.Value.GetText();
                if (text.EndsWith(".")) {
                    text = text.Substring(0, text.Length - 1);
                    if (text.Length == 0) {
                        // don't return all available members on empty dot.
                        return false;
                    }
                } else {
                    int cut = text.LastIndexOfAny(new[] { '.', ']', ')' });
                    if (cut != -1) {
                        text = text.Substring(0, cut);
                    } else {
                        text = String.Empty;
                    }
                }
            }


            expressionExtent = sourceSpan ?? new SnapshotSpan(Span.GetStartPoint(_snapshot), 0);

            return true;
        }
Exemple #5
0
        private bool IsModuleName(IRenameVariableInput input)
        {
            // make sure we're in
            var span       = _view.GetCaretSpan();
            var buffer     = span.TextBuffer;
            var snapshot   = buffer.CurrentSnapshot;
            var classifier = buffer.GetPythonClassifier();

            bool sawImport = false, sawFrom = false, sawName = false;
            var  walker = ReverseExpressionParser.ReverseClassificationSpanEnumerator(classifier, span.GetEndPoint(snapshot));

            while (walker.MoveNext())
            {
                var current = walker.Current;
                if (current == null)
                {
                    // new-line
                    break;
                }

                var text = current.Span.GetText();
                if (current.ClassificationType.IsOfType(PredefinedClassificationTypeNames.Identifier))
                {
                    // identifiers are ok
                    sawName = true;
                }
                else if (current.ClassificationType == classifier.Provider.DotClassification ||
                         current.ClassificationType == classifier.Provider.CommaClassification)
                {
                    // dots and commas are ok
                }
                else if (current.ClassificationType == classifier.Provider.GroupingClassification)
                {
                    if (text != "(" && text != ")")
                    {
                        // list/dict groupings are not ok
                        break;
                    }
                }
                else if (current.ClassificationType.IsOfType(PredefinedClassificationTypeNames.Keyword))
                {
                    if (text == "import")
                    {
                        sawImport = true;
                    }
                    else if (text == "from")
                    {
                        sawFrom = true;
                        break;
                    }
                    else if (text == "as")
                    {
                        if (sawName)
                        {
                            // import fob as oar
                            // from fob import oar as baz
                            break;
                        }
                    }
                    else
                    {
                        break;
                    }
                }
                else
                {
                    break;
                }
            }

            // we saw from, but not import, so we're renaming a module name (from fob, renaming fob)
            // or we saw import, but not a from, so we're renaming a module name
            return(sawFrom != sawImport);
        }
        private bool ShouldTriggerIdentifierCompletionSession(out bool commitByDefault) {
            commitByDefault = true;

            if (_bufferParser == null ||
                !_provider.PythonService.AdvancedOptions.AutoListIdentifiers ||
                !_provider.PythonService.AdvancedOptions.AutoListMembers) {
                return false;
            }

            SnapshotPoint? caretPoint = _textView.BufferGraph.MapDownToFirstMatch(
                _textView.Caret.Position.BufferPosition,
                PointTrackingMode.Positive,
                EditorExtensions.IsPythonContent,
                PositionAffinity.Predecessor
            );
            if (!caretPoint.HasValue) {
                return false;
            }

            var snapshot = caretPoint.Value.Snapshot;

            var statement = new ReverseExpressionParser(
                snapshot,
                snapshot.TextBuffer,
                snapshot.CreateTrackingSpan(caretPoint.Value.Position, 0, SpanTrackingMode.EdgeNegative)
            ).GetStatementRange();
            if (!statement.HasValue || caretPoint.Value <= statement.Value.Start) {
                return false;
            }

            var text = new SnapshotSpan(statement.Value.Start, caretPoint.Value).GetText();
            if (string.IsNullOrEmpty(text)) {
                return false;
            }

            var languageVersion = _bufferParser._parser.InterpreterFactory.Configuration.Version.ToLanguageVersion();
            PythonAst ast;
            using (var parser = Parser.CreateParser(new StringReader(text), languageVersion, new ParserOptions())) {
                ast = parser.ParseSingleStatement();
            }

            var walker = new ExpressionCompletionWalker(caretPoint.Value.Position - statement.Value.Start.Position);
            ast.Walk(walker);
            commitByDefault = walker.CommitByDefault;
            return walker.CanComplete;
        }
Exemple #7
0
        public int GetDataTipText(TextSpan[] pSpan, out string pbstrText) {
            if (!_wpfTextView.TextBuffer.ContentType.IsOfType(PythonCoreConstants.ContentType)) {
                pbstrText = null;
                return VSConstants.E_NOTIMPL;
            }

            if (pSpan.Length != 1) {
                throw new ArgumentException("Array parameter should contain exactly one TextSpan", "pSpan");
            }

            // Adjust the span to expression boundaries.

            var snapshot = _wpfTextView.TextSnapshot;
            var start = LineAndColumnNumberToSnapshotPoint(snapshot, pSpan[0].iStartLine, pSpan[0].iStartIndex);
            var end = LineAndColumnNumberToSnapshotPoint(snapshot, pSpan[0].iEndLine, pSpan[0].iEndIndex);

            // If this is a zero-length span (which it usually is, unless there's selection), adjust it
            // to cover one char to the right, since an empty span at the beginning of the expression does
            // not count as belonging to that expression;
            if (start == end && start.Position != snapshot.Length) {
                end += 1;
            }

            var snapshotSpan = new SnapshotSpan(start, end);
            var trackingSpan = snapshot.CreateTrackingSpan(snapshotSpan.Span, SpanTrackingMode.EdgeExclusive);
            var rep = new ReverseExpressionParser(snapshot, _wpfTextView.TextBuffer, trackingSpan);
            var exprSpan = rep.GetExpressionRange(forCompletion: false);
            if (exprSpan != null) {
                SnapshotPointToLineAndColumnNumber(exprSpan.Value.Start, out pSpan[0].iStartLine, out pSpan[0].iStartIndex);
                SnapshotPointToLineAndColumnNumber(exprSpan.Value.End, out pSpan[0].iEndLine, out pSpan[0].iEndIndex);
            } else {
                // If it's not an expression, suppress the tip.
                pbstrText = null;
                return VSConstants.E_FAIL;
            }

            return _debugger.GetDataTipValue(_vsTextLines, pSpan, null, out pbstrText);
        }
Exemple #8
0
        /// <summary>
        /// Gets a list of signatuers available for the expression at the provided location in the snapshot.
        /// </summary>
        internal static SignatureAnalysis GetSignatures(IServiceProvider serviceProvider, ITextSnapshot snapshot, ITrackingSpan span) {
            var buffer = snapshot.TextBuffer;
            ReverseExpressionParser parser = new ReverseExpressionParser(snapshot, buffer, span);

            var loc = parser.Span.GetSpan(parser.Snapshot.Version);

            int paramIndex;
            SnapshotPoint? sigStart;
            string lastKeywordArg;
            bool isParameterName;
            var exprRange = parser.GetExpressionRange(1, out paramIndex, out sigStart, out lastKeywordArg, out isParameterName);
            if (exprRange == null || sigStart == null) {
                return new SignatureAnalysis("", 0, new ISignature[0]);
            }

            var text = new SnapshotSpan(exprRange.Value.Snapshot, new Span(exprRange.Value.Start, sigStart.Value.Position - exprRange.Value.Start)).GetText();
            var applicableSpan = parser.Snapshot.CreateTrackingSpan(exprRange.Value.Span, SpanTrackingMode.EdgeInclusive);

            if (snapshot.TextBuffer.GetAnalyzer(serviceProvider).ShouldEvaluateForCompletion(text)) {
                var liveSigs = TryGetLiveSignatures(snapshot, paramIndex, text, applicableSpan, lastKeywordArg);
                if (liveSigs != null) {
                    return liveSigs;
                }
            }

            var start = Stopwatch.ElapsedMilliseconds;

            var analysisItem = buffer.GetProjectEntry();
            if (analysisItem != null) {
                var analysis = ((IPythonProjectEntry)analysisItem).Analysis;
                if (analysis != null) {
                    var location = TranslateIndex(loc.Start, snapshot, analysis);

                    IEnumerable<IOverloadResult> sigs;
                    lock (snapshot.TextBuffer.GetAnalyzer(serviceProvider)) {
                        sigs = analysis.GetSignatures(text, location);
                    }
                    var end = Stopwatch.ElapsedMilliseconds;

                    if (/*Logging &&*/ (end - start) > CompletionAnalysis.TooMuchTime) {
                        Trace.WriteLine(String.Format("{0} lookup time {1} for signatures", text, end - start));
                    }

                    var result = new List<ISignature>();
                    foreach (var sig in sigs) {
                        result.Add(new PythonSignature(applicableSpan, sig, paramIndex, lastKeywordArg));
                    }

                    return new SignatureAnalysis(
                        text,
                        paramIndex,
                        result,
                        lastKeywordArg
                    );
                }
            }
            return new SignatureAnalysis(text, paramIndex, new ISignature[0]);
        }
Exemple #9
0
        private static CompletionAnalysis GetNormalCompletionContext(IServiceProvider serviceProvider, ITextSnapshot snapshot, ITrackingSpan applicableSpan, ITrackingPoint point, CompletionOptions options) {
            var span = applicableSpan.GetSpan(snapshot);

            if (IsSpaceCompletion(snapshot, point) && !IntellisenseController.ForceCompletions) {
                return CompletionAnalysis.EmptyCompletionContext;
            }

            var parser = new ReverseExpressionParser(snapshot, snapshot.TextBuffer, applicableSpan);
            if (parser.IsInGrouping()) {
                options = options.Clone();
                options.IncludeStatementKeywords = false;
            }

            return new NormalCompletionAnalysis(
                snapshot.TextBuffer.GetAnalyzer(serviceProvider),
                snapshot,
                applicableSpan,
                snapshot.TextBuffer,
                options,
                serviceProvider
            );
        }
        internal static MissingImportAnalysis GetMissingImports(ITextSnapshot snapshot, ITrackingSpan span) {
            ReverseExpressionParser parser = new ReverseExpressionParser(snapshot, snapshot.TextBuffer, span);
            var loc = span.GetSpan(snapshot.Version);
            int dummy;
            SnapshotPoint? dummyPoint;
            string lastKeywordArg;
            bool isParameterName;
            var exprRange = parser.GetExpressionRange(0, out dummy, out dummyPoint, out lastKeywordArg, out isParameterName);
            if (exprRange == null || isParameterName) {
                return MissingImportAnalysis.Empty;
            }

            var analysis = ((IPythonProjectEntry)snapshot.TextBuffer.GetProjectEntry()).Analysis;
            if (analysis == null) {
                return MissingImportAnalysis.Empty;
            }

            var text = exprRange.Value.GetText();
            var analyzer = analysis.ProjectState;
            var index = span.GetStartPoint(snapshot).Position;

            var expr = Statement.GetExpression(
                analysis.GetAstFromTextByIndex(
                    text,
                    TranslateIndex(
                        index,
                        snapshot,
                        analysis
                    )
                ).Body
            );

            if (expr != null && expr is NameExpression) {
                var nameExpr = (NameExpression)expr;

                if (!IsImplicitlyDefinedName(nameExpr)) {
                    var applicableSpan = parser.Snapshot.CreateTrackingSpan(
                        exprRange.Value.Span,
                        SpanTrackingMode.EdgeExclusive
                    );

                    lock (snapshot.TextBuffer.GetAnalyzer()) {
                        index = TranslateIndex(
                            index,
                            snapshot,
                            analysis
                        );
                        var variables = analysis.GetVariablesByIndex(text, index).Where(IsDefinition).Count();

                        var values = analysis.GetValuesByIndex(text, index).ToArray();

                        // if we have type information or an assignment to the variable we won't offer 
                        // an import smart tag.
                        if (values.Length == 0 && variables == 0) {
                            string name = nameExpr.Name;
                            var imports = analysis.ProjectState.FindNameInAllModules(name);

                            return new MissingImportAnalysis(imports, applicableSpan);
                        }
                    }
                }
            }

            // if we have type information don't offer to add imports
            return MissingImportAnalysis.Empty;
        }
        /// <summary>
        /// Gets a list of signatuers available for the expression at the provided location in the snapshot.
        /// </summary>
        public static SignatureAnalysis GetSignatures(ITextSnapshot snapshot, ITrackingSpan span) {
            var buffer = snapshot.TextBuffer;
            ReverseExpressionParser parser = new ReverseExpressionParser(snapshot, buffer, span);

            var loc = parser.Span.GetSpan(parser.Snapshot.Version);

            int paramIndex;
            SnapshotPoint? sigStart;
            string lastKeywordArg;
            bool isParameterName;
            var exprRange = parser.GetExpressionRange(1, out paramIndex, out sigStart, out lastKeywordArg, out isParameterName, forSignatureHelp: true);
            if (exprRange == null || sigStart == null) {
                return new SignatureAnalysis(String.Empty, 0, new ISignature[0]);
            }

            var text = new SnapshotSpan(exprRange.Value.Snapshot, new Span(exprRange.Value.Start, sigStart.Value.Position - exprRange.Value.Start)).GetText();
            var applicableSpan = parser.Snapshot.CreateTrackingSpan(exprRange.Value.Span, SpanTrackingMode.EdgeInclusive);

            var analysisItem = buffer.GetProjectEntry();
            if (analysisItem != null) {
                var analysis = ((IJsProjectEntry)analysisItem).Analysis;
                if (analysis != null) {
                    int index = TranslateIndex(loc.Start, snapshot, analysis);

                    IEnumerable<IOverloadResult> sigs;
                    lock (snapshot.TextBuffer.GetAnalyzer()) {
                        sigs = analysis.GetSignaturesByIndex(text, index);
                    }

                    var result = new List<ISignature>();
                    foreach (var sig in sigs) {
                        result.Add(new NodejsSignature(applicableSpan, sig, paramIndex, lastKeywordArg));
                    }

                    return new SignatureAnalysis(
                        text,
                        paramIndex,
                        result,
                        lastKeywordArg
                    );
                }
            }
            return new SignatureAnalysis(text, paramIndex, new ISignature[0]);
        }
        private static CompletionAnalysis GetNormalCompletionContext(ITextSnapshot snapshot, ITrackingSpan applicableSpan, ITrackingPoint point) {
            var span = applicableSpan.GetSpan(snapshot);

            if (IsSpaceCompletion(snapshot, point) && !IntellisenseController.ForceCompletions) {
                return CompletionAnalysis.EmptyCompletionContext;
            }

            GetMemberOptions options = GetMemberOptions.IncludeExpressionKeywords | GetMemberOptions.IncludeStatementKeywords;
            var parser = new ReverseExpressionParser(snapshot, snapshot.TextBuffer, applicableSpan);
            if (parser.IsInGrouping()) {
                options &= ~GetMemberOptions.IncludeStatementKeywords;
            }

            return new NormalCompletionAnalysis(
                snapshot.TextBuffer.GetAnalyzer(),
                snapshot,
                applicableSpan,
                snapshot.TextBuffer,
                options
            );
        }
        private static CompletionAnalysis TrySpecialCompletions(ITextSnapshot snapshot, ITrackingSpan span, ITrackingPoint point) {
            var snapSpan = span.GetSpan(snapshot);
            var buffer = snapshot.TextBuffer;
            var classifier = buffer.GetNodejsClassifier();
            if (classifier == null) {
                return null;
            }
            var start = snapSpan.Start;

            var parser = new ReverseExpressionParser(snapshot, buffer, span);
            if (parser.IsInGrouping()) {
                var range = parser.GetExpressionRange(nesting: 1);
                if (range != null) {
                    start = range.Value.Start;
                }
            }

            // Get the classifiers from beginning of the line to the beginning of snapSpan.
            // The contents of snapSpan differ depending on what is determined in
            // CompletionSource.GetApplicableSpan.
            //
            // In the case of:
            //      var myIdentifier<cursor>
            // the applicable span will be "myIdentifier", so GetClassificationSpans will operate on "var "
            // 
            // In the case of comments and string literals, the applicable span will be empty,
            // so snapSpan.Start will occur at the current cursor position. 
            var tokens = classifier.GetClassificationSpans(new SnapshotSpan(start.GetContainingLine().Start, snapSpan.Start));
            if (tokens.Count > 0) {
                // Check for context-sensitive intellisense
                var lastClass = tokens[tokens.Count - 1];

                if (lastClass.ClassificationType == classifier.Provider.Comment ||
                    lastClass.ClassificationType == classifier.Provider.StringLiteral ||
                    (lastClass.ClassificationType == classifier.Provider.Keyword &&
                    _emptyCompletionContextKeywords.Contains(lastClass.Span.GetText()))) {
                    // No completions in comments, strings, or directly after certain keywords.
                    return CompletionAnalysis.EmptyCompletionContext;
                }
                return null;
            }

            return null;
        }
        private static CompletionAnalysis TrySpecialCompletions(ITextSnapshot snapshot, ITrackingSpan span, ITrackingPoint point) {
            var snapSpan = span.GetSpan(snapshot);
            var buffer = snapshot.TextBuffer;
            var classifier = buffer.GetNodejsClassifier();
            if (classifier == null) {
                return null;
            }
            var start = snapSpan.Start;

            var parser = new ReverseExpressionParser(snapshot, buffer, span);
            if (parser.IsInGrouping()) {
                var range = parser.GetExpressionRange(nesting: 1);
                if (range != null) {
                    start = range.Value.Start;
                }
            }

            var tokens = classifier.GetClassificationSpans(new SnapshotSpan(start.GetContainingLine().Start, snapSpan.Start));
            if (tokens.Count > 0) {
                // Check for context-sensitive intellisense
                var lastClass = tokens[tokens.Count - 1];

                if (lastClass.ClassificationType == classifier.Provider.Comment) {
                    // No completions in comments
                    return CompletionAnalysis.EmptyCompletionContext;
                } else if (lastClass.ClassificationType == classifier.Provider.StringLiteral) {
                    // String completion
                    return CompletionAnalysis.EmptyCompletionContext;
                }
                return null;
            }

            return null;
        }
Exemple #15
0
        private static int CalculateIndentation(string baseline, ITextSnapshotLine line, IEditorOptions options, IClassifier classifier, ITextView textView) {
            int indentation = GetIndentation(baseline, options.GetTabSize());
            int tabSize = options.GetIndentSize();
            var tokens = classifier.GetClassificationSpans(line.Extent);
            if (tokens.Count > 0 && !IsUnterminatedStringToken(tokens[tokens.Count - 1])) {
                int tokenIndex = tokens.Count - 1;

                while (tokenIndex >= 0 &&
                    (tokens[tokenIndex].ClassificationType.IsOfType(PredefinedClassificationTypeNames.Comment) ||
                    tokens[tokenIndex].ClassificationType.IsOfType(PredefinedClassificationTypeNames.WhiteSpace))) {
                    tokenIndex--;
                }

                if (tokenIndex < 0) {
                    return indentation;
                }

                if (ReverseExpressionParser.IsExplicitLineJoin(tokens[tokenIndex])) {
                    // explicit line continuation, we indent 1 level for the continued line unless
                    // we're already indented because of multiple line continuation characters.

                    indentation = GetIndentation(line.GetText(), options.GetTabSize());
                    var joinedLine = tokens[tokenIndex].Span.Start.GetContainingLine();
                    if (joinedLine.LineNumber > 0) {
                        var prevLineSpans = classifier.GetClassificationSpans(tokens[tokenIndex].Span.Snapshot.GetLineFromLineNumber(joinedLine.LineNumber - 1).Extent);
                        if (prevLineSpans.Count == 0 || !ReverseExpressionParser.IsExplicitLineJoin(prevLineSpans[prevLineSpans.Count - 1])) {
                            indentation += tabSize;
                        }
                    } else {
                        indentation += tabSize;
                    }

                    return indentation;
                }

                string sline = tokens[tokenIndex].Span.GetText();
                var lastChar = sline.Length == 0 ? '\0' : sline[sline.Length - 1];

                // use the expression parser to figure out if we're in a grouping...
                var spans = textView.BufferGraph.MapDownToFirstMatch(
                    tokens[tokenIndex].Span,
                    SpanTrackingMode.EdgePositive,
                    PythonContentTypePrediciate
                );
                if (spans.Count == 0) {
                    return indentation;
                }
                
                var revParser = new ReverseExpressionParser(
                        spans[0].Snapshot,
                        spans[0].Snapshot.TextBuffer,
                        spans[0].Snapshot.CreateTrackingSpan(
                            spans[0].Span,
                            SpanTrackingMode.EdgePositive
                        )
                    );

                var tokenStack = new System.Collections.Generic.Stack<ClassificationSpan>();
                tokenStack.Push(null);  // end with an implicit newline
                bool endAtNextNull = false;

                foreach (var token in revParser) {
                    tokenStack.Push(token);
                    if (token == null && endAtNextNull) {
                        break;
                    } else if (token != null &&
                       token.ClassificationType == revParser.Classifier.Provider.Keyword &&
                       PythonKeywords.IsOnlyStatementKeyword(token.Span.GetText())) {
                        endAtNextNull = true;
                    }
                }

                var indentStack = new System.Collections.Generic.Stack<LineInfo>();
                var current = LineInfo.Empty;

                while (tokenStack.Count > 0) {
                    var token = tokenStack.Pop();
                    if (token == null) {
                        current.NeedsUpdate = true;
                    } else if (token.IsOpenGrouping()) {
                        indentStack.Push(current);
                        var start = token.Span.Start;
                        var line2 = start.GetContainingLine();
                        var next = tokenStack.Count > 0 ? tokenStack.Peek() : null;
                        if (next != null && next.Span.End <= line2.End) {
                            current = new LineInfo {
                                Indentation = start.Position - line2.Start.Position + 1
                            };
                        } else {
                            current = new LineInfo {
                                Indentation = GetIndentation(line2.GetText(), tabSize) + tabSize
                            };
                        }
                    } else if (token.IsCloseGrouping()) {
                        if (indentStack.Count > 0) {
                            current = indentStack.Pop();
                        } else {
                            current.NeedsUpdate = true;
                        }
                    } else if (ReverseExpressionParser.IsExplicitLineJoin(token)) {
                        while (token != null && tokenStack.Count > 0) {
                            token = tokenStack.Pop();
                        }
                    } else if (current.NeedsUpdate == true) {
                        var line2 = token.Span.Start.GetContainingLine();
                        current = new LineInfo {
                            Indentation = GetIndentation(line2.GetText(), tabSize)
                        };
                    }

                    if (token != null && ShouldDedentAfterKeyword(token)) {     // dedent after some statements
                        current.ShouldDedentAfter = true;
                    }

                    if (token != null && token.Span.GetText() == ":" &&         // indent after a colon
                        indentStack.Count == 0) {                               // except in a grouping
                        current.ShouldIndentAfter = true;
                        // If the colon isn't at the end of the line, cancel it out.
                        // If the following is a ShouldDedentAfterKeyword, only one dedent will occur.
                        current.ShouldDedentAfter = (tokenStack.Count != 0 && tokenStack.Peek() != null);
                    }
                }

                indentation = current.Indentation +
                    (current.ShouldIndentAfter ? tabSize : 0) -
                    (current.ShouldDedentAfter ? tabSize : 0);
            }

            // Map indentation back to the view's text buffer.
            int offset = 0;
            var viewLineStart = textView.BufferGraph.MapUpToSnapshot(line.Start, PointTrackingMode.Positive, PositionAffinity.Successor, textView.TextSnapshot);
            if (viewLineStart.HasValue) {
                offset = viewLineStart.Value.Position - viewLineStart.Value.GetContainingLine().Start.Position;
            }

            return offset + indentation;
        }
Exemple #16
0
        private static CompletionAnalysis TrySpecialCompletions(IServiceProvider serviceProvider, ITextSnapshot snapshot, ITrackingSpan span, ITrackingPoint point, CompletionOptions options) {
            var snapSpan = span.GetSpan(snapshot);
            var buffer = snapshot.TextBuffer;
            var classifier = buffer.GetPythonClassifier();
            if (classifier == null) {
                return null;
            }

            var parser = new ReverseExpressionParser(snapshot, buffer, span);
            var statementRange = parser.GetStatementRange();
            if (!statementRange.HasValue) {
                statementRange = snapSpan.Start.GetContainingLine().Extent;
            }
            if (snapSpan.Start < statementRange.Value.Start) {
                return null;
            }

            var tokens = classifier.GetClassificationSpans(new SnapshotSpan(statementRange.Value.Start, snapSpan.Start));
            if (tokens.Count > 0) {
                // Check for context-sensitive intellisense
                var lastClass = tokens[tokens.Count - 1];

                if (lastClass.ClassificationType == classifier.Provider.Comment) {
                    // No completions in comments
                    return CompletionAnalysis.EmptyCompletionContext;
                } else if (lastClass.ClassificationType == classifier.Provider.StringLiteral) {
                    // String completion
                    if (lastClass.Span.Start.GetContainingLine().LineNumber == lastClass.Span.End.GetContainingLine().LineNumber) {
                        return new StringLiteralCompletionList(span, buffer, options);
                    } else {
                        // multi-line string, no string completions.
                        return CompletionAnalysis.EmptyCompletionContext;
                    }
                } else if (lastClass.ClassificationType == classifier.Provider.Operator &&
                    lastClass.Span.GetText() == "@") {

                    if (tokens.Count == 1) {
                    return new DecoratorCompletionAnalysis(span, buffer, options);
                    }
                    // TODO: Handle completions automatically popping up
                    // after '@' when it is used as a binary operator.
                } else if (CompletionAnalysis.IsKeyword(lastClass, "def")) {
                    return new OverrideCompletionAnalysis(span, buffer, options);
                }

                // Import completions
                var first = tokens[0];
                if (CompletionAnalysis.IsKeyword(first, "import")) {
                    return ImportCompletionAnalysis.Make(tokens, span, buffer, options);
                } else if (CompletionAnalysis.IsKeyword(first, "from")) {
                    return FromImportCompletionAnalysis.Make(tokens, span, buffer, options);
                } else if (CompletionAnalysis.IsKeyword(first, "raise") || CompletionAnalysis.IsKeyword(first, "except")) {
                    if (tokens.Count == 1 ||
                        lastClass.ClassificationType.IsOfType(PythonPredefinedClassificationTypeNames.Comma) ||
                        (lastClass.IsOpenGrouping() && tokens.Count < 3)) {
                        return new ExceptionCompletionAnalysis(span, buffer, options);
                    }
                }
                return null;
            } else if ((tokens = classifier.GetClassificationSpans(snapSpan.Start.GetContainingLine().ExtentIncludingLineBreak)).Count > 0 &&
               tokens[0].ClassificationType == classifier.Provider.StringLiteral) {
                // multi-line string, no string completions.
                return CompletionAnalysis.EmptyCompletionContext;
            } else if (snapshot.IsReplBufferWithCommand()) {
                return CompletionAnalysis.EmptyCompletionContext;
            }

            return null;
        }
Exemple #17
0
        private bool ShouldTriggerIdentifierCompletionSession(out bool commitByDefault) {
            commitByDefault = true;

            if (!_provider.PythonService.AdvancedOptions.AutoListIdentifiers ||
                !_provider.PythonService.AdvancedOptions.AutoListMembers) {
                return false;
            }

            var caretPoint = _textView.GetPythonCaret();
            if (!caretPoint.HasValue) {
                return false;
            }

            var snapshot = caretPoint.Value.Snapshot;

            var statement = new ReverseExpressionParser(
                snapshot,
                snapshot.TextBuffer,
                snapshot.CreateTrackingSpan(caretPoint.Value.Position, 0, SpanTrackingMode.EdgeNegative)
            ).GetStatementRange();
            if (!statement.HasValue || caretPoint.Value <= statement.Value.Start) {
                return false;
            }

            var text = new SnapshotSpan(statement.Value.Start, caretPoint.Value).GetText();
            if (string.IsNullOrEmpty(text)) {
                return false;
            }

            var analysis = _textView.GetAnalysisEntry(snapshot.TextBuffer, _serviceProvider);
            if (analysis == null) {
                return false;
            }

            var languageVersion = analysis.Analyzer.InterpreterFactory.Configuration.Version.ToLanguageVersion();
            PythonAst ast;
            using (var parser = Parser.CreateParser(new StringReader(text), languageVersion, new ParserOptions { Verbatim = true })) {
                ast = parser.ParseSingleStatement();
            }

            var walker = new ExpressionCompletionWalker(caretPoint.Value.Position - statement.Value.Start.Position);
            ast.Walk(walker);
            commitByDefault = walker.CommitByDefault;
            return walker.CanComplete;
        }
Exemple #18
0
        /// <summary>
        /// Gets a ExpressionAnalysis for the expression at the provided span.  If the span is in
        /// part of an identifier then the expression is extended to complete the identifier.
        /// </summary>
        internal static ExpressionAnalysis AnalyzeExpression(IServiceProvider serviceProvider, ITextSnapshot snapshot, ITrackingSpan span, bool forCompletion = true) {
            var buffer = snapshot.TextBuffer;
            ReverseExpressionParser parser = new ReverseExpressionParser(snapshot, buffer, span);

            var loc = parser.Span.GetSpan(parser.Snapshot.Version);
            var exprRange = parser.GetExpressionRange(forCompletion);

            if (exprRange == null) {
                return ExpressionAnalysis.Empty;
            }

            string text = exprRange.Value.GetText();

            var applicableSpan = parser.Snapshot.CreateTrackingSpan(
                exprRange.Value.Span,
                SpanTrackingMode.EdgeExclusive
            );

            IPythonProjectEntry entry;
            if (buffer.TryGetPythonProjectEntry(out entry) && entry.Analysis != null && text.Length > 0) {
                var lineNo = parser.Snapshot.GetLineNumberFromPosition(loc.Start);
                return new ExpressionAnalysis(
                    snapshot.TextBuffer.GetAnalyzer(serviceProvider),
                    text,
                    entry.Analysis,
                    loc.Start,
                    applicableSpan,
                    parser.Snapshot
                );
            }

            return ExpressionAnalysis.Empty;
        }
Exemple #19
0
        private static int CalculateIndentation(string baseline, ITextSnapshotLine line, IEditorOptions options, IClassifier classifier, ITextView textView)
        {
            int indentation = GetIndentation(baseline, options.GetTabSize());
            int tabSize     = options.GetIndentSize();
            var tokens      = classifier.GetClassificationSpans(line.Extent);

            if (tokens.Count > 0 && !IsUnterminatedStringToken(tokens[tokens.Count - 1]))
            {
                int tokenIndex = tokens.Count - 1;

                while (tokenIndex >= 0 &&
                       (tokens[tokenIndex].ClassificationType.IsOfType(PredefinedClassificationTypeNames.Comment) ||
                        tokens[tokenIndex].ClassificationType.IsOfType(PredefinedClassificationTypeNames.WhiteSpace)))
                {
                    tokenIndex--;
                }

                if (tokenIndex < 0)
                {
                    return(indentation);
                }

                if (ReverseExpressionParser.IsExplicitLineJoin(tokens[tokenIndex]))
                {
                    // explicit line continuation, we indent 1 level for the continued line unless
                    // we're already indented because of multiple line continuation characters.

                    indentation = GetIndentation(line.GetText(), options.GetTabSize());
                    var joinedLine = tokens[tokenIndex].Span.Start.GetContainingLine();
                    if (joinedLine.LineNumber > 0)
                    {
                        var prevLineSpans = classifier.GetClassificationSpans(tokens[tokenIndex].Span.Snapshot.GetLineFromLineNumber(joinedLine.LineNumber - 1).Extent);
                        if (prevLineSpans.Count == 0 || !ReverseExpressionParser.IsExplicitLineJoin(prevLineSpans[prevLineSpans.Count - 1]))
                        {
                            indentation += tabSize;
                        }
                    }
                    else
                    {
                        indentation += tabSize;
                    }

                    return(indentation);
                }

                string sline    = tokens[tokenIndex].Span.GetText();
                var    lastChar = sline.Length == 0 ? '\0' : sline[sline.Length - 1];

                // use the expression parser to figure out if we're in a grouping...
                var spans = textView.BufferGraph.MapDownToFirstMatch(
                    tokens[tokenIndex].Span,
                    SpanTrackingMode.EdgePositive,
                    PythonContentTypePrediciate
                    );
                if (spans.Count == 0)
                {
                    return(indentation);
                }

                var revParser = new ReverseExpressionParser(
                    spans[0].Snapshot,
                    spans[0].Snapshot.TextBuffer,
                    spans[0].Snapshot.CreateTrackingSpan(
                        spans[0].Span,
                        SpanTrackingMode.EdgePositive
                        )
                    );

                var tokenStack = new System.Collections.Generic.Stack <ClassificationSpan>();
                tokenStack.Push(null);  // end with an implicit newline
                bool endAtNextNull = false;

                foreach (var token in revParser)
                {
                    tokenStack.Push(token);
                    if (token == null && endAtNextNull)
                    {
                        break;
                    }
                    else if (token != null &&
                             token.ClassificationType == revParser.Classifier.Provider.Keyword &&
                             PythonKeywords.IsOnlyStatementKeyword(token.Span.GetText()))
                    {
                        endAtNextNull = true;
                    }
                }

                var indentStack = new System.Collections.Generic.Stack <LineInfo>();
                var current     = LineInfo.Empty;

                while (tokenStack.Count > 0)
                {
                    var token = tokenStack.Pop();
                    if (token == null)
                    {
                        current.NeedsUpdate = true;
                    }
                    else if (token.IsOpenGrouping())
                    {
                        indentStack.Push(current);
                        var start = token.Span.Start;
                        var line2 = start.GetContainingLine();
                        var next  = tokenStack.Count > 0 ? tokenStack.Peek() : null;
                        if (next != null && next.Span.End <= line2.End)
                        {
                            current = new LineInfo {
                                Indentation = start.Position - line2.Start.Position + 1
                            };
                        }
                        else
                        {
                            current = new LineInfo {
                                Indentation = GetIndentation(line2.GetText(), tabSize) + tabSize
                            };
                        }
                    }
                    else if (token.IsCloseGrouping())
                    {
                        if (indentStack.Count > 0)
                        {
                            current = indentStack.Pop();
                        }
                        else
                        {
                            current.NeedsUpdate = true;
                        }
                    }
                    else if (ReverseExpressionParser.IsExplicitLineJoin(token))
                    {
                        while (token != null && tokenStack.Count > 0)
                        {
                            token = tokenStack.Pop();
                        }
                    }
                    else if (current.NeedsUpdate == true)
                    {
                        var line2 = token.Span.Start.GetContainingLine();
                        current = new LineInfo {
                            Indentation = GetIndentation(line2.GetText(), tabSize)
                        };
                    }

                    if (token != null && ShouldDedentAfterKeyword(token))       // dedent after some statements
                    {
                        current.ShouldDedentAfter = true;
                    }

                    if (token != null && token.Span.GetText() == ":" &&         // indent after a colon
                        indentStack.Count == 0)                                 // except in a grouping
                    {
                        current.ShouldIndentAfter = true;
                        // If the colon isn't at the end of the line, cancel it out.
                        // If the following is a ShouldDedentAfterKeyword, only one dedent will occur.
                        current.ShouldDedentAfter = (tokenStack.Count != 0 && tokenStack.Peek() != null);
                    }
                }

                indentation = current.Indentation +
                              (current.ShouldIndentAfter ? tabSize : 0) -
                              (current.ShouldDedentAfter ? tabSize : 0);
            }

            // Map indentation back to the view's text buffer.
            int offset        = 0;
            var viewLineStart = textView.BufferGraph.MapUpToSnapshot(line.Start, PointTrackingMode.Positive, PositionAffinity.Successor, textView.TextSnapshot);

            if (viewLineStart.HasValue)
            {
                offset = viewLineStart.Value.Position - viewLineStart.Value.GetContainingLine().Start.Position;
            }

            return(offset + indentation);
        }
Exemple #20
0
        internal static MissingImportAnalysis GetMissingImports(IServiceProvider serviceProvider, ITextSnapshot snapshot, ITrackingSpan span) {
            ReverseExpressionParser parser = new ReverseExpressionParser(snapshot, snapshot.TextBuffer, span);
            var loc = span.GetSpan(snapshot.Version);
            int dummy;
            SnapshotPoint? dummyPoint;
            string lastKeywordArg;
            bool isParameterName;
            var exprRange = parser.GetExpressionRange(0, out dummy, out dummyPoint, out lastKeywordArg, out isParameterName);
            if (exprRange == null || isParameterName) {
                return MissingImportAnalysis.Empty;
            }

            IPythonProjectEntry entry;
            ModuleAnalysis analysis;
            if (!snapshot.TextBuffer.TryGetPythonProjectEntry(out entry) ||
                entry == null ||
                (analysis = entry.Analysis) == null) {
                return MissingImportAnalysis.Empty;
            }

            var text = exprRange.Value.GetText();
            if (string.IsNullOrEmpty(text)) {
                return MissingImportAnalysis.Empty;
            }

            var analyzer = analysis.ProjectState;
            var index = (parser.GetStatementRange() ?? span.GetSpan(snapshot)).Start.Position;

            var location = TranslateIndex(
                index,
                snapshot,
                analysis
            );
            var nameExpr = GetFirstNameExpression(analysis.GetAstFromText(text, location).Body);

            if (nameExpr != null && !IsImplicitlyDefinedName(nameExpr)) {
                var name = nameExpr.Name;
                lock (snapshot.TextBuffer.GetAnalyzer(serviceProvider)) {
                    var hasVariables = analysis.GetVariables(name, location).Any(IsDefinition);
                    var hasValues = analysis.GetValues(name, location).Any();

                    // if we have type information or an assignment to the variable we won't offer 
                    // an import smart tag.
                    if (!hasValues && !hasVariables) {
                        var applicableSpan = parser.Snapshot.CreateTrackingSpan(
                            exprRange.Value.Span,
                            SpanTrackingMode.EdgeExclusive
                        );
                        return new MissingImportAnalysis(name, analysis.ProjectState, applicableSpan);
                    }
                }
            }

            // if we have type information don't offer to add imports
            return MissingImportAnalysis.Empty;
        }
        internal static TextSpan? GetDataTipSpan(IWpfTextView wpfTextView, TextSpan selection) {
            // Adjust the span to expression boundaries.
            var snapshot = wpfTextView.TextSnapshot;
            var start = LineAndColumnNumberToSnapshotPoint(snapshot, selection.iStartLine, selection.iStartIndex);
            var end = LineAndColumnNumberToSnapshotPoint(snapshot, selection.iEndLine, selection.iEndIndex);

            // If this is a zero-length span (which it usually is, unless there's selection), adjust it
            // to cover one char to the right, since an empty span at the beginning of the expression does
            // not count as belonging to that expression;
            if (start == end && start.Position != snapshot.Length) {
                end += 1;
            }

            var snapshotSpan = new SnapshotSpan(start, end);
            var trackingSpan = snapshot.CreateTrackingSpan(snapshotSpan.Span, SpanTrackingMode.EdgeExclusive);
            var rep = new ReverseExpressionParser(snapshot, wpfTextView.TextBuffer, trackingSpan);
            var exprSpan = rep.GetExpressionRange(forCompletion: false);
            if (exprSpan == null) {
                return null;
            }

            // Check whether this is an expression with side effects - if it does, we don't want to show a data tip for it.
            string text = exprSpan.Value.GetText();
            var ast = new JSParser(text).Parse(new CodeSettings());

            var sideEffectsDetectingVisitor = new SideEffectsDetectingVisitor();
            ast.Walk(sideEffectsDetectingVisitor);
            if (sideEffectsDetectingVisitor.HasSideEffects) {
                return null;
            }

            TextSpan dataTipSpan;
            SnapshotPointToLineAndColumnNumber(exprSpan.Value.Start, out dataTipSpan.iStartLine, out dataTipSpan.iStartIndex);
            SnapshotPointToLineAndColumnNumber(exprSpan.Value.End, out dataTipSpan.iEndLine, out dataTipSpan.iEndIndex);
            return dataTipSpan;
        }