Skip to content

Commit

Permalink
Repl services (#447)
Browse files Browse the repository at this point in the history
* Added some minimal smart indentation

* Added some minimal smart indentation

* Revert "Added some minimal smart indentation"

This reverts commit bb41b8f.

* Revert "Added some minimal smart indentation"

This reverts commit 9ad45c8.

* Basic syntax highlighting

* Update ConfigurationDefaults.cs

* Weak completion added

* Simple filtering

* Update CompletionService.cs
  • Loading branch information
LPeter1997 authored Aug 27, 2024
1 parent caf883a commit 1133427
Show file tree
Hide file tree
Showing 12 changed files with 265 additions and 53 deletions.
21 changes: 15 additions & 6 deletions src/Draco.Compiler/Api/CodeCompletion/CompletionKind.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,19 @@ namespace Draco.Compiler.Api.CodeCompletion;
/// </summary>
public enum CompletionKind
{
Variable,
Function,
Class,
Module,
Property,
Keyword,
DeclarationKeyword,
VisibilityKeyword,
ControlFlowKeyword,

VariableName,
ParameterName,
ModuleName,
FunctionName,
PropertyName,
FieldName,

ReferenceTypeName,
ValueTypeName,

Operator,
}
3 changes: 3 additions & 0 deletions src/Draco.Compiler/Api/CodeCompletion/CompletionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ private CompletionContext GetCurrentContexts(SyntaxTree syntaxTree, int cursorIn
BlockFunctionBodySyntax => CompletionContext.Declaration | CompletionContext.Expression,
_ => node.Parent switch
{
// Special case, we are in a script
NameExpressionSyntax expr when expr.Parent is ScriptEntrySyntax =>
CompletionContext.Declaration | CompletionContext.Expression,
// Type expression
NameTypeSyntax => CompletionContext.Type,
// Parameter name declaration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,27 +32,44 @@ public override ImmutableArray<CompletionItem> GetCompletionItems(
}

private static CompletionItem? GetCompletionItem(
SourceText source, ImmutableArray<ISymbol> symbols, CompletionContext currentContexts, SourceSpan span) => symbols.First() switch
SourceText source, ImmutableArray<ISymbol> symbols, CompletionContext currentContexts, SourceSpan span)
{
var sym = symbols.First();
// NOTE: We should have something for this in the API
while (sym is IAliasSymbol alias) sym = alias.Substitution;
return sym switch
{
ITypeSymbol or IAliasSymbol when currentContexts.HasFlag(CompletionContext.Expression)
|| currentContexts.HasFlag(CompletionContext.Type) =>
CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.Class),
ITypeSymbol t when currentContexts.HasFlag(CompletionContext.Expression)
|| currentContexts.HasFlag(CompletionContext.Type) =>
CompletionItem.Create(
source,
t.Name,
span,
symbols,
t.IsValueType ? CompletionKind.ValueTypeName : CompletionKind.ReferenceTypeName),

IParameterSymbol when currentContexts.HasFlag(CompletionContext.Expression) =>
CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.ParameterName),

IVariableSymbol when currentContexts.HasFlag(CompletionContext.Expression) =>
CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.Variable),
CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.VariableName),

IPropertySymbol when currentContexts.HasFlag(CompletionContext.Expression) =>
CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.Property),
CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.PropertyName),

IFieldSymbol when currentContexts.HasFlag(CompletionContext.Expression) =>
CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.FieldName),

// We need the type context here for qualified type references
IModuleSymbol when currentContexts.HasFlag(CompletionContext.Expression)
|| currentContexts.HasFlag(CompletionContext.Type)
|| currentContexts.HasFlag(CompletionContext.Import) =>
CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.Module),
|| currentContexts.HasFlag(CompletionContext.Type)
|| currentContexts.HasFlag(CompletionContext.Import) =>
CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.ModuleName),

IFunctionSymbol fun when !fun.IsSpecialName && currentContexts.HasFlag(CompletionContext.Expression) =>
CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.Function),
CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.FunctionName),

_ => null,
};
}
}
36 changes: 19 additions & 17 deletions src/Draco.Compiler/Api/CodeCompletion/KeywordCompletionProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,29 @@ public sealed class KeywordCompletionProvider : CompletionProvider
{
private static ImmutableArray<CompletionItem> GetDeclarationKeywords(SourceText source, SourceSpan span) =>
[
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),
CompletionItem.Create(source, "import", span, CompletionKind.DeclarationKeyword),
CompletionItem.Create(source, "var", span, CompletionKind.DeclarationKeyword),
CompletionItem.Create(source, "val", span, CompletionKind.DeclarationKeyword),
CompletionItem.Create(source, "func", span, CompletionKind.DeclarationKeyword),
CompletionItem.Create(source, "module", span, CompletionKind.DeclarationKeyword),

CompletionItem.Create(source, "internal", span, CompletionKind.VisibilityKeyword),
CompletionItem.Create(source, "public", span, CompletionKind.VisibilityKeyword),
];

private static ImmutableArray<CompletionItem> GetExpressionKeywords(SourceText source, SourceSpan span) =>
[
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),
CompletionItem.Create(source, "if", span, CompletionKind.ControlFlowKeyword),
CompletionItem.Create(source, "while", span, CompletionKind.ControlFlowKeyword),
CompletionItem.Create(source, "for", span, CompletionKind.ControlFlowKeyword),
CompletionItem.Create(source, "return", span, CompletionKind.ControlFlowKeyword),
CompletionItem.Create(source, "goto", span, CompletionKind.ControlFlowKeyword),

CompletionItem.Create(source, "and", span, CompletionKind.Operator),
CompletionItem.Create(source, "or", span, CompletionKind.Operator),
CompletionItem.Create(source, "not", span, CompletionKind.Operator),
CompletionItem.Create(source, "mod", span, CompletionKind.Operator),
CompletionItem.Create(source, "rem", span, CompletionKind.Operator),
];

public override bool IsApplicableIn(CompletionContext context)
Expand Down
25 changes: 18 additions & 7 deletions src/Draco.Compiler/Api/CodeCompletion/MemberCompletionProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,23 +79,34 @@ public static bool TryDeconstructMemberAccess(SyntaxNode? node, [MaybeNullWhen(f
private static CompletionItem? GetCompletionItem(
SourceText source, ImmutableArray<ISymbol> symbols, CompletionContext currentContexts, SourceSpan span) => symbols.First() switch
{
ITypeSymbol when currentContexts.HasFlag(CompletionContext.Type)
|| currentContexts.HasFlag(CompletionContext.Expression) =>
CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.Class),
ITypeSymbol t when currentContexts.HasFlag(CompletionContext.Type)
|| currentContexts.HasFlag(CompletionContext.Expression) =>
CompletionItem.Create(
source,
t.Name,
span,
symbols,
t.IsValueType ? CompletionKind.ValueTypeName : CompletionKind.ReferenceTypeName),

IModuleSymbol when currentContexts.HasFlag(CompletionContext.Type)
|| currentContexts.HasFlag(CompletionContext.Expression)
|| currentContexts.HasFlag(CompletionContext.Import) =>
CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.Module),
CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.ModuleName),

IParameterSymbol when currentContexts.HasFlag(CompletionContext.Expression) =>
CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.ParameterName),

IVariableSymbol when currentContexts.HasFlag(CompletionContext.Expression) =>
CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.Variable),
CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.VariableName),

IPropertySymbol when currentContexts.HasFlag(CompletionContext.Expression) =>
CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.Property),
CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.PropertyName),

IFieldSymbol when currentContexts.HasFlag(CompletionContext.Expression) =>
CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.FieldName),

IFunctionSymbol fun when !fun.IsSpecialName && currentContexts.HasFlag(CompletionContext.Expression) =>
CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.Function),
CompletionItem.Create(source, symbols.First().Name, span, symbols, CompletionKind.FunctionName),

_ => null,
};
Expand Down
25 changes: 20 additions & 5 deletions src/Draco.Compiler/Api/Scripting/ReplSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,23 @@ public static bool IsCompleteEntry(string text)
return SyntaxFacts.IsCompleteEntry(tree.Root);
}

/// <summary>
/// The metadata references used in the session.
/// </summary>
public ImmutableArray<MetadataReference> MetadataReferences => this.context.MetadataReferences;

/// <summary>
/// The global imports used in the session.
/// </summary>
public GlobalImports GlobalImports => this.context.GlobalImports;

/// <summary>
/// The last compilation in the session.
/// </summary>
public Compilation? LastCompilation => this.previousEntries.Count == 0
? null
: this.previousEntries[^1].Compilation;

private readonly List<Script<object?>> previousEntries = [];
private readonly ReplContext context = new();

Expand Down Expand Up @@ -150,11 +167,9 @@ public ExecutionResult<TResult> Evaluate<TResult>(SyntaxNode node)

private Script<object?> MakeScript(SyntaxTree tree) => Script.Create(
syntaxTree: tree,
globalImports: this.context.GlobalImports,
metadataReferences: this.context.MetadataReferences,
previousCompilation: this.previousEntries.Count == 0
? null
: this.previousEntries[^1].Compilation,
globalImports: this.GlobalImports,
metadataReferences: this.MetadataReferences,
previousCompilation: this.LastCompilation,
assemblyLoadContext: this.context.AssemblyLoadContext);

private static SyntaxTree ToSyntaxTree(SyntaxNode node) => node switch
Expand Down
9 changes: 9 additions & 0 deletions src/Draco.Compiler/Api/Syntax/SyntaxTree.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,15 @@ public TNode FindInChildren<TNode>(int index = 0)
/// <returns>All subtrees in intersecting <paramref name="range"/> in parent-child order.</returns>
public IEnumerable<SyntaxNode> TraverseSubtreesIntersectingRange(SyntaxRange range) => this.Root.TraverseSubtreesIntersectingRange(range);

/// <summary>
/// Enumerates this subtree, yielding all descendant nodes that are involved with a cursor position.
/// This differs from <see cref="TraverseSubtreesAtPosition(SyntaxPosition)"/> in a sense, because a
/// cursor cares about things immediately before or after it.
/// </summary>
/// <param name="position">The position of the cursor.</param>
/// <returns>All subtrees involved with <paramref name="position"/> in parent-child order.</returns>
public IEnumerable<SyntaxNode> TraverseSubtreesAtCursorPosition(SyntaxPosition position) => this.Root.TraverseSubtreesAtCursorPosition(position);

/// <summary>
/// Reorders the <see cref="SyntaxTree"/> that contains <paramref name="toReorder"/> node and puts <paramref name="toReorder"/> to specified <paramref name="position"/> in the original <see cref="SyntaxList"/>.
/// </summary>
Expand Down
24 changes: 18 additions & 6 deletions src/Draco.LanguageServer/Translator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,24 @@ public static LspModels.CompletionItem ToLsp(CompilerApi.CodeCompletion.Completi

public static LspModels.CompletionItemKind ToLsp(CompilerApi.CodeCompletion.CompletionKind kind) => kind switch
{
CompilerApi.CodeCompletion.CompletionKind.Variable => LspModels.CompletionItemKind.Variable,
CompilerApi.CodeCompletion.CompletionKind.Function => LspModels.CompletionItemKind.Function,
CompilerApi.CodeCompletion.CompletionKind.Keyword => LspModels.CompletionItemKind.Keyword,
CompilerApi.CodeCompletion.CompletionKind.Class => LspModels.CompletionItemKind.Class,
CompilerApi.CodeCompletion.CompletionKind.Module => LspModels.CompletionItemKind.Module,
CompilerApi.CodeCompletion.CompletionKind.Property => LspModels.CompletionItemKind.Property,
CompilerApi.CodeCompletion.CompletionKind.ControlFlowKeyword
or CompilerApi.CodeCompletion.CompletionKind.DeclarationKeyword
or CompilerApi.CodeCompletion.CompletionKind.VisibilityKeyword => LspModels.CompletionItemKind.Keyword,

CompilerApi.CodeCompletion.CompletionKind.VariableName
or CompilerApi.CodeCompletion.CompletionKind.ParameterName => LspModels.CompletionItemKind.Variable,

CompilerApi.CodeCompletion.CompletionKind.ModuleName => LspModels.CompletionItemKind.Module,

CompilerApi.CodeCompletion.CompletionKind.FunctionName => LspModels.CompletionItemKind.Method,

CompilerApi.CodeCompletion.CompletionKind.PropertyName => LspModels.CompletionItemKind.Property,

CompilerApi.CodeCompletion.CompletionKind.FieldName => LspModels.CompletionItemKind.Field,

CompilerApi.CodeCompletion.CompletionKind.ReferenceTypeName => LspModels.CompletionItemKind.Class,
CompilerApi.CodeCompletion.CompletionKind.ValueTypeName => LspModels.CompletionItemKind.Struct,

_ => throw new System.ArgumentOutOfRangeException(nameof(kind)),
};

Expand Down
6 changes: 6 additions & 0 deletions src/Draco.Repl/Configuration.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using Draco.Compiler.Api.Diagnostics;
using Draco.Compiler.Api.Syntax;
using PrettyPrompt.Highlighting;

namespace Draco.Repl;
Expand All @@ -24,6 +25,11 @@ internal sealed class Configuration
/// </summary>
public ColorScheme<InterfaceColor> InterfaceColors { get; set; } = ConfigurationDefaults.GetInterfaceColors();

/// <summary>
/// The colors used for syntax highlighting.
/// </summary>
public ColorScheme<SyntaxColoring> SyntaxColors { get; set; } = ConfigurationDefaults.GetSyntaxColors();

/// <summary>
/// The default imports for the REPL session.
/// </summary>
Expand Down
48 changes: 48 additions & 0 deletions src/Draco.Repl/ConfigurationDefaults.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Draco.Compiler.Api.Syntax;
using PrettyPrompt.Highlighting;

namespace Draco.Repl;
Expand All @@ -18,4 +19,51 @@ public static ColorScheme<InterfaceColor> GetInterfaceColors()
colors.Set(InterfaceColor.ErrorColor, AnsiColor.BrightRed);
return colors;
}

/// <summary>
/// Retrieves the default syntax colors.
/// </summary>
/// <returns>The default syntax colors.</returns>
public static ColorScheme<SyntaxColoring> GetSyntaxColors()
{
var colors = new ColorScheme<SyntaxColoring>();

colors.Set(SyntaxColoring.LineComment, AnsiColor.Rgb(87, 166, 74));
colors.Set(SyntaxColoring.DeclarationKeyword, AnsiColor.Rgb(96, 139, 78));

var stringLiteralColor = AnsiColor.Rgb(214, 157, 133);
colors.Set(SyntaxColoring.StringQuotes, stringLiteralColor);
colors.Set(SyntaxColoring.StringContent, stringLiteralColor);
colors.Set(SyntaxColoring.CharacterQuotes, stringLiteralColor);
colors.Set(SyntaxColoring.CharacterContent, stringLiteralColor);

var escapeSeqColor = AnsiColor.Rgb(255, 214, 142);
colors.Set(SyntaxColoring.InterpolationQuotes, escapeSeqColor);
colors.Set(SyntaxColoring.EscapeSequence, escapeSeqColor);

var declarationColor = AnsiColor.Rgb(86, 156, 214);
colors.Set(SyntaxColoring.DeclarationKeyword, declarationColor);
colors.Set(SyntaxColoring.VisibilityKeyword, declarationColor);
colors.Set(SyntaxColoring.ControlFlowKeyword, AnsiColor.Rgb(215, 160, 223));

colors.Set(SyntaxColoring.BooleanLiteral, declarationColor);
colors.Set(SyntaxColoring.NumberLiteral, AnsiColor.Rgb(181, 206, 168));

colors.Set(SyntaxColoring.FunctionName, AnsiColor.Rgb(220, 220, 170));
colors.Set(SyntaxColoring.ReferenceTypeName, AnsiColor.Rgb(78, 201, 176));
colors.Set(SyntaxColoring.ValueTypeName, AnsiColor.Rgb(115, 194, 145));

var variableColor = AnsiColor.Rgb(156, 217, 240);
colors.Set(SyntaxColoring.VariableName, variableColor);
colors.Set(SyntaxColoring.ParameterName, variableColor);
colors.Set(SyntaxColoring.FieldName, AnsiColor.BrightWhite);
colors.Set(SyntaxColoring.PropertyName, AnsiColor.BrightWhite);
colors.Set(SyntaxColoring.ModuleName, AnsiColor.BrightWhite);

colors.Set(SyntaxColoring.Punctuation, AnsiColor.BrightWhite);
colors.Set(SyntaxColoring.Operator, AnsiColor.BrightWhite);
colors.Set(SyntaxColoring.Parenthesis, AnsiColor.BrightWhite);

return colors;
}
}
2 changes: 1 addition & 1 deletion src/Draco.Repl/Loop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public async Task Run()
session.AddImports(configuration.DefaultImports);

await using var prompt = new Prompt(
callbacks: new ReplPromptCallbacks(),
callbacks: new ReplPromptCallbacks(configuration, session),
configuration: new PromptConfiguration(
prompt: configuration.GetFormattedPrompt()));

Expand Down
Loading

0 comments on commit 1133427

Please sign in to comment.