Skip to content

Commit d63104a

Browse files
committed
fix: preserve trailing newlines and silence empty output
- Accumulate raw bytes in executeRaw instead of stripping \r\n per line, so println output retains its trailing newline while print does not - Remove "(no output)" placeholder; suppress blank line for empty output - Switch -e/--eval flags and --project (replaces --env) to mirror julia CLI - Add proc_windows.go stub so Windows builds succeed
1 parent a8cf2b1 commit d63104a

5 files changed

Lines changed: 37 additions & 24 deletions

File tree

go/client_test.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -225,26 +225,36 @@ func TestEvalBasic(t *testing.T) {
225225
t.Fatalf("eval error: %v", resp["error"])
226226
}
227227
out, _ := resp["output"].(string)
228-
if out != "hello world" {
229-
t.Errorf("eval output = %q, want %q", out, "hello world")
228+
if out != "hello world\n" {
229+
t.Errorf("eval output = %q, want %q", out, "hello world\n")
230230
}
231231

232232
// State persists across calls
233233
send(map[string]any{"action": "eval", "code": "x = 42"})
234234
resp2 := send(map[string]any{"action": "eval", "code": "println(x)"})
235235
out2, _ := resp2["output"].(string)
236-
if out2 != "42" {
237-
t.Errorf("state not persisted: x = %q, want 42", out2)
236+
if out2 != "42\n" {
237+
t.Errorf("state not persisted: x = %q, want %q", out2, "42\n")
238238
}
239239

240240
// Restart clears state
241241
send(map[string]any{"action": "restart"})
242242
resp3 := send(map[string]any{"action": "eval", "code": "println(isdefined(Main, :x))"})
243243
out3, _ := resp3["output"].(string)
244-
if out3 != "false" {
244+
if out3 != "false\n" {
245245
t.Errorf("after restart x should be undefined, got %q", out3)
246246
}
247247

248+
// println adds trailing newline; print does not
249+
resp4 := send(map[string]any{"action": "eval", "code": `print("no-nl")`})
250+
if out4, _ := resp4["output"].(string); out4 != "no-nl" {
251+
t.Errorf("print output = %q, want %q", out4, "no-nl")
252+
}
253+
resp5 := send(map[string]any{"action": "eval", "code": `println("with-nl")`})
254+
if out5, _ := resp5["output"].(string); out5 != "with-nl\n" {
255+
t.Errorf("println output = %q, want %q", out5, "with-nl\n")
256+
}
257+
248258
// Stop daemon
249259
conn, _ := net.Dial("unix", socketPath)
250260
json.NewEncoder(conn).Encode(map[string]any{"action": "stop"})

go/daemon.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,6 @@ func handleRequest(state *daemonState, req map[string]any) map[string]any {
6060
}
6161
return errResp(err.Error())
6262
}
63-
if output == "" {
64-
output = "(no output)"
65-
}
6663
return map[string]any{"output": output, "error": nil}
6764

6865
case "restart":

go/main.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,9 @@ func run(socketPath string, payload map[string]any, startIfNeeded bool) {
118118
fmt.Fprintln(os.Stderr, resp.Error)
119119
os.Exit(1)
120120
}
121-
fmt.Println(resp.Output)
121+
if resp.Output != "" {
122+
fmt.Print(resp.Output)
123+
}
122124
}
123125

124126
func cmdEval(socketPath, code, project string, timeout float64, juliaCmd string) {

go/session.go

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -139,11 +139,14 @@ func (s *JuliaSession) isAlive() bool {
139139
}
140140

141141
type readResult struct {
142-
lines []string
143-
err error
142+
output string
143+
err error
144144
}
145145

146146
func (s *JuliaSession) executeRaw(code string, timeoutSecs float64) (string, error) {
147+
// The sentinel command writes an extra "\n" before the marker so it always
148+
// starts on its own line even when the user code didn't end with a newline.
149+
// We strip exactly that one "\n" when assembling the result.
147150
sentinelCmd := fmt.Sprintf(
148151
"flush(stderr); write(stdout, \"\\n\"); println(stdout, \"%s\"); flush(stdout)\n",
149152
s.sentinel,
@@ -154,46 +157,46 @@ func (s *JuliaSession) executeRaw(code string, timeoutSecs float64) (string, err
154157

155158
ch := make(chan readResult, 1)
156159
go func() {
157-
var lines []string
160+
var buf strings.Builder
158161
for {
159162
line, err := s.stdout.ReadString('\n')
160-
trimmed := strings.TrimRight(line, "\r\n")
161-
if trimmed == s.sentinel {
162-
if len(lines) > 0 && lines[len(lines)-1] == "" {
163-
lines = lines[:len(lines)-1]
163+
if strings.TrimRight(line, "\r\n") == s.sentinel {
164+
// Strip the one "\n" we injected before the sentinel.
165+
out := buf.String()
166+
if len(out) > 0 && out[len(out)-1] == '\n' {
167+
out = out[:len(out)-1]
164168
}
165-
ch <- readResult{lines, nil}
169+
ch <- readResult{out, nil}
166170
return
167171
}
168172
if err != nil {
169173
s.dead.Store(true)
170-
ch <- readResult{lines, fmt.Errorf("Julia process died during execution.\nOutput before death:\n%s", strings.Join(lines, "\n"))}
174+
ch <- readResult{buf.String(), fmt.Errorf("Julia process died during execution.\nOutput before death:\n%s", buf.String())}
171175
return
172176
}
173-
lines = append(lines, trimmed)
177+
buf.WriteString(line)
174178
}
175179
}()
176180

177181
if timeoutSecs <= 0 {
178182
r := <-ch
179-
return strings.Join(r.lines, "\n"), r.err
183+
return r.output, r.err
180184
}
181185

182186
timer := time.NewTimer(time.Duration(float64(time.Second) * timeoutSecs))
183187
defer timer.Stop()
184188

185189
select {
186190
case r := <-ch:
187-
return strings.Join(r.lines, "\n"), r.err
191+
return r.output, r.err
188192
case <-timer.C:
189193
s.proc.Process.Kill()
190194
s.proc.Wait()
191195
s.dead.Store(true)
192196
r := <-ch // goroutine unblocks on EOF after kill
193-
partial := strings.Join(r.lines, "\n")
194197
msg := fmt.Sprintf("Execution timed out after %vs. Session killed; it will restart on next call.", timeoutSecs)
195-
if partial != "" {
196-
msg += "\n\nOutput before timeout:\n" + partial
198+
if r.output != "" {
199+
msg += "\n\nOutput before timeout:\n" + r.output
197200
}
198201
return "", fmt.Errorf("%s", msg)
199202
}

justfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
install:
2+
julia-client stop
23
go build -o ~/.local/bin/julia-client ./go/
34
npx skills add . -g -y
45

0 commit comments

Comments
 (0)