diff --git a/CSharpRepl.Services/CSharpRepl.Services.csproj b/CSharpRepl.Services/CSharpRepl.Services.csproj
index d98f2bea..23b13cd7 100644
--- a/CSharpRepl.Services/CSharpRepl.Services.csproj
+++ b/CSharpRepl.Services/CSharpRepl.Services.csproj
@@ -41,7 +41,7 @@
-
+
diff --git a/CSharpRepl.Services/Roslyn/RoslynServices.cs b/CSharpRepl.Services/Roslyn/RoslynServices.cs
index 2c2325f7..6b97d2b4 100644
--- a/CSharpRepl.Services/Roslyn/RoslynServices.cs
+++ b/CSharpRepl.Services/Roslyn/RoslynServices.cs
@@ -340,6 +340,13 @@ public async Task IsTextCompleteStatementAsync(string text)
return root is null || SyntaxFactory.IsCompleteSubmission(root.SyntaxTree); // if something's wrong and we can't get the syntax tree, we don't want to prevent evaluation.
}
+ ///
+ /// When is a single-line statement the user didn't terminate (e.g. int i = 0),
+ /// returns it with the missing semicolon appended; otherwise .
+ /// It is deliberately editor-only; piped input is not auto-terminated.
+ ///
+ public string? TryAutoInsertSemicolon(string text) => AutoInsertSemicolon.TryAppend(text, parseOptions);
+
public async Task GetSpanToReplaceByCompletionAsync(string text, int caret, CancellationToken cancellationToken)
{
await Initialization.ConfigureAwait(false);
diff --git a/CSharpRepl.Services/Roslyn/Scripting/AutoInsertSemicolon.cs b/CSharpRepl.Services/Roslyn/Scripting/AutoInsertSemicolon.cs
new file mode 100644
index 00000000..9479f8c1
--- /dev/null
+++ b/CSharpRepl.Services/Roslyn/Scripting/AutoInsertSemicolon.cs
@@ -0,0 +1,54 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+using System;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace CSharpRepl.Services.Roslyn.Scripting;
+
+///
+/// If the user types "var x = 0" and submits, autoinsert the trailing semicolon.
+///
+/// This is an interactive editor concern: the prompt uses it to decide whether Enter submits and to
+/// show the inserted ; on the committed line. Evaluation itself is left untouched (piped input is not
+/// auto-terminated).
+///
+internal static class AutoInsertSemicolon
+{
+ ///
+ /// When is a single-line statement the user didn't terminate, returns it (trimmed)
+ /// with the missing semicolon appended, otherwise return
+ ///
+ public static string? TryAppend(string text, CSharpParseOptions parseOptions)
+ {
+ var trimmed = text.Trim();
+ var tree = CSharpSyntaxTree.ParseText(trimmed, parseOptions);
+ if (SyntaxFactory.IsCompleteSubmission(tree))
+ {
+ return null; // already complete — nothing to append (e.g. the expression `1 + 1`)
+ }
+
+ // Only single-line input is auto-semicoloned; a statement spread across lines (e.g. a fluent chain whose
+ // cursor is at the end of line 2) is left alone so it can keep being edited.
+ if (trimmed.AsSpan().IndexOfAny('\n', '\r') >= 0)
+ {
+ return null;
+ }
+
+ if (tree.GetRoot() is not CompilationUnitSyntax { Members: [.., FieldDeclarationSyntax { SemicolonToken.IsMissing: true }] })
+ {
+ return null;
+ }
+
+ var terminated = trimmed + ";";
+ var fixedTree = CSharpSyntaxTree.ParseText(terminated, parseOptions);
+ return SyntaxFactory.IsCompleteSubmission(fixedTree) &&
+ !fixedTree.GetDiagnostics().Any(d => d.Severity == DiagnosticSeverity.Error)
+ ? terminated
+ : null;
+ }
+}
diff --git a/CSharpRepl/CSharpRepl.csproj b/CSharpRepl/CSharpRepl.csproj
index 90c46fd0..39578d4e 100644
--- a/CSharpRepl/CSharpRepl.csproj
+++ b/CSharpRepl/CSharpRepl.csproj
@@ -25,7 +25,7 @@
-
+