Ejemplo n.º 1
0
        public void DidChangeTextDocument(DidChangeTextDocumentParams @params, Action <IDocument> enqueueAction)
        {
            var changes = @params.contentChanges;

            if (changes == null)
            {
                return;
            }

            var uri = @params.textDocument.uri;
            var doc = _projectFiles.GetEntry(uri) as IDocument;

            if (doc == null)
            {
                return;
            }

            try {
                var part = _projectFiles.GetPart(uri);
                _log.TraceMessage($"Received changes for {uri}");

                var docVersion  = Math.Max(doc.GetDocumentVersion(part), 0);
                var fromVersion = Math.Max(@params.textDocument.version - 1 ?? docVersion, 0);

                if (fromVersion > docVersion && @params.contentChanges?.Any(c => c.range == null) != true)
                {
                    // Expected from version hasn't been seen yet, and there are no resets in this
                    // change, so enqueue it for later.
                    _log.TraceMessage($"Deferring changes for {uri} until version {fromVersion} is seen");
                    lock (_pendingChanges) {
                        _pendingChanges.Add(@params);
                    }
                    return;
                }

                var toVersion = @params.textDocument.version ?? (fromVersion + changes.Length);

                doc.UpdateDocument(part, new DocumentChangeSet(
                                       fromVersion,
                                       toVersion,
                                       changes.Select(c => new DocumentChange {
                    ReplacedSpan = c.range.GetValueOrDefault(),
                    WholeBuffer  = !c.range.HasValue,
                    InsertedText = c.text
                })
                                       ));

                DidChangeTextDocumentParams?next = null;
                lock (_pendingChanges) {
                    var notExpired = _pendingChanges
                                     .Where(p => p.textDocument.version.GetValueOrDefault() >= toVersion)
                                     .OrderBy(p => p.textDocument.version.GetValueOrDefault())
                                     .ToArray();

                    _pendingChanges.Clear();
                    if (notExpired.Any())
                    {
                        next = notExpired.First();
                        _pendingChanges.AddRange(notExpired.Skip(1));
                    }
                }
                if (next.HasValue)
                {
                    DidChangeTextDocument(next.Value, null);
                }
            } finally {
                if (enqueueAction != null)
                {
                    _log.TraceMessage($"Applied changes to {uri}");
                    enqueueAction(doc);
                }
            }
        }
Ejemplo n.º 2
0
 public int GetPart(TextDocumentIdentifier document) => _projectFiles.GetPart(document.uri);
Ejemplo n.º 3
0
        internal Task <CompletionList> Completion(CompletionParams @params, CancellationToken cancellationToken)
        {
            var uri = @params.textDocument.uri;

            ProjectFiles.GetAnalysis(@params.textDocument, @params.position, @params._version, out var entry, out var tree);
            TraceMessage($"Completions in {uri} at {@params.position}");

            tree = GetParseTree(entry, uri, cancellationToken, out var version) ?? tree;
            var analysis = entry?.Analysis;

            if (analysis == null)
            {
                TraceMessage($"No analysis found for {uri}");
                return(Task.FromResult(new CompletionList()));
            }

            var opts = GetOptions(@params.context);
            var ctxt = new CompletionAnalysis(analysis, tree, @params.position, opts, _displayTextBuilder, this,
                                              () => entry.ReadDocument(ProjectFiles.GetPart(uri), out _));
            var members = ctxt.GetCompletionsFromString(@params._expr) ?? ctxt.GetCompletions();

            if (members == null)
            {
                TraceMessage($"Do not trigger at {@params.position} in {uri}");
                return(Task.FromResult(new CompletionList()));
            }

            if (!Settings.completion.showAdvancedMembers)
            {
                members = members.Where(m => !m.label.StartsWith("__"));
            }

            var filterKind = @params.context?._filterKind;

            if (filterKind.HasValue && filterKind != CompletionItemKind.None)
            {
                TraceMessage($"Only returning {filterKind.Value} items");
                members = members.Where(m => m.kind == filterKind.Value);
            }

            var res = new CompletionList {
                items            = members.ToArray(),
                _expr            = ctxt.ParentExpression?.ToCodeString(tree, CodeFormattingOptions.Traditional),
                _commitByDefault = ctxt.ShouldCommitByDefault,
                _allowSnippet    = ctxt.ShouldAllowSnippets
            };

            SourceLocation trigger = @params.position;

            if (ctxt.ApplicableSpan.HasValue)
            {
                res._applicableSpan = ctxt.ApplicableSpan;
            }
            else if (ctxt.Node != null)
            {
                var span = ctxt.Node.GetSpan(tree);
                if (@params.context?.triggerKind == CompletionTriggerKind.TriggerCharacter)
                {
                    if (span.End > trigger)
                    {
                        span = new SourceSpan(span.Start, trigger);
                    }
                }
                if (span.End != span.Start)
                {
                    res._applicableSpan = span;
                }
            }
            else if (@params.context?.triggerKind == CompletionTriggerKind.TriggerCharacter)
            {
                var ch = @params.context?.triggerCharacter.FirstOrDefault() ?? '\0';
                res._applicableSpan = new SourceSpan(
                    trigger.Line,
                    Tokenizer.IsIdentifierStartChar(ch) ? Math.Max(1, trigger.Column - 1) : trigger.Column,
                    trigger.Line,
                    trigger.Column
                    );
            }

            LogMessage(MessageType.Info, $"Found {res.items.Length} completions for {uri} at {@params.position} after filtering");

            var evt = PostProcessCompletion;

            if (evt != null)
            {
                var e = new Extensibility.CompletionEventArgs(analysis, tree, @params.position, res);
                try {
                    evt(this, e);
                    res       = e.CompletionList;
                    res.items = res.items ?? Array.Empty <CompletionItem>();
                    LogMessage(MessageType.Info, $"Found {res.items.Length} completions after hooks");
                } catch (Exception ex) when(!ex.IsCriticalException())
                {
                    // We do not replace res in this case.
                    LogMessage(MessageType.Error, $"Error while post-processing completions: {ex}");
                }
            }
            return(Task.FromResult(res));
        }