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 )); }
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; }
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; }
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); }
/// <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]); }
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; }
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; }
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; }
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; }
/// <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; }
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); }
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; }