diff --git a/src/Draco.Compiler.Tests/Semantics/CodeCompletionTests.cs b/src/Draco.Compiler.Tests/Semantics/CodeCompletionTests.cs index 6bd254924..51eb2afa9 100644 --- a/src/Draco.Compiler.Tests/Semantics/CodeCompletionTests.cs +++ b/src/Draco.Compiler.Tests/Semantics/CodeCompletionTests.cs @@ -17,7 +17,8 @@ private static ImmutableArray GetCompletions(SyntaxTree tree, Se service.AddProvider(new KeywordCompletionProvider()); service.AddProvider(new ExpressionCompletionProvider()); service.AddProvider(new MemberCompletionProvider()); - return service.GetCompletions(tree, model, cursor); + var cursorIndex = tree.SourceText.SyntaxPositionToIndex(cursor); + return service.GetCompletions(tree, model, cursorIndex); } [Fact] diff --git a/src/Draco.Compiler/Api/CodeCompletion/CompletionItem.cs b/src/Draco.Compiler/Api/CodeCompletion/CompletionItem.cs index 215aa2d89..da45094b8 100644 --- a/src/Draco.Compiler/Api/CodeCompletion/CompletionItem.cs +++ b/src/Draco.Compiler/Api/CodeCompletion/CompletionItem.cs @@ -12,12 +12,12 @@ namespace Draco.Compiler.Api.CodeCompletion; /// The of this completion. public sealed record class CompletionItem(ImmutableArray Edits, string DisplayText, ImmutableArray Symbols, CompletionKind Kind) { - public static CompletionItem Create(string text, SyntaxRange range, CompletionKind kind) => - new([new TextEdit(range, text)], text, [], kind); + public static CompletionItem Create(SourceText source, string text, SourceSpan span, CompletionKind kind) => + new([new TextEdit(source, span, text)], text, [], kind); - public static CompletionItem Create(string text, SyntaxRange range, ISymbol symbol, CompletionKind kind) => - new([new TextEdit(range, text)], text, [symbol], kind); + public static CompletionItem Create(SourceText source, string text, SourceSpan span, ISymbol symbol, CompletionKind kind) => + new([new TextEdit(source, span, text)], text, [symbol], kind); - public static CompletionItem Create(string text, SyntaxRange range, ImmutableArray symbols, CompletionKind kind) => - new([new TextEdit(range, text)], text, symbols, kind); + public static CompletionItem Create(SourceText source, string text, SourceSpan span, ImmutableArray symbols, CompletionKind kind) => + new([new TextEdit(source, span, text)], text, symbols, kind); } diff --git a/src/Draco.Compiler/Api/CodeCompletion/CompletionProvider.cs b/src/Draco.Compiler/Api/CodeCompletion/CompletionProvider.cs index 14ca86a53..d5dec7cfa 100644 --- a/src/Draco.Compiler/Api/CodeCompletion/CompletionProvider.cs +++ b/src/Draco.Compiler/Api/CodeCompletion/CompletionProvider.cs @@ -21,8 +21,9 @@ public abstract class CompletionProvider /// /// The for which this service will create suggestions. /// The for this . - /// Position of cursor in the . + /// Position of cursor in the as an index. /// Flag enum of current contexts. /// All the s this created. - public abstract ImmutableArray GetCompletionItems(SyntaxTree tree, SemanticModel semanticModel, SyntaxPosition cursor, CompletionContext contexts); + public abstract ImmutableArray GetCompletionItems( + SyntaxTree tree, SemanticModel semanticModel, int cursorIndex, CompletionContext contexts); } diff --git a/src/Draco.Compiler/Api/CodeCompletion/CompletionService.cs b/src/Draco.Compiler/Api/CodeCompletion/CompletionService.cs index 52b6f83a7..f67dc0ba8 100644 --- a/src/Draco.Compiler/Api/CodeCompletion/CompletionService.cs +++ b/src/Draco.Compiler/Api/CodeCompletion/CompletionService.cs @@ -24,30 +24,31 @@ public sealed class CompletionService /// /// The for which this service will create suggestions. /// The for this . - /// Position of cursor in the . + /// Index of the cursor in the . /// s from all s. - public ImmutableArray GetCompletions(SyntaxTree tree, SemanticModel semanticModel, SyntaxPosition cursor) + public ImmutableArray GetCompletions(SyntaxTree tree, SemanticModel semanticModel, int cursorIndex) { var result = ImmutableArray.CreateBuilder(); - var currentContext = this.GetCurrentContexts(tree, cursor); + var currentContext = this.GetCurrentContexts(tree, cursorIndex); foreach (var provider in this.providers) { if (provider.IsApplicableIn(currentContext)) { - result.AddRange(provider.GetCompletionItems(tree, semanticModel, cursor, currentContext)); + result.AddRange(provider.GetCompletionItems(tree, semanticModel, cursorIndex, currentContext)); } } return result.ToImmutable(); } /// - /// Gets current context based on location of in the . + /// Gets current context based on location of in the . /// /// The in which to find contexts. - /// The location in the . + /// The location in the . /// Flag enum of the currently valid s. - private CompletionContext GetCurrentContexts(SyntaxTree syntaxTree, SyntaxPosition cursor) + private CompletionContext GetCurrentContexts(SyntaxTree syntaxTree, int cursorIndex) { + var cursor = syntaxTree.IndexToSyntaxPosition(cursorIndex); var node = syntaxTree.Root.TraverseSubtreesAtCursorPosition(cursor).Last(); return node switch { diff --git a/src/Draco.Compiler/Api/CodeCompletion/ExpressionCompletionProvider.cs b/src/Draco.Compiler/Api/CodeCompletion/ExpressionCompletionProvider.cs index 8d25ac789..7f47ca278 100644 --- a/src/Draco.Compiler/Api/CodeCompletion/ExpressionCompletionProvider.cs +++ b/src/Draco.Compiler/Api/CodeCompletion/ExpressionCompletionProvider.cs @@ -16,40 +16,43 @@ public override bool IsApplicableIn(CompletionContext context) return context.HasFlag(CompletionContext.Expression) || context.HasFlag(CompletionContext.Type) || context.HasFlag(CompletionContext.Import); } - public override ImmutableArray GetCompletionItems(SyntaxTree tree, SemanticModel semanticModel, SyntaxPosition cursor, CompletionContext contexts) + public override ImmutableArray GetCompletionItems( + SyntaxTree tree, SemanticModel semanticModel, int cursorIndex, CompletionContext contexts) { + var cursor = tree.IndexToSyntaxPosition(cursorIndex); var syntax = tree.Root.TraverseSubtreesAtCursorPosition(cursor).LastOrDefault(); if (syntax is null) return []; var symbols = semanticModel.GetAllDefinedSymbols(syntax); - var range = (syntax as SyntaxToken)?.Range ?? new(cursor, 0); + var span = (syntax as SyntaxToken)?.Span ?? new(cursorIndex, 0); var completions = symbols // NOTE: Grouping by GetType is very error-prone, maybe we need a symbol "kind" .GroupBy(x => (x.GetType(), x.Name)) - .Select(x => GetCompletionItem([.. x], contexts, range)); + .Select(x => GetCompletionItem(tree.SourceText, [.. x], contexts, span)); return completions.OfType().ToImmutableArray(); } - private static CompletionItem? GetCompletionItem(ImmutableArray symbols, CompletionContext currentContexts, SyntaxRange range) => symbols.First() switch - { - TypeSymbol or TypeAliasSymbol when currentContexts.HasFlag(CompletionContext.Expression) - || currentContexts.HasFlag(CompletionContext.Type) => - CompletionItem.Create(symbols.First().Name, range, symbols, CompletionKind.Class), + private static CompletionItem? GetCompletionItem( + SourceText source, ImmutableArray symbols, CompletionContext currentContexts, SourceSpan span) => symbols.First() switch + { + TypeSymbol or TypeAliasSymbol when currentContexts.HasFlag(CompletionContext.Expression) + || currentContexts.HasFlag(CompletionContext.Type) => + CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.Class), - IVariableSymbol when currentContexts.HasFlag(CompletionContext.Expression) => - CompletionItem.Create(symbols.First().Name, range, symbols, CompletionKind.Variable), + IVariableSymbol when currentContexts.HasFlag(CompletionContext.Expression) => + CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.Variable), - PropertySymbol when currentContexts.HasFlag(CompletionContext.Expression) => - CompletionItem.Create(symbols.First().Name, range, symbols, CompletionKind.Property), + PropertySymbol when currentContexts.HasFlag(CompletionContext.Expression) => + CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.Property), - // We need the type context here for qualified type references - ModuleSymbol when currentContexts.HasFlag(CompletionContext.Expression) - || currentContexts.HasFlag(CompletionContext.Type) - || currentContexts.HasFlag(CompletionContext.Import) => - CompletionItem.Create(symbols.First().Name, range, symbols, CompletionKind.Module), + // We need the type context here for qualified type references + ModuleSymbol when currentContexts.HasFlag(CompletionContext.Expression) + || currentContexts.HasFlag(CompletionContext.Type) + || currentContexts.HasFlag(CompletionContext.Import) => + CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.Module), - FunctionSymbol fun when !fun.IsSpecialName && currentContexts.HasFlag(CompletionContext.Expression) => - CompletionItem.Create(symbols.First().Name, range, symbols, CompletionKind.Function), + FunctionSymbol fun when !fun.IsSpecialName && currentContexts.HasFlag(CompletionContext.Expression) => + CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.Function), - _ => null, - }; + _ => null, + }; } diff --git a/src/Draco.Compiler/Api/CodeCompletion/KeywordCompletionProvider.cs b/src/Draco.Compiler/Api/CodeCompletion/KeywordCompletionProvider.cs index f2fe38ccd..6d87c8764 100644 --- a/src/Draco.Compiler/Api/CodeCompletion/KeywordCompletionProvider.cs +++ b/src/Draco.Compiler/Api/CodeCompletion/KeywordCompletionProvider.cs @@ -10,29 +10,29 @@ namespace Draco.Compiler.Api.CodeCompletion; /// public sealed class KeywordCompletionProvider : CompletionProvider { - private static ImmutableArray GetDeclarationKeywords(SyntaxRange range) => + private static ImmutableArray GetDeclarationKeywords(SourceText source, SourceSpan span) => [ - CompletionItem.Create("import", range, CompletionKind.Keyword), - CompletionItem.Create("var", range, CompletionKind.Keyword), - CompletionItem.Create("val", range, CompletionKind.Keyword), - CompletionItem.Create("func", range, CompletionKind.Keyword), - CompletionItem.Create("internal", range, CompletionKind.Keyword), - CompletionItem.Create("public", range, CompletionKind.Keyword), - CompletionItem.Create("module", range, CompletionKind.Keyword), + CompletionItem.Create(source, "import", span, CompletionKind.Keyword), + CompletionItem.Create(source, "var", span, CompletionKind.Keyword), + CompletionItem.Create(source, "val", span, CompletionKind.Keyword), + CompletionItem.Create(source, "func", span, CompletionKind.Keyword), + CompletionItem.Create(source, "internal", span, CompletionKind.Keyword), + CompletionItem.Create(source, "public", span, CompletionKind.Keyword), + CompletionItem.Create(source, "module", span, CompletionKind.Keyword), ]; - private static ImmutableArray GetExpressionKeywords(SyntaxRange range) => + private static ImmutableArray GetExpressionKeywords(SourceText source, SourceSpan span) => [ - CompletionItem.Create("if", range, CompletionKind.Keyword), - CompletionItem.Create("while", range, CompletionKind.Keyword), - CompletionItem.Create("for", range, CompletionKind.Keyword), - CompletionItem.Create("return", range, CompletionKind.Keyword), - CompletionItem.Create("goto", range, CompletionKind.Keyword), - CompletionItem.Create("and", range, CompletionKind.Keyword), - CompletionItem.Create("or", range, CompletionKind.Keyword), - CompletionItem.Create("not", range, CompletionKind.Keyword), - CompletionItem.Create("mod", range, CompletionKind.Keyword), - CompletionItem.Create("rem", range, CompletionKind.Keyword), + CompletionItem.Create(source, "if", span, CompletionKind.Keyword), + CompletionItem.Create(source, "while", span, CompletionKind.Keyword), + CompletionItem.Create(source, "for", span, CompletionKind.Keyword), + CompletionItem.Create(source, "return", span, CompletionKind.Keyword), + CompletionItem.Create(source, "goto", span, CompletionKind.Keyword), + CompletionItem.Create(source, "and", span, CompletionKind.Keyword), + CompletionItem.Create(source, "or", span, CompletionKind.Keyword), + CompletionItem.Create(source, "not", span, CompletionKind.Keyword), + CompletionItem.Create(source, "mod", span, CompletionKind.Keyword), + CompletionItem.Create(source, "rem", span, CompletionKind.Keyword), ]; public override bool IsApplicableIn(CompletionContext context) @@ -41,14 +41,16 @@ public override bool IsApplicableIn(CompletionContext context) return context.HasFlag(CompletionContext.Declaration) || context.HasFlag(CompletionContext.Expression); } - public override ImmutableArray GetCompletionItems(SyntaxTree tree, SemanticModel semanticModel, SyntaxPosition cursor, CompletionContext contexts) + public override ImmutableArray GetCompletionItems( + SyntaxTree tree, SemanticModel semanticModel, int cursorIndex, CompletionContext contexts) { + var cursor = tree.IndexToSyntaxPosition(cursorIndex); var syntax = tree.Root.TraverseSubtreesAtCursorPosition(cursor).LastOrDefault(); if (syntax is null) return []; - var range = (syntax as SyntaxToken)?.Range ?? new(cursor, 0); + var span = (syntax as SyntaxToken)?.Span ?? new(cursorIndex, 0); var result = ImmutableArray.CreateBuilder(); - if (contexts.HasFlag(CompletionContext.Expression)) result.AddRange(GetExpressionKeywords(range)); - if (contexts.HasFlag(CompletionContext.Declaration)) result.AddRange(GetDeclarationKeywords(range)); + if (contexts.HasFlag(CompletionContext.Expression)) result.AddRange(GetExpressionKeywords(tree.SourceText, span)); + if (contexts.HasFlag(CompletionContext.Declaration)) result.AddRange(GetDeclarationKeywords(tree.SourceText, span)); return result.ToImmutable(); } } diff --git a/src/Draco.Compiler/Api/CodeCompletion/MemberCompletionProvider.cs b/src/Draco.Compiler/Api/CodeCompletion/MemberCompletionProvider.cs index 4650d5bbe..260ac7e80 100644 --- a/src/Draco.Compiler/Api/CodeCompletion/MemberCompletionProvider.cs +++ b/src/Draco.Compiler/Api/CodeCompletion/MemberCompletionProvider.cs @@ -3,6 +3,7 @@ using System.Linq; using Draco.Compiler.Api.Semantics; using Draco.Compiler.Api.Syntax; +using Draco.Compiler.Internal.Symbols.Source; namespace Draco.Compiler.Api.CodeCompletion; @@ -17,19 +18,21 @@ public override bool IsApplicableIn(CompletionContext context) return context.HasFlag(CompletionContext.Expression) || context.HasFlag(CompletionContext.Type) || context.HasFlag(CompletionContext.Import); } - public override ImmutableArray GetCompletionItems(SyntaxTree tree, SemanticModel semanticModel, SyntaxPosition cursor, CompletionContext contexts) + public override ImmutableArray GetCompletionItems( + SyntaxTree tree, SemanticModel semanticModel, int cursorIndex, CompletionContext contexts) { + var cursor = tree.IndexToSyntaxPosition(cursorIndex); var nodesAtCursor = tree.Root.TraverseSubtreesAtCursorPosition(cursor); if (nodesAtCursor.LastOrDefault() is not SyntaxToken token) return []; var expr = token.Parent; - var range = token.Kind == TokenKind.Dot ? new SyntaxRange(token.Range.End, 0) : token.Range; + var span = token.Kind == TokenKind.Dot ? new SourceSpan(token.Span.End, 0) : token.Span; // If we can't get the accessed propery, we just return empty array if (!TryGetMemberAccess(tree, cursor, semanticModel, out var symbols)) return []; var completions = symbols // NOTE: Not very robust, just like in the other place // Also, duplication .GroupBy(x => (x.GetType(), x.Name)) - .Select(x => GetCompletionItem([.. x], contexts, range)); + .Select(x => GetCompletionItem(tree.SourceText, [.. x], contexts, span)); return completions.OfType().ToImmutableArray(); } @@ -74,26 +77,27 @@ public static bool TryDeconstructMemberAccess(SyntaxNode? node, [MaybeNullWhen(f } } - private static CompletionItem? GetCompletionItem(ImmutableArray symbols, CompletionContext currentContexts, SyntaxRange range) => symbols.First() switch - { - TypeSymbol when currentContexts.HasFlag(CompletionContext.Type) - || currentContexts.HasFlag(CompletionContext.Expression) => - CompletionItem.Create(symbols.First().Name, range, symbols, CompletionKind.Class), + private static CompletionItem? GetCompletionItem( + SourceText source, ImmutableArray symbols, CompletionContext currentContexts, SourceSpan span) => symbols.First() switch + { + TypeSymbol when currentContexts.HasFlag(CompletionContext.Type) + || currentContexts.HasFlag(CompletionContext.Expression) => + CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.Class), - ModuleSymbol when currentContexts.HasFlag(CompletionContext.Type) - || currentContexts.HasFlag(CompletionContext.Expression) - || currentContexts.HasFlag(CompletionContext.Import) => - CompletionItem.Create(symbols.First().Name, range, symbols, CompletionKind.Module), + ModuleSymbol when currentContexts.HasFlag(CompletionContext.Type) + || currentContexts.HasFlag(CompletionContext.Expression) + || currentContexts.HasFlag(CompletionContext.Import) => + CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.Module), - IVariableSymbol when currentContexts.HasFlag(CompletionContext.Expression) => - CompletionItem.Create(symbols.First().Name, range, symbols, CompletionKind.Variable), + IVariableSymbol when currentContexts.HasFlag(CompletionContext.Expression) => + CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.Variable), - PropertySymbol when currentContexts.HasFlag(CompletionContext.Expression) => - CompletionItem.Create(symbols.First().Name, range, symbols, CompletionKind.Property), + PropertySymbol when currentContexts.HasFlag(CompletionContext.Expression) => + CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.Property), - FunctionSymbol fun when !fun.IsSpecialName && currentContexts.HasFlag(CompletionContext.Expression) => - CompletionItem.Create(symbols.First().Name, range, symbols, CompletionKind.Function), + FunctionSymbol fun when !fun.IsSpecialName && currentContexts.HasFlag(CompletionContext.Expression) => + CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.Function), - _ => null, - }; + _ => null, + }; } diff --git a/src/Draco.Compiler/Api/CodeFixes/CodeFix.cs b/src/Draco.Compiler/Api/CodeFixes/CodeFix.cs index cf2ed6e25..04b1f38c0 100644 --- a/src/Draco.Compiler/Api/CodeFixes/CodeFix.cs +++ b/src/Draco.Compiler/Api/CodeFixes/CodeFix.cs @@ -1,4 +1,5 @@ using System.Collections.Immutable; +using Draco.Compiler.Api.Syntax; namespace Draco.Compiler.Api.CodeFixes; diff --git a/src/Draco.Compiler/Api/CodeFixes/CodeFixProvider.cs b/src/Draco.Compiler/Api/CodeFixes/CodeFixProvider.cs index bbc9f481c..ad34d2eee 100644 --- a/src/Draco.Compiler/Api/CodeFixes/CodeFixProvider.cs +++ b/src/Draco.Compiler/Api/CodeFixes/CodeFixProvider.cs @@ -19,7 +19,7 @@ public abstract class CodeFixProvider /// /// Diagnostic for which the shold provide es. /// The for which the es should be generated. - /// The of the . + /// The for which the es should be generated.. /// All es from this . - public abstract ImmutableArray GetCodeFixes(Diagnostic diagnostic, SyntaxTree tree, SyntaxRange range); + public abstract ImmutableArray GetCodeFixes(Diagnostic diagnostic, SyntaxTree tree, SourceSpan span); } diff --git a/src/Draco.Compiler/Api/CodeFixes/CodeFixService.cs b/src/Draco.Compiler/Api/CodeFixes/CodeFixService.cs index 38f6db22d..1ad612728 100644 --- a/src/Draco.Compiler/Api/CodeFixes/CodeFixService.cs +++ b/src/Draco.Compiler/Api/CodeFixes/CodeFixService.cs @@ -24,16 +24,16 @@ public sealed class CodeFixService /// /// The for which this service will create codefixes. /// The for this . - /// The of the this fixes. - /// es from all registered s. - public ImmutableArray GetCodeFixes(SyntaxTree tree, SemanticModel semanticModel, SyntaxRange range) + /// The to get the fixes for. + /// es from all registered s within . + public ImmutableArray GetCodeFixes(SyntaxTree tree, SemanticModel semanticModel, SourceSpan span) { var result = ImmutableArray.CreateBuilder(); foreach (var provider in this.providers) { foreach (var diagnostic in semanticModel.Diagnostics.IntersectBy(provider.DiagnosticCodes, x => x.Code)) { - result.AddRange(provider.GetCodeFixes(diagnostic, tree, range)); + result.AddRange(provider.GetCodeFixes(diagnostic, tree, span)); } } return result.ToImmutable(); diff --git a/src/Draco.Compiler/Api/CodeFixes/ImportCodeFixProvider.cs b/src/Draco.Compiler/Api/CodeFixes/ImportCodeFixProvider.cs index c562b40c9..047d190f6 100644 --- a/src/Draco.Compiler/Api/CodeFixes/ImportCodeFixProvider.cs +++ b/src/Draco.Compiler/Api/CodeFixes/ImportCodeFixProvider.cs @@ -13,24 +13,24 @@ public sealed class ImportCodeFixProvider : CodeFixProvider { public override ImmutableArray DiagnosticCodes { get; } = [SymbolResolutionErrors.ImportNotAtTop.Code]; - public override ImmutableArray GetCodeFixes(Diagnostic diagnostic, SyntaxTree tree, SyntaxRange range) + public override ImmutableArray GetCodeFixes(Diagnostic diagnostic, SyntaxTree tree, SourceSpan span) { // Checks if in the diagnostics is any diag this provider can fix, meaning it has the correct template and if it is in the range of this codefix - if (tree.TraverseSubtreesIntersectingRange(range).LastOrDefault(x => x is ImportDeclarationSyntax) is ImportDeclarationSyntax import - && diagnostic.Location.Range!.Value.Intersects(range)) + if (tree.TraverseSubtreesIntersectingSpan(span).LastOrDefault(x => x is ImportDeclarationSyntax) is ImportDeclarationSyntax import + && diagnostic.Location.Span!.Value.Intersects(span)) { return [ - new CodeFix("Move import statement to be at the top of the scope", this.TopOfScope(tree, range)), - new CodeFix("Move import statement to be at the top of the file", this.TopOfFile(tree, range)), + new CodeFix("Move import statement to be at the top of the scope", this.TopOfScope(tree, span)), + new CodeFix("Move import statement to be at the top of the file", this.TopOfFile(tree, span)), ]; } return []; } - private ImmutableArray TopOfScope(SyntaxTree tree, SyntaxRange range) + private ImmutableArray TopOfScope(SyntaxTree tree, SourceSpan span) { - var import = tree.TraverseSubtreesIntersectingRange(range).LastOrDefault(x => x is ImportDeclarationSyntax); + var import = tree.TraverseSubtreesIntersectingSpan(span).LastOrDefault(x => x is ImportDeclarationSyntax); if (import is null) return []; var newTree = import.Parent is DeclarationStatementSyntax ? tree.Reorder(import.Parent, 0) @@ -38,9 +38,9 @@ private ImmutableArray TopOfScope(SyntaxTree tree, SyntaxRange range) return tree.SyntaxTreeDiff(newTree); } - private ImmutableArray TopOfFile(SyntaxTree tree, SyntaxRange range) + private ImmutableArray TopOfFile(SyntaxTree tree, SourceSpan span) { - var import = tree.TraverseSubtreesIntersectingRange(range).LastOrDefault(x => x is ImportDeclarationSyntax); + var import = tree.TraverseSubtreesIntersectingSpan(span).LastOrDefault(x => x is ImportDeclarationSyntax); if (import is null) return []; var newTree = import.Parent is DeclarationStatementSyntax ? tree.Remove(import.Parent) diff --git a/src/Draco.Compiler/Api/Diagnostics/Location.cs b/src/Draco.Compiler/Api/Diagnostics/Location.cs index 2e8ff5222..b8681a84e 100644 --- a/src/Draco.Compiler/Api/Diagnostics/Location.cs +++ b/src/Draco.Compiler/Api/Diagnostics/Location.cs @@ -22,8 +22,15 @@ public abstract partial class Location /// public virtual SourceText SourceText => SourceText.None; + /// + /// The span of this location. + /// + public virtual SourceSpan? Span => null; + /// /// The range of this location. /// - public virtual SyntaxRange? Range => null; + public SyntaxRange? Range => this.Span is null + ? null + : this.SourceText.SourceSpanToSyntaxRange(this.Span.Value); } diff --git a/src/Draco.Compiler/Api/Diagnostics/SourceLocation.cs b/src/Draco.Compiler/Api/Diagnostics/SourceLocation.cs index 6e9aa798d..7728d36dd 100644 --- a/src/Draco.Compiler/Api/Diagnostics/SourceLocation.cs +++ b/src/Draco.Compiler/Api/Diagnostics/SourceLocation.cs @@ -5,10 +5,9 @@ namespace Draco.Compiler.Api.Diagnostics; /// /// Represents an in-source location. /// -internal sealed class SourceLocation(SourceText sourceText, SourceSpan span) : Location +internal sealed class SourceLocation(SourceText sourceText, SourceSpan Span) : Location { public override SourceText SourceText { get; } = sourceText; - public override SyntaxRange? Range { get; } = sourceText.SourceSpanToSyntaxRange(span); public SourceLocation(SyntaxTree syntaxTree, SourceSpan span) : this(syntaxTree.SourceText, span) diff --git a/src/Draco.Compiler/Api/Syntax/SyntaxNode.cs b/src/Draco.Compiler/Api/Syntax/SyntaxNode.cs index a329b6524..4c0e49548 100644 --- a/src/Draco.Compiler/Api/Syntax/SyntaxNode.cs +++ b/src/Draco.Compiler/Api/Syntax/SyntaxNode.cs @@ -214,7 +214,7 @@ public IEnumerable TraverseSubtreesIntersectingSpan(SourceSpan span) /// The position that has to be contained. /// All subtrees containing in parent-child order. public IEnumerable TraverseSubtreesAtPosition(SyntaxPosition position) => - this.TraverseSubtreesAtIndex(this.Tree.SourceText.SyntaxPositionToIndex(position)); + this.TraverseSubtreesAtIndex(this.Tree.SyntaxPositionToIndex(position)); /// /// Enumerates this subtree, yielding all descendant nodes intersecting the given range. @@ -222,7 +222,7 @@ public IEnumerable TraverseSubtreesAtPosition(SyntaxPosition positio /// The range to check for intersection with the nodes. /// All subtrees in intersecting in parent-child order. public IEnumerable TraverseSubtreesIntersectingRange(SyntaxRange range) => - this.TraverseSubtreesIntersectingSpan(this.Tree.SourceText.SyntaxRangeToSourceSpan(range)); + this.TraverseSubtreesIntersectingSpan(this.Tree.SyntaxRangeToSourceSpan(range)); public abstract void Accept(SyntaxVisitor visitor); public abstract TResult Accept(SyntaxVisitor visitor); diff --git a/src/Draco.Compiler/Api/Syntax/SyntaxTree.cs b/src/Draco.Compiler/Api/Syntax/SyntaxTree.cs index 542ac9ebc..d21a19eae 100644 --- a/src/Draco.Compiler/Api/Syntax/SyntaxTree.cs +++ b/src/Draco.Compiler/Api/Syntax/SyntaxTree.cs @@ -156,7 +156,7 @@ public TNode FindInChildren(int index = 0) /// Array of s. public ImmutableArray SyntaxTreeDiff(SyntaxTree other) => // TODO: We can use a better diff algo - [new TextEdit(this.Root.Range, other.ToString())]; + [new TextEdit(this.SourceText, this.Root.Span, other.ToString())]; /// /// Syntactically formats this . @@ -164,6 +164,34 @@ public ImmutableArray SyntaxTreeDiff(SyntaxTree other) => /// The formatted tree. public SyntaxTree Format() => Formatter.Format(this); + /// + /// Utility method to convert an index to a syntax position. + /// + /// The index to convert. + /// The equivalent syntax position to . + public SyntaxPosition IndexToSyntaxPosition(int index) => this.SourceText.IndexToSyntaxPosition(index); + + /// + /// Utility method to convert a syntax position to an index. + /// + /// The position to convert. + /// The equivalent index to . + public int SyntaxPositionToIndex(SyntaxPosition position) => this.SourceText.SyntaxPositionToIndex(position); + + /// + /// Utility method to convert a source span to a syntax range. + /// + /// The source span to convert. + /// The equivalent syntax range to . + public SyntaxRange SourceSpanToSyntaxRange(SourceSpan span) => this.SourceText.SourceSpanToSyntaxRange(span); + + /// + /// Utility method to convert a syntax range to a source span. + /// + /// The syntax range to convert. + /// The equivalent source span to . + public SourceSpan SyntaxRangeToSourceSpan(SyntaxRange range) => this.SourceText.SyntaxRangeToSourceSpan(range); + /// /// The internal root of the tree. /// diff --git a/src/Draco.Compiler/Api/Syntax/TextEdit.cs b/src/Draco.Compiler/Api/Syntax/TextEdit.cs new file mode 100644 index 000000000..28c58784b --- /dev/null +++ b/src/Draco.Compiler/Api/Syntax/TextEdit.cs @@ -0,0 +1,15 @@ +namespace Draco.Compiler.Api.Syntax; + +/// +/// Represents an edit in source text. +/// +/// The source text that will be edited. +/// The span of the text that will be replaced by . +/// The text that should be inserted into the source text. +public sealed record class TextEdit(SourceText Source, SourceSpan Span, string Text) +{ + /// + /// The range of the text that will be replaced by . + /// + public SyntaxRange Range => this.Source.SourceSpanToSyntaxRange(this.Span); +} diff --git a/src/Draco.Compiler/Api/TextEdit.cs b/src/Draco.Compiler/Api/TextEdit.cs deleted file mode 100644 index fabeaa8a6..000000000 --- a/src/Draco.Compiler/Api/TextEdit.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Draco.Compiler.Api.Syntax; - -namespace Draco.Compiler.Api; - -/// -/// Represents an edit in source text. -/// -/// The range of the text that will be replaced by . -/// The text that should be inserted into the source text. -public record class TextEdit(SyntaxRange Range, string Text); diff --git a/src/Draco.LanguageServer/Capabilities/CodeAction.cs b/src/Draco.LanguageServer/Capabilities/CodeAction.cs index d074a07d8..0705829b1 100644 --- a/src/Draco.LanguageServer/Capabilities/CodeAction.cs +++ b/src/Draco.LanguageServer/Capabilities/CodeAction.cs @@ -24,7 +24,9 @@ internal sealed partial class DracoLanguageServer : ICodeAction if (syntaxTree is null) return Task.FromResult(null as IList>); var semanticModel = compilation.GetSemanticModel(syntaxTree); - var fixes = this.codeFixService.GetCodeFixes(syntaxTree, semanticModel, Translator.ToCompiler(param.Range)); + var range = Translator.ToCompiler(param.Range); + var span = syntaxTree.SyntaxRangeToSourceSpan(range); + var fixes = this.codeFixService.GetCodeFixes(syntaxTree, semanticModel, span); var actions = new List>(); foreach (var fix in fixes) diff --git a/src/Draco.LanguageServer/Capabilities/CodeCompletion.cs b/src/Draco.LanguageServer/Capabilities/CodeCompletion.cs index 62c6c8dfd..4de19baf8 100644 --- a/src/Draco.LanguageServer/Capabilities/CodeCompletion.cs +++ b/src/Draco.LanguageServer/Capabilities/CodeCompletion.cs @@ -25,7 +25,8 @@ public Task> CompleteAsync(CompletionParams param, Cancell var semanticModel = compilation.GetSemanticModel(syntaxTree); var cursorPosition = Translator.ToCompiler(param.Position); - var completionItems = this.completionService.GetCompletions(syntaxTree, semanticModel, cursorPosition); + var cursorIndex = syntaxTree.SyntaxPositionToIndex(cursorPosition); + var completionItems = this.completionService.GetCompletions(syntaxTree, semanticModel, cursorIndex); return Task.FromResult>(completionItems.Select(Translator.ToLsp).ToList()); } } diff --git a/src/Draco.LanguageServer/Capabilities/Rename.cs b/src/Draco.LanguageServer/Capabilities/Rename.cs index c88deb252..056d770c2 100644 --- a/src/Draco.LanguageServer/Capabilities/Rename.cs +++ b/src/Draco.LanguageServer/Capabilities/Rename.cs @@ -82,7 +82,7 @@ private static IEnumerable FindAllAppearances( } } - private static TextEdit RenameNode(SyntaxNode original, string name) => original switch + private static Lsp.Model.TextEdit RenameNode(SyntaxNode original, string name) => original switch { ParameterSyntax p => RenameToken(p.Name, name), GenericParameterSyntax g => RenameToken(g.Name, name), @@ -102,7 +102,7 @@ SyntaxToken t when t.Parent is ForExpressionSyntax @for _ => throw new ArgumentOutOfRangeException(nameof(original)), }; - private static TextEdit RenameToken(SyntaxToken token, string name) => new() + private static Lsp.Model.TextEdit RenameToken(SyntaxToken token, string name) => new() { Range = Translator.ToLsp(token.Range), NewText = name, diff --git a/src/Draco.LanguageServer/Translator.cs b/src/Draco.LanguageServer/Translator.cs index 1af3882bb..f22f6e1a2 100644 --- a/src/Draco.LanguageServer/Translator.cs +++ b/src/Draco.LanguageServer/Translator.cs @@ -139,7 +139,7 @@ public static LspModels.SignatureInformation ToLsp(CompilerApi.Semantics.IFuncti }; } - public static LspModels.ITextEdit ToLsp(CompilerApi.TextEdit edit) => new LspModels.TextEdit() + public static LspModels.ITextEdit ToLsp(CompilerApi.Syntax.TextEdit edit) => new LspModels.TextEdit() { NewText = edit.Text, Range = ToLsp(edit.Range),