예제 #1
0
        public override async Task <TextEdit[]> DocumentOnTypeFormatting(DocumentOnTypeFormattingParams @params, CancellationToken cancellationToken)
        {
            int targetLine; // One-indexed line number

            switch (@params.ch)
            {
            case "\n":
                targetLine = @params.position.line;
                break;

            case ";":
                targetLine = @params.position.line + 1;
                break;

            default:
                throw new ArgumentException("unexpected trigger character", nameof(@params.ch));
            }

            var uri = @params.textDocument.uri;

            if (!(ProjectFiles.GetEntry(uri) is IDocument doc))
            {
                return(Array.Empty <TextEdit>());
            }
            var part = ProjectFiles.GetPart(uri);

            using (var reader = doc.ReadDocument(part, out _)) {
                var lineFormatter = new LineFormatter(reader, Analyzer.LanguageVersion);
                return(lineFormatter.FormatLine(targetLine));
            }
        }
예제 #2
0
        private Dictionary <Uri, List <Reference> > FilterPrivatePrefixed(
            Dictionary <Uri, List <Reference> > refs,
            string originalName,
            string privatePrefix,
            string newName,
            Uri documentReaderUri,
            IDocumentReader documentReader)
        {
            // Filter out references depending on the private prefix, if any.
            foreach (var kvp in refs)
            {
                var ent = ProjectFiles.GetEntry(kvp.Key);

                var ast    = (ent as ProjectEntry).GetCurrentParse().Tree;
                var reader = kvp.Key == documentReaderUri
                    ? documentReader
                    : new DocumentReader(ent as IDocument, ProjectFiles.GetPart(kvp.Key));

                if (ast == null || reader == null)
                {
                    throw new InvalidOperationException(Resources.RenameVariable_NoInformationAvailableForVariable.FormatUI(originalName));
                }

                var fullName = $"{privatePrefix}{originalName}";
                for (var i = 0; i < kvp.Value.Count; i++)
                {
                    var reference = kvp.Value[i];
                    Debug.Assert(reference.range.start.line == reference.range.end.line);

                    var actualName = reader.ReadRange(reference.range, ast);
                    // If name does not match exactly, so we might be renaming a prefixed name
                    if (string.IsNullOrEmpty(privatePrefix))
                    {
                        // Not a mangled case, if names don't match, do not rename.
                        if (actualName != fullName)
                        {
                            kvp.Value.RemoveAt(i);
                            i--;
                        }
                        continue; // All good, rename.
                    }
                    // If renaming from private name to private name, rename the non-prefixed portion
                    if (actualName.StartsWith(privatePrefix) && newName.StartsWith("__"))
                    {
                        reference.range.start.character = reference.range.start.character + privatePrefix.Length;
                    }
                }
            }
            return(refs);
        }
        public override async Task <TextEdit[]> DocumentOnTypeFormatting(DocumentOnTypeFormattingParams @params, CancellationToken cancellationToken)
        {
            int targetLine;

            switch (@params.ch)
            {
            case "\n":
                targetLine = @params.position.line - 1;
                break;

            case ";":
            case ":":
                targetLine = @params.position.line;
                break;

            default:
                throw new ArgumentException("unexpected trigger character", nameof(@params.ch));
            }

            var uri = @params.textDocument.uri;

            if (!(ProjectFiles.GetEntry(uri) is IDocument doc))
            {
                return(Array.Empty <TextEdit>());
            }
            var part = ProjectFiles.GetPart(uri);

            using (var reader = doc.ReadDocument(part, out _)) {
                if (@params.ch == ":")
                {
                    return(await BlockFormatter.ProvideEdits(reader, @params.position, @params.options));
                }

                var lineFormatter  = new LineFormatter(reader, Analyzer.LanguageVersion);
                var edits          = lineFormatter.FormatLine(targetLine);
                var unmatchedToken = lineFormatter.UnmatchedToken(targetLine);

                if (unmatchedToken != null)
                {
                    var message = Resources.LineFormatter_UnmatchedToken.FormatInvariant(unmatchedToken.Value.token, unmatchedToken.Value.line + 1);
                    LogMessage(MessageType.Warning, message);
                }

                return(edits);
            }
        }
예제 #4
0
        public override async Task <WorkspaceEdit> Rename(RenameParams @params, CancellationToken cancellationToken)
        {
            ProjectFiles.GetEntry(@params.textDocument, @params._version, out var entry, out var tree);
            if (entry == null || tree == null)
            {
                throw new InvalidOperationException(Resources.RenameVariable_UnableGetExpressionAnalysis);
            }

            var references = await FindReferences(new ReferencesParams {
                textDocument = new TextDocumentIdentifier {
                    uri = @params.textDocument.uri
                },
                position = @params.position,
                context  = new ReferenceContext {
                    includeDeclaration = true
                }
            }, cancellationToken);

            if (references.Any(x => x._isModule))
            {
                throw new InvalidOperationException(Resources.RenameVariable_CannotRenameModuleName);
            }

            var definition = references.FirstOrDefault(r => r._kind == ReferenceKind.Definition);

            if (definition == null)
            {
                throw new InvalidOperationException(Resources.RenameVariable_CannotRename);
            }

            var definitionSpan = definition.range.ToLinearSpan(tree);
            var reader         = new DocumentReader(entry as IDocument, ProjectFiles.GetPart(definition.uri));
            var originalName   = reader.Read(definitionSpan.Start, definitionSpan.Length);

            if (originalName == null)
            {
                throw new InvalidOperationException(Resources.RenameVariable_SelectSymbol);
            }
            if (!references.Any(r => r._kind == ReferenceKind.Definition || r._kind == ReferenceKind.Reference))
            {
                throw new InvalidOperationException(Resources.RenameVariable_NoInformationAvailableForVariable.FormatUI(originalName));
            }

            // See https://en.wikipedia.org/wiki/Name_mangling, Python section.
            var privatePrefix = entry.Analysis.GetPrivatePrefix(definition.range.start);

            if (!string.IsNullOrEmpty(privatePrefix) && !string.IsNullOrEmpty(originalName) && originalName.StartsWithOrdinal(privatePrefix))
            {
                originalName = originalName.Substring(privatePrefix.Length + 1);
            }

            // Group by URI for more optimal document reading in FilterPrivatePrefixed
            var grouped = references
                          .GroupBy(x => x.uri)
                          .ToDictionary(g => g.Key, e => e.ToList());

            var refs = FilterPrivatePrefixed(grouped, originalName, privatePrefix, @params.newName, @params.textDocument.uri, reader);
            // Convert to Dictionary<Uri, TextEdit[]>
            var changes = refs
                          .ToDictionary(
                kvp => kvp.Key,
                kvp => kvp.Value.Select(t => new TextEdit {
                range   = t.range,
                newText = @params.newName
            }).ToArray());

            return(new WorkspaceEdit {
                changes = changes
            });
        }
예제 #5
0
        public override async Task <CompletionList> Completion(CompletionParams @params, CancellationToken cancellationToken)
        {
            var uri = @params.textDocument.uri;

            ProjectFiles.GetEntry(@params.textDocument, @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 != null ? await entry.GetAnalysisAsync(50, cancellationToken) : null;

            if (analysis == null)
            {
                TraceMessage($"No analysis found for {uri}");
                return(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(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 completions = members.ToArray();

            if (Settings.completion.addBrackets && (!filterKind.HasValue || CanHaveBrackets(filterKind.Value)))
            {
                foreach (var completionItem in completions.Where(ci => CanHaveBrackets(ci.kind)))
                {
                    completionItem.insertText      += "($0)";
                    completionItem.insertTextFormat = InsertTextFormat.Snippet;
                }
            }

            bool CanHaveBrackets(CompletionItemKind kind)
            => kind == CompletionItemKind.Constructor ||
            kind == CompletionItemKind.Function ||
            kind == CompletionItemKind.Method;

            var res = new CompletionList {
                items            = completions,
                _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");

            if (HandleOldStyleCompletionExtension(analysis as ModuleAnalysis, tree, @params.position, res))
            {
                return(res);
            }

            await InvokeExtensionsAsync((ext, token)
                                        => (ext as ICompletionExtension)?.HandleCompletionAsync(uri, analysis, tree, @params.position, res, cancellationToken), cancellationToken);

            return(res);
        }
        public override async Task <CompletionList> Completion(CompletionParams @params, CancellationToken cancellationToken)
        {
            var uri = @params.textDocument.uri;

            ProjectFiles.GetEntry(@params.textDocument, @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 != null ? await entry.GetAnalysisAsync(50, cancellationToken) : null;

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

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

            var members = string.IsNullOrEmpty(@params._expr)
                ? ctxt.GetCompletions()
                : ctxt.GetCompletionsFromString(@params._expr);

            if (members == null)
            {
                TraceMessage($"No completions at {@params.position} in {uri}");
                return(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
            };

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

            if (HandleOldStyleCompletionExtension(analysis as ModuleAnalysis, tree, @params.position, res))
            {
                return(res);
            }

            await InvokeExtensionsAsync((ext, token)
                                        => (ext as ICompletionExtension)?.HandleCompletionAsync(uri, analysis, tree, @params.position, res, cancellationToken), cancellationToken);

            return(res);
        }