예제 #1
0
            public ClassifierHelper(string code, PythonLanguageVersion version)
            {
                _view = new PythonEditor("", version);

                var providers = _view.VS.ComponentModel.GetExtensions <IClassifierProvider>().ToArray();

                _provider1 = providers.OfType <PythonClassifierProvider>().Single();
                _provider2 = providers.OfType <PythonAnalysisClassifierProvider>().Single();

                _classificationsReady1 = new ManualResetEventSlim();
                _classificationsReady2 = new ManualResetEventSlim();

                AstClassifier.ClassificationChanged += (s, e) => _classificationsReady1.SetIfNotDisposed();
                var startVersion = _view.CurrentSnapshot.Version;

                AnalysisClassifier.ClassificationChanged += (s, e) => {
                    try {
                        var bi = PythonTextBufferInfo.TryGetForBuffer(_view.View.TextView.TextBuffer);
                        if (bi?.LastAnalysisReceivedVersion == null)
                        {
                            return;
                        }
                        // make sure we have classifications from the version we analyzed after
                        // setting the text below.
                        if (bi.LastAnalysisReceivedVersion.VersionNumber > startVersion.VersionNumber)
                        {
                            _classificationsReady2.SetIfNotDisposed();
                        }
                    } catch (Exception ex) {
                        _excInfo = ExceptionDispatchInfo.Capture(ex);
                    }
                };

                _view.Text = code;
            }
예제 #2
0
        private static void ValidateBufferContents(IEnumerable <ITextSnapshot> snapshots, AP.FileUpdateResponse response)
        {
#if DEBUG
            if (response.newCode == null)
            {
                return;
            }

            foreach (var snapshot in snapshots)
            {
                var bi = PythonTextBufferInfo.TryGetForBuffer(snapshot.TextBuffer);
                if (bi == null)
                {
                    continue;
                }

                string newCode;
                if (!response.newCode.TryGetValue(bi.AnalysisBufferId, out newCode))
                {
                    continue;
                }

                if (newCode.TrimEnd() != snapshot.GetText().TrimEnd())
                {
                    Console.Error.WriteLine($"New Code: [{newCode}]");
                    Console.Error.WriteLine($"Snapshot: [{snapshot.GetText()}]");
                    Debug.Fail("Buffer content mismatch");
                }
            }
#endif
        }
예제 #3
0
 async Task IPythonTextBufferInfoEventSink.PythonTextBufferEventAsync(PythonTextBufferInfo sender, PythonTextBufferInfoEventArgs e)
 {
     if (e.Event == PythonTextBufferInfoEvents.NewAnalysis)
     {
         await OnNewAnalysis(sender, e.AnalysisEntry);
     }
 }
예제 #4
0
 private void AssertLines(PythonTextBufferInfo buffer, params int[] lengths)
 {
     AssertLines(
         buffer.LocationTracker.GetLineLocations(buffer.Buffer.CurrentSnapshot.Version.VersionNumber),
         lengths
         );
 }
예제 #5
0
        public void BufferSync_Issue3570()
        {
            // https://github.com/Microsoft/PTVS/issues/3570

            var buffer = new MockTextBuffer("line");
            var bi     = PythonTextBufferInfo.ForBuffer(null, buffer);

            bi.AddSentSnapshot(buffer.CurrentSnapshot);

            Assert.AreEqual(new SourceLocation(1, 5), bi.LocationTracker.GetSourceLocation(4, 0));

            using (var e = buffer.CreateEdit()) {
                e.Insert(e.Snapshot.Length, "\r\n");
                e.Apply();
            }

            using (var e = buffer.CreateEdit()) {
                e.Insert(e.Snapshot.Length, "    c");
                e.Apply();
            }

            using (var e = buffer.CreateEdit()) {
                e.Insert(e.Snapshot.Length, "o");
                e.Apply();
            }

            var updates    = BufferParser.GetUpdatesForSnapshot(bi, buffer.CurrentSnapshot).ToArray();
            var changeInfo = string.Join(", ", updates
                                         .Select(u => string.Join(", ", u.changes.Select(c => $"({c.startLine},{c.startColumn},'{c.newText}')")))
                                         .Select(u => $"[{u}]"));

            Assert.AreEqual("[(1,5,'\r\n')], [(2,1,'    c')], [(2,6,'o')]", changeInfo);
        }
예제 #6
0
        private static void ValidateBufferContents(IEnumerable <ITextSnapshot> snapshots, AP.FileUpdateResponse response)
        {
#if DEBUG
            if (response.newCode == null)
            {
                return;
            }

            foreach (var snapshot in snapshots)
            {
                var bi = PythonTextBufferInfo.TryGetForBuffer(snapshot.TextBuffer);
                if (bi == null)
                {
                    continue;
                }

                string newCode;
                if (!response.newCode.TryGetValue(bi.AnalysisBufferId, out newCode))
                {
                    continue;
                }

                Debug.Assert(newCode.TrimEnd() == snapshot.GetText().TrimEnd(), "Buffer content mismatch");
            }
#endif
        }
예제 #7
0
        internal PythonTextBufferInfo GetBufferInfo()
        {
            var bi = PythonTextBufferInfo.TryGetForBuffer(TextBuffer);

            Debug.Assert(bi != null, "Getting completions from uninitialized buffer " + TextBuffer.ToString());
            return(bi);
        }
 async Task IPythonTextBufferInfoEventSink.PythonTextBufferEventAsync(PythonTextBufferInfo sender, PythonTextBufferInfoEventArgs e)
 {
     if (_triggerEvents.Contains(e.Event))
     {
         await OnNewAnalysis(sender, e.AnalysisEntry);
     }
 }
예제 #9
0
        /// <summary>
        /// Wired to parser event for when the parser has completed parsing a new tree and we need
        /// to update the navigation bar with the new data.
        /// </summary>
        async Task IPythonTextBufferInfoEventSink.PythonTextBufferEventAsync(PythonTextBufferInfo sender, PythonTextBufferInfoEventArgs e)
        {
            if (e.Event == PythonTextBufferInfoEvents.NewParseTree)
            {
                var dropDownBar = _dropDownBar;
                if (dropDownBar == null)
                {
                    return;
                }

                var navigations = await _uiThread.InvokeTask(() => e.AnalysisEntry.Analyzer.GetNavigationsAsync(_textView));

                lock (_navigationsLock) {
                    _navigations = navigations;
                    for (int i = 0; i < _curSelection.Length; i++)
                    {
                        _curSelection[i] = -1;
                    }
                }

                Action callback = () => CaretPositionChanged(
                    this,
                    new CaretPositionChangedEventArgs(
                        _textView,
                        _textView.Caret.Position,
                        _textView.Caret.Position
                        )
                    );

                try {
                    await _dispatcher.BeginInvoke(callback, DispatcherPriority.Background);
                } catch (TaskCanceledException) {
                }
            }
        }
예제 #10
0
        internal int RemoveBuffer(ITextBuffer subjectBuffer)
        {
            int result;
            var bi = PythonTextBufferInfo.TryGetForBuffer(subjectBuffer);

            lock (this) {
                if (bi != null)
                {
                    var existing = _buffers.FirstOrDefault(b => b.Buffer == bi);
                    if (existing != null && existing.Release())
                    {
                        _buffers = _buffers.Where(b => b != existing).ToArray();

                        bi.RemoveSink(this);

                        VsProjectAnalyzer.DisconnectErrorList(bi);
                        _bufferIdMapping.Remove(bi.AnalysisBufferId);
                        bi.SetAnalysisBufferId(-1);

                        bi.Buffer.Properties.RemoveProperty(typeof(PythonTextBufferInfo));
                    }
                }
                result = _buffers.Length;
            }

            return(result);
        }
예제 #11
0
 internal PythonAnalysisClassifier(PythonAnalysisClassifierProvider provider, PythonTextBufferInfo buffer)
 {
     _provider                     = provider;
     _buffer                       = buffer;
     _buffer.OnNewAnalysis        += OnNewAnalysis;
     _buffer.OnContentTypeChanged += BufferContentTypeChanged;
 }
예제 #12
0
        internal static AP.ChangeInfo[] GetChanges(PythonTextBufferInfo buffer, ITextVersion curVersion)
        {
            var changes = new List <AP.ChangeInfo>();

            if (curVersion.Changes != null)
            {
                foreach (var change in curVersion.Changes)
                {
                    var oldPos = buffer.LocationTracker.GetSourceLocation(change.OldPosition, curVersion.VersionNumber);
                    var oldEnd = buffer.LocationTracker.GetSourceLocation(change.OldEnd, curVersion.VersionNumber);
                    changes.Add(new AP.ChangeInfo {
                        startLine   = oldPos.Line,
                        startColumn = oldPos.Column,
                        endLine     = oldEnd.Line,
                        endColumn   = oldEnd.Column,
                        newText     = change.NewText,
#if DEBUG
                        _startIndex = change.OldPosition,
                        _endIndex   = change.OldEnd
#endif
                    });
                }
            }

#if DEBUG
            Debug.WriteLine("Getting changes for version {0}", curVersion.VersionNumber);
            foreach (var c in changes)
            {
                Debug.WriteLine($" - ({c.startLine}, {c.startColumn})-({c.endLine}, {c.endColumn}): \"{c.newText}\"");
            }
#endif
            return(changes.ToArray());
        }
예제 #13
0
        public void BasicProjectTest()
        {
            var sln = Generator.Project(
                "HelloWorld",
                ProjectGenerator.Compile("server", "")
                ).Generate();

            using (var vs = sln.ToMockVs()) {
                Assert.IsNotNull(vs.WaitForItem("HelloWorld", "Python Environments"));
                Assert.IsNotNull(vs.WaitForItem("HelloWorld", "References"));
                Assert.IsNotNull(vs.WaitForItem("HelloWorld", "Search Paths"));
                Assert.IsNotNull(vs.WaitForItem("HelloWorld", "server.py"));
                var view = vs.OpenItem("HelloWorld", "server.py");

                var bi = PythonTextBufferInfo.TryGetForBuffer(view.TextView.TextBuffer);
                for (int retries = 20; retries > 0 && bi.AnalysisEntry == null; --retries)
                {
                    Thread.Sleep(500);
                }

                view.Invoke(() => view.Type("import"));
                view.Invoke(() => view.Type(" "));

                using (var sh = view.WaitForSession <ICompletionSession>()) {
                    AssertUtil.Contains(sh.Session.Completions(), "sys");
                }
            }
        }
예제 #14
0
        private Task OnTextContentChangedAsync(PythonTextBufferInfo sender, TextContentChangedEventArgs e)
        {
            if (e == null)
            {
                Debug.Fail("Invalid type passed to event");
            }

            var snapshot = e.After;

            if (!snapshot.IsReplBufferWithCommand())
            {
                _tokenCache.EnsureCapacity(snapshot.LineCount);

                var tokenizer = GetTokenizer(snapshot);
                foreach (var change in e.Changes)
                {
                    if (change.LineCountDelta > 0)
                    {
                        _tokenCache.InsertLines(snapshot.GetLineNumberFromPosition(change.NewEnd) + 1 - change.LineCountDelta, change.LineCountDelta);
                    }
                    else if (change.LineCountDelta < 0)
                    {
                        _tokenCache.DeleteLines(snapshot.GetLineNumberFromPosition(change.NewEnd) + 1, -change.LineCountDelta);
                    }

                    ApplyChange(tokenizer, snapshot, change.NewSpan);
                }
            }

            return(Task.CompletedTask);
        }
예제 #15
0
        private IEnumerable <CompletionResult> GetAvailableCompletions(PythonTextBufferInfo bufferInfo, SnapshotPoint point)
        {
            var analysis = bufferInfo.AnalysisEntry;

            if (analysis == null)
            {
                return(Enumerable.Empty <CompletionResult>());
            }

            var analyzer = analysis.Analyzer;

            lock (analyzer) {
                var location = VsProjectAnalyzer.TranslateIndex(
                    point.Position,
                    point.Snapshot,
                    analysis
                    );
                var parameters = Enumerable.Empty <CompletionResult>();
                var sigs       = analyzer.WaitForRequest(analyzer.GetSignaturesAsync(analysis, View, _snapshot, Span), "GetCompletions.GetSignatures");
                if (sigs != null && sigs.Signatures.Any())
                {
                    parameters = sigs.Signatures
                                 .SelectMany(s => s.Parameters)
                                 .Select(p => p.Name)
                                 .Distinct()
                                 .Select(n => new CompletionResult(n + "=", PythonMemberType.NamedArgument));
                }
                return(analyzer.WaitForRequest(analyzer.GetAllAvailableMembersAsync(analysis, location, _options.MemberOptions), "GetCompletions.GetAllAvailableMembers")
                       .MaybeEnumerate()
                       .Union(parameters, CompletionComparer.MemberEquality));
            }
        }
        private static bool IsValidBraceCompletionContext(PythonTextBufferInfo buffer, SnapshotPoint openingPoint)
        {
            if (buffer == null)
            {
                return(false);
            }

            Debug.Assert(openingPoint.Position >= 0, "SnapshotPoint.Position should always be zero or positive.");
            if (openingPoint.Position < 0)
            {
                return(false);
            }

            // If we have a token here that is in any of these categories, we do
            // not want to complete.
            var category = buffer.GetTokenAtPoint(openingPoint)?.Category ?? TokenCategory.None;

            return(!(
                       category == TokenCategory.Comment ||
                       category == TokenCategory.LineComment ||
                       category == TokenCategory.DocComment ||
                       category == TokenCategory.StringLiteral ||
                       category == TokenCategory.IncompleteMultiLineStringLiteral
                       ));
        }
예제 #17
0
 public OutliningTagger(PythonEditorServices services, PythonTextBufferInfo buffer)
 {
     _services = services;
     _buffer   = buffer;
     _buffer.OnNewParseTree += OnNewParseTree;
     Enabled = _services.Python?.AdvancedOptions.EnterOutliningModeOnOpen ?? true;
 }
예제 #18
0
        internal int Unregister(IVsDropdownBarManager manager)
        {
            _textView.Caret.PositionChanged -= CaretPositionChanged;

            // A buffer may have multiple DropDownBarClients, given one may open multiple CodeWindows
            // over a single buffer using Window/New Window
            List <DropDownBarClient> clients;

            if (_textView.Properties.TryGetProperty(typeof(DropDownBarClient), out clients))
            {
                clients.Remove(this);
                if (clients.Count == 0)
                {
                    _textView.Properties.RemoveProperty(typeof(DropDownBarClient));
                }
            }
            foreach (var tb in PythonTextBufferInfo.GetAllFromView(_textView))
            {
                tb.RemoveSink(this);
            }
#if DEBUG
            IVsDropdownBar       existing;
            IVsDropdownBarClient existingClient;
            if (ErrorHandler.Succeeded(manager.GetDropdownBar(out existing)) &&
                ErrorHandler.Succeeded(existing.GetClient(out existingClient)))
            {
                Debug.Assert(existingClient == this, "Unregistering the wrong dropdown client");
            }
#endif

            return(manager.RemoveDropdownBar());
        }
예제 #19
0
        internal AnalysisEntry GetAnalysisEntry()
        {
            var bi = PythonTextBufferInfo.TryGetForBuffer(TextBuffer);

            Debug.Assert(bi != null, "Getting completions from uninitialized buffer " + TextBuffer.ToString());
            Debug.Assert(bi?.AnalysisEntry != null, "Failed to get project entry for buffer " + TextBuffer.ToString());
            return(bi?.AnalysisEntry);
        }
예제 #20
0
 Task IPythonTextBufferInfoEventSink.PythonTextBufferEventAsync(PythonTextBufferInfo sender, PythonTextBufferInfoEventArgs e)
 {
     if (e.Event == PythonTextBufferInfoEvents.NewAnalysis)
     {
         return(OnNewAnalysisAsync(sender, e.AnalysisEntry));
     }
     return(Task.CompletedTask);
 }
예제 #21
0
 Task IPythonTextBufferInfoEventSink.PythonTextBufferEventAsync(PythonTextBufferInfo sender, PythonTextBufferInfoEventArgs e)
 {
     if (e.Event == PythonTextBufferInfoEvents.NewAnalysis)
     {
         SuggestedActionsChanged?.Invoke(this, EventArgs.Empty);
     }
     return(Task.CompletedTask);
 }
예제 #22
0
 /// <summary>
 /// Wired to parser event for when the parser has completed parsing a new tree and we need
 /// to update the navigation bar with the new data.
 /// </summary>
 async Task IPythonTextBufferInfoEventSink.PythonTextBufferEventAsync(PythonTextBufferInfo sender, PythonTextBufferInfoEventArgs e)
 {
     if (e.Event == PythonTextBufferInfoEvents.NewParseTree)
     {
         AnalysisEntry analysisEntry = e.AnalysisEntry;
         await RefreshNavigationsFromAnalysisEntry(analysisEntry);
     }
 }
예제 #23
0
 public void AddBuffer(PythonTextBufferInfo buffer)
 {
     buffer.OnNewAnalysis += OnNewAnalysis;
     if (buffer.AnalysisEntry?.IsAnalyzed == true)
     {
         OnNewAnalysis(buffer, EventArgs.Empty);
     }
 }
예제 #24
0
 async Task IPythonTextBufferInfoEventSink.PythonTextBufferEventAsync(PythonTextBufferInfo sender, PythonTextBufferInfoEventArgs e)
 {
     if (e.Event == PythonTextBufferInfoEvents.NewParseTree)
     {
         // TODO: Reconsider whether we process asynchronously and then marshal
         // at the end.
         await _services.Site.GetUIThread().InvokeTask(() => UpdateTagsAsync(sender, e.AnalysisEntry));
     }
 }
예제 #25
0
        internal bool GetPrecedingExpression(out string parentExpression, out SnapshotSpan expressionExtent)
        {
            parentExpression = string.Empty;
            expressionExtent = default(SnapshotSpan);

            var bi = PythonTextBufferInfo.TryGetForBuffer(_snapshot.TextBuffer);

            if (bi == null)
            {
                return(false);
            }
            var span = Span.GetSpan(_snapshot);
            var expr = bi.GetExpressionAtPoint(span, GetExpressionOptions.EvaluateMembers);

            if (expr != null)
            {
                parentExpression = expr.Value.GetText() ?? "";
                expressionExtent = new SnapshotSpan(expr.Value.Start, span.End);
                return(true);
            }

            expr = bi.GetExpressionAtPoint(span, GetExpressionOptions.Rename);
            if (expr != null)
            {
                expressionExtent = expr.Value;
                return(true);
            }

            var tok = bi.GetTokenAtPoint(span.End);

            if (tok == null)
            {
                expressionExtent = span;
                return(true);
            }

            switch (tok.Value.Category)
            {
            case TokenCategory.Delimiter:
            case TokenCategory.Grouping:
            case TokenCategory.Operator:
            case TokenCategory.WhiteSpace:
                // Expect top-level completions after these
                expressionExtent = span;
                return(true);

            case TokenCategory.BuiltinIdentifier:
            case TokenCategory.Identifier:
            case TokenCategory.Keyword:
                // Expect filtered top-level completions here
                // (but the return value is no different)
                expressionExtent = span;
                return(true);
            }

            return(false);
        }
예제 #26
0
        Task IPythonTextBufferInfoEventSink.PythonTextBufferEventAsync(PythonTextBufferInfo sender, PythonTextBufferInfoEventArgs e)
        {
            switch (e.Event)
            {
            case PythonTextBufferInfoEvents.TextContentChangedLowPriority:
                lock (this) {
                    // only immediately re-parse on line changes after we've seen a text change.
                    var ne = (e as PythonTextBufferInfoNestedEventArgs)?.NestedEventArgs as TextContentChangedEventArgs;

                    if (_parsing)
                    {
                        // we are currently parsing, just reque when we complete
                        _requeue = true;
                        _timer.Change(Timeout.Infinite, Timeout.Infinite);
                    }
                    else if (_parseImmediately)
                    {
                        // we are a test buffer, we should requeue immediately
                        Requeue();
                    }
                    else if (ne == null)
                    {
                        // failed to get correct type for this event
                        Debug.Fail("Failed to get correct event type");
                    }
                    else if (LineAndTextChanges(ne))
                    {
                        // user pressed enter, we should requeue immediately
                        Requeue();
                    }
                    else
                    {
                        // parse if the user doesn't do anything for a while.
                        _textChange = IncludesTextChanges(ne);
                        _timer.Change(ReparseDelay, Timeout.Infinite);
                    }
                }
                break;

            case PythonTextBufferInfoEvents.DocumentEncodingChanged:
                lock (this) {
                    if (_parsing)
                    {
                        // we are currently parsing, just reque when we complete
                        _requeue = true;
                        _timer.Change(Timeout.Infinite, Timeout.Infinite);
                    }
                    else
                    {
                        Requeue();
                    }
                }
                break;
            }
            return(Task.CompletedTask);
        }
 public void AddBuffer(PythonTextBufferInfo buffer)
 {
     buffer.AddSink(typeof(T), this);
     if (buffer.AnalysisEntry?.IsAnalyzed == true)
     {
         OnNewAnalysis(buffer, buffer.AnalysisEntry)
         .HandleAllExceptions(Services.Site, GetType())
         .DoNotWait();
     }
 }
예제 #28
0
 public void AddBuffer(PythonTextBufferInfo buffer)
 {
     buffer.AddSink(typeof(UnresolvedImportSquiggleProvider), this);
     if (buffer.AnalysisEntry?.IsAnalyzed == true)
     {
         OnNewAnalysis(buffer, buffer.AnalysisEntry)
         .HandleAllExceptions(_services.Site, GetType())
         .DoNotWait();
     }
 }
예제 #29
0
        public static Task <AnalysisEntry> GetAnalysisEntryAsync(this ITextBuffer buffer, PythonEditorServices services = null, CancellationToken cancellationToken = default(CancellationToken))
        {
            var bi = services == null?PythonTextBufferInfo.TryGetForBuffer(buffer) : services.GetBufferInfo(buffer);

            if (bi != null)
            {
                return(bi.GetAnalysisEntryAsync(cancellationToken));
            }
            return(Task.FromResult <AnalysisEntry>(null));
        }
예제 #30
0
 private void BufferGraph_GraphBuffersChanged(object sender, VisualStudio.Text.Projection.GraphBuffersChangedEventArgs e)
 {
     foreach (var b in e.RemovedBuffers)
     {
         PythonTextBufferInfo.TryGetForBuffer(b)?.RemoveSink(typeof(DropDownBarClient));
     }
     foreach (var b in e.AddedBuffers)
     {
         _services.GetBufferInfo(b).AddSink(typeof(DropDownBarClient), this);
     }
 }