Skip to content

Commit cc2ba91

Browse files
Merge pull request #24 from ktsu-dev/claude/default-document-class
Open on a class that does something, and fix loading an assignment
2 parents f84fbbd + 6d9c964 commit cc2ba91

3 files changed

Lines changed: 81 additions & 20 deletions

File tree

Coder.Editor/CoderEditorApp.cs

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -130,17 +130,35 @@ private void RememberWindow()
130130
/// <summary>
131131
/// Builds the document a fresh editor opens with.
132132
/// </summary>
133-
/// <returns>A function with one parameter and an empty body.</returns>
133+
/// <returns>A small class with two fields and two methods that do something with them.</returns>
134134
/// <remarks>
135-
/// An empty function rather than an empty graph: the AST has no node that means "nothing yet",
136-
/// and a user who has just opened the editor is better served by something to attach to than by
137-
/// a blank canvas and no way to start.
135+
/// Something to read rather than something to start from: the AST has no node meaning "nothing
136+
/// yet", and a canvas holding one empty function shows neither what the node kinds are nor how
137+
/// they connect. This one puts a field, a parameter, an assignment, a binary expression, a local
138+
/// and a return on screen at once, so the shape of the graph is legible before anything is added.
138139
/// </remarks>
139-
public static FunctionDeclaration NewDocument()
140+
public static ClassDeclaration NewDocument()
140141
{
141-
FunctionDeclaration function = new("newFunction") { ReturnType = "void" };
142-
function.Parameters.Add(new Parameter("value", "int"));
143-
return function;
142+
ClassDeclaration declaration = new("Counter");
143+
144+
declaration.Members.Add(new VariableDeclaration("count", "int", new LiteralExpression<int>(0)));
145+
declaration.Members.Add(new VariableDeclaration("step", "int", new LiteralExpression<int>(1)));
146+
147+
FunctionDeclaration add = new("Add") { ReturnType = "int" };
148+
add.Parameters.Add(new Parameter("amount", "int"));
149+
add.Body.Add(new AssignmentStatement(
150+
new VariableReference("count"),
151+
new BinaryExpression(new VariableReference("count"), BinaryOperator.Add, new VariableReference("amount"))));
152+
add.Body.Add(new ReturnStatement(new VariableReference("count")));
153+
declaration.Members.Add(add);
154+
155+
FunctionDeclaration next = new("Next") { ReturnType = "int" };
156+
next.Body.Add(new VariableDeclaration("result", "int",
157+
new BinaryExpression(new VariableReference("count"), BinaryOperator.Add, new VariableReference("step"))));
158+
next.Body.Add(new ReturnStatement(new VariableReference("result")));
159+
declaration.Members.Add(next);
160+
161+
return declaration;
144162
}
145163

146164
/// <summary>

Coder.Test/Editor/CoderEditorAppTests.cs

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,56 @@ private static CoderEditorApp NewApp(DocumentStore store, EditorSettings? settin
6565
settings ?? new EditorSettings());
6666

6767
/// <summary>
68-
/// Tests that a fresh editor opens with something to attach to rather than a blank canvas.
68+
/// Tests that a document containing an assignment survives being written and read back.
69+
/// </summary>
70+
/// <remarks>
71+
/// AssignmentStatement's deserialization constructor built its placeholder target through the
72+
/// VariableReference overload that rejects an empty name, so every load of a document holding one
73+
/// threw before it could be overwritten. The default document has an assignment in it, so this is
74+
/// the path a user takes by saving and reopening what the editor gave them.
75+
/// </remarks>
76+
[TestMethod]
77+
public void Document_WithAnAssignment_RoundTripsThroughTheFileSystem()
78+
{
79+
DocumentStore store = NewStore();
80+
CoderEditorApp app = NewApp(store);
81+
82+
string path = PathIn("assigning");
83+
Assert.IsTrue(app.Save(path), app.Status);
84+
85+
CoderEditorApp reopened = NewApp(store);
86+
Assert.IsTrue(reopened.Open(path), reopened.Status);
87+
88+
ClassDeclaration reopenedRoot = (ClassDeclaration)reopened.Editor.Graph.Root;
89+
Assert.IsTrue(
90+
reopenedRoot.Members.OfType<FunctionDeclaration>().SelectMany(m => m.Body).OfType<AssignmentStatement>().Any(),
91+
"the reopened document should still hold its assignment");
92+
}
93+
94+
/// <summary>
95+
/// Tests that a fresh editor opens with something to read rather than a blank canvas.
6996
/// </summary>
7097
[TestMethod]
71-
public void NewDocument_IsAFunctionWithSomethingToAttachTo()
98+
public void NewDocument_IsAClassWithFieldsAndMethodsThatDoSomething()
7299
{
73-
FunctionDeclaration document = CoderEditorApp.NewDocument();
100+
ClassDeclaration document = CoderEditorApp.NewDocument();
101+
102+
Assert.AreEqual("Counter", document.Name);
103+
104+
List<VariableDeclaration> fields = [.. document.Members.OfType<VariableDeclaration>()];
105+
Assert.AreEqual(2, fields.Count, "the class should carry a couple of fields");
106+
Assert.IsTrue(fields.TrueForAll(f => f.InitialValue is not null), "each field should be initialised");
74107

75-
Assert.AreEqual("newFunction", document.Name);
76-
Assert.AreEqual(1, document.Parameters.Count);
108+
List<FunctionDeclaration> methods = [.. document.Members.OfType<FunctionDeclaration>()];
109+
Assert.AreEqual(2, methods.Count, "the class should carry a couple of methods");
110+
Assert.IsTrue(methods.TrueForAll(m => m.Body.Count > 0), "each method should have a body");
111+
112+
Assert.IsTrue(
113+
methods.SelectMany(m => m.Body).OfType<AssignmentStatement>().Any(a => a.Value is BinaryExpression),
114+
"a method should assign the result of an expression");
115+
Assert.IsTrue(
116+
methods.SelectMany(m => m.Body).OfType<VariableDeclaration>().Any(v => v.InitialValue is BinaryExpression),
117+
"a method should declare a local from an expression");
77118
}
78119

79120
/// <summary>
@@ -92,8 +133,8 @@ public void Document_RoundTripsThroughTheFileSystem()
92133
CoderEditorApp reopened = NewApp(store);
93134
Assert.IsTrue(reopened.Open(path), reopened.Status);
94135

95-
Assert.IsInstanceOfType<FunctionDeclaration>(reopened.Editor.Graph.Root);
96-
Assert.AreEqual("newFunction", ((FunctionDeclaration)reopened.Editor.Graph.Root).Name);
136+
Assert.IsInstanceOfType<ClassDeclaration>(reopened.Editor.Graph.Root);
137+
Assert.AreEqual("Counter", ((ClassDeclaration)reopened.Editor.Graph.Root).Name);
97138
Assert.AreEqual(path, reopened.DocumentPath);
98139
}
99140

@@ -259,11 +300,11 @@ public void Preview_GeneratesInTheSelectedLanguage()
259300
CoderEditorApp app = NewApp(store, settings);
260301

261302
app.Regenerate();
262-
StringAssert.Contains(app.GeneratedCode, "public void newFunction", StringComparison.Ordinal);
303+
StringAssert.Contains(app.GeneratedCode, "public class Counter", StringComparison.Ordinal);
263304

264305
settings.PreviewLanguageId = "python";
265306
app.Regenerate();
266-
StringAssert.Contains(app.GeneratedCode, "def newFunction", StringComparison.Ordinal);
307+
StringAssert.Contains(app.GeneratedCode, "def Add(self, amount: int)", StringComparison.Ordinal);
267308
}
268309

269310
/// <summary>
@@ -367,7 +408,7 @@ public void Export_WritesTheGeneratedCodeBesideTheDocument()
367408

368409
Assert.IsNotNull(written);
369410
Assert.AreEqual(Path.Combine(root, "greeting.cs"), written);
370-
StringAssert.Contains(File.ReadAllText(written), "public void newFunction(int value)", StringComparison.Ordinal);
411+
StringAssert.Contains(File.ReadAllText(written), "public int Add(int amount)", StringComparison.Ordinal);
371412
StringAssert.Contains(app.Status, "Wrote", StringComparison.Ordinal);
372413
}
373414

@@ -521,7 +562,7 @@ public void CodePane_ListsProblemsInsteadOfGeneratingWhileIncomplete()
521562
CoderEditorApp app = NewApp(store);
522563

523564
// An operand nobody has filled in yet, which is what Validate reports.
524-
FunctionDeclaration document = CoderEditorApp.NewDocument();
565+
FunctionDeclaration document = new("incomplete") { ReturnType = "int" };
525566
document.Body.Add(new ReturnStatement(
526567
new BinaryExpression(AstSchema.Unfilled(), BinaryOperator.Add, AstSchema.Unfilled())));
527568
Assert.IsTrue(app.Open(WriteDocument(store, document, PathIn("incomplete"))), app.Status);

Coder/Ast/AssignmentStatement.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ public AssignmentStatement(Expression target, Expression value, AssignmentOperat
4444
/// </summary>
4545
public AssignmentStatement()
4646
{
47-
Target = new VariableReference("");
47+
// VariableReference(string) rejects an empty name, so the placeholder a deserializer overwrites
48+
// has to come from the parameterless constructor instead.
49+
Target = new VariableReference();
4850
Value = new LiteralExpression<string>("");
4951
Operator = AssignmentOperator.Assign;
5052
}

0 commit comments

Comments
 (0)