Skip to content

Commit 838bbb6

Browse files
authored
Merge pull request #16 from KashifKhn/dev
Dev
2 parents 43ad0cb + 000224b commit 838bbb6

File tree

5 files changed

+97
-38
lines changed

5 files changed

+97
-38
lines changed

docs/docs/commands/dev.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,11 @@ haft dev serve --no-interactive
116116

117117
| Build Tool | Run Command | Compile Command (for restart) |
118118
|------------|-------------|-------------------------------|
119-
| Maven | `mvn spring-boot:run -DskipTests` | `mvn compile -DskipTests -q` |
119+
| Maven | `mvn spring-boot:run -DskipTests -B` | `mvn compile -DskipTests -q -B` |
120120
| Gradle | `./gradlew bootRun -x test` | `./gradlew classes -x test -q` |
121121

122+
Note: The `-B` flag enables Maven batch mode for cleaner output formatting.
123+
122124
---
123125

124126
## Plugin Integration

internal/cli/dev/keyboard.go

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package dev
33
import (
44
"fmt"
55
"os"
6-
"runtime"
76

87
"github.com/mattn/go-isatty"
98
"golang.org/x/term"
@@ -145,29 +144,25 @@ func (kl *KeyboardListener) parseKey(buf []byte) KeyCommand {
145144
}
146145

147146
func ClearScreen() {
148-
if runtime.GOOS == "windows" {
149-
fmt.Print("\033[H\033[2J")
150-
} else {
151-
fmt.Print("\033[H\033[2J")
152-
}
147+
fmt.Print("\033[H\033[2J")
153148
}
154149

155150
func PrintKeyCommands() {
156-
fmt.Println()
157-
fmt.Println("\033[36mHaft dev commands:\033[0m")
158-
fmt.Println(" \033[33mr\033[0m Restart (compile first, then restart)")
159-
fmt.Println(" \033[33mq\033[0m Quit")
160-
fmt.Println(" \033[33mc\033[0m Clear screen")
161-
fmt.Println(" \033[33mh\033[0m Help")
162-
fmt.Println()
151+
fmt.Print("\r\n")
152+
fmt.Print("\033[36mHaft dev commands:\033[0m\r\n")
153+
fmt.Print(" \033[33mr\033[0m Restart (compile first, then restart)\r\n")
154+
fmt.Print(" \033[33mq\033[0m Quit\r\n")
155+
fmt.Print(" \033[33mc\033[0m Clear screen\r\n")
156+
fmt.Print(" \033[33mh\033[0m Help\r\n")
157+
fmt.Print("\r\n")
163158
}
164159

165160
func PrintBanner() {
166-
fmt.Println()
167-
fmt.Println("\033[36m╭─────────────────────────────────────────╮\033[0m")
168-
fmt.Println("\033[36m│\033[0m \033[1;35mHaft Dev Server\033[0m \033[36m│\033[0m")
169-
fmt.Println("\033[36m│\033[0m Press \033[33mr\033[0m to restart, \033[33mq\033[0m to quit \033[36m│\033[0m")
170-
fmt.Println("\033[36m│\033[0m Press \033[33mh\033[0m for more commands \033[36m│\033[0m")
171-
fmt.Println("\033[36m╰─────────────────────────────────────────╯\033[0m")
172-
fmt.Println()
161+
fmt.Print("\r\n")
162+
fmt.Print("\033[36m╭─────────────────────────────────────────╮\033[0m\r\n")
163+
fmt.Print("\033[36m│\033[0m \033[1;35mHaft Dev Server\033[0m \033[36m│\033[0m\r\n")
164+
fmt.Print("\033[36m│\033[0m Press \033[33mr\033[0m to restart, \033[33mq\033[0m to quit \033[36m│\033[0m\r\n")
165+
fmt.Print("\033[36m│\033[0m Press \033[33mh\033[0m for more commands \033[36m│\033[0m\r\n")
166+
fmt.Print("\033[36m╰─────────────────────────────────────────╯\033[0m\r\n")
167+
fmt.Print("\r\n")
173168
}

internal/cli/dev/process.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -295,18 +295,20 @@ func (pm *ProcessManager) Restart(ctx context.Context) error {
295295
pm.restartCount++
296296
pm.mu.Unlock()
297297

298-
fmt.Fprintln(pm.stdout, "\n\033[33m→ Compiling...\033[0m")
298+
fmt.Fprintln(pm.stdout, "\n\033[33m─────────────────────────────────────────\033[0m")
299+
fmt.Fprintln(pm.stdout, "\033[33m→ Compiling...\033[0m")
299300

300301
if err := pm.Compile(ctx); err != nil {
301-
fmt.Fprintf(pm.stderr, "\033[31m✗ Compilation failed: %v\033[0m\n", err)
302+
fmt.Fprintf(pm.stderr, "\n\033[31m✗ Compilation failed: %v\033[0m\n", err)
302303
fmt.Fprintln(pm.stdout, "\033[33m→ Keeping current server running\033[0m")
304+
fmt.Fprintln(pm.stdout, "\033[33m─────────────────────────────────────────\033[0m")
303305
pm.mu.Lock()
304306
pm.setState(StateRunning)
305307
pm.mu.Unlock()
306308
return err
307309
}
308310

309-
fmt.Fprintln(pm.stdout, "\033[32m✓ Compilation successful\033[0m")
311+
fmt.Fprintln(pm.stdout, "\n\033[32m✓ Compilation successful\033[0m")
310312
fmt.Fprintln(pm.stdout, "\033[33m→ Stopping server...\033[0m")
311313

312314
pm.mu.Lock()
@@ -320,6 +322,7 @@ func (pm *ProcessManager) Restart(ctx context.Context) error {
320322
}
321323

322324
fmt.Fprintln(pm.stdout, "\033[33m→ Starting server...\033[0m")
325+
fmt.Fprintln(pm.stdout, "\033[33m─────────────────────────────────────────\033[0m")
323326

324327
return pm.Start()
325328
}
@@ -337,7 +340,7 @@ func (pm *ProcessManager) buildRunCommand() (string, []string) {
337340

338341
func (pm *ProcessManager) buildMavenRunCommand() (string, []string) {
339342
executable := getMavenExecutable()
340-
args := []string{"spring-boot:run", "-DskipTests"}
343+
args := []string{"spring-boot:run", "-DskipTests", "-B", "-ntp"}
341344

342345
if pm.profile != "" {
343346
args = append(args, fmt.Sprintf("-Dspring-boot.run.profiles=%s", pm.profile))
@@ -381,11 +384,11 @@ func (pm *ProcessManager) buildGradleRunCommand() (string, []string) {
381384
func (pm *ProcessManager) buildCompileCommand() (string, []string) {
382385
switch pm.buildTool {
383386
case buildtool.Maven:
384-
return getMavenExecutable(), []string{"compile", "-DskipTests", "-q"}
387+
return getMavenExecutable(), []string{"compile", "-DskipTests", "-q", "-B"}
385388
case buildtool.Gradle, buildtool.GradleKotln:
386389
return getGradleExecutable(), []string{"classes", "-x", "test", "-q"}
387390
default:
388-
return getMavenExecutable(), []string{"compile", "-DskipTests", "-q"}
391+
return getMavenExecutable(), []string{"compile", "-DskipTests", "-q", "-B"}
389392
}
390393
}
391394

internal/cli/dev/serve.go

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package dev
33
import (
44
"context"
55
"fmt"
6+
"io"
67
"os"
78
"os/signal"
89
"runtime"
@@ -74,18 +75,24 @@ func runServe(profile string, debug bool, port int, noInteractive bool) error {
7475
return fmt.Errorf("not a Spring Boot project: %w", err)
7576
}
7677

78+
keyboard := NewKeyboardListener()
79+
isInteractive := keyboard.IsInteractive() && !noInteractive
80+
81+
var stdout, stderr io.Writer = os.Stdout, os.Stderr
82+
if isInteractive {
83+
stdout = newCRLFWriter(os.Stdout)
84+
stderr = newCRLFWriter(os.Stderr)
85+
}
86+
7787
pm := NewProcessManager(ProcessConfig{
7888
BuildTool: result.BuildTool,
7989
Profile: profile,
8090
Debug: debug,
8191
Port: port,
82-
Stdout: os.Stdout,
83-
Stderr: os.Stderr,
92+
Stdout: stdout,
93+
Stderr: stderr,
8494
})
8595

86-
keyboard := NewKeyboardListener()
87-
isInteractive := keyboard.IsInteractive() && !noInteractive
88-
8996
trigger := NewTriggerWatcher()
9097
if err := trigger.Setup(); err != nil {
9198
logger.Warning("Failed to setup trigger watcher", "error", err)
@@ -133,15 +140,15 @@ func runServe(profile string, debug bool, port int, noInteractive bool) error {
133140
switch cmd {
134141
case KeyRestart:
135142
if pm.IsBusy() {
136-
fmt.Println("\n\033[33m→ Restart already in progress...\033[0m")
143+
fmt.Print("\r\n\033[33m→ Restart already in progress...\033[0m\r\n")
137144
continue
138145
}
139146
if err := pm.Restart(ctx); err != nil {
140147
logger.Debug("Restart failed", "error", err)
141148
}
142149

143150
case KeyQuit:
144-
fmt.Println("\n\033[33m→ Shutting down...\033[0m")
151+
fmt.Print("\r\n\033[33m→ Shutting down...\033[0m\r\n")
145152
if err := pm.Stop(); err != nil {
146153
logger.Debug("Error during shutdown", "error", err)
147154
}
@@ -159,7 +166,7 @@ func runServe(profile string, debug bool, port int, noInteractive bool) error {
159166
if pm.IsBusy() {
160167
continue
161168
}
162-
fmt.Println("\n\033[35m→ External restart triggered\033[0m")
169+
fmt.Print("\r\n\033[35m→ External restart triggered\033[0m\r\n")
163170
if err := pm.Restart(ctx); err != nil {
164171
logger.Debug("External restart failed", "error", err)
165172
}
@@ -168,8 +175,8 @@ func runServe(profile string, debug bool, port int, noInteractive bool) error {
168175
if pm.State() == StateIdle || pm.State() == StateFailed {
169176
if lastErr := pm.LastError(); lastErr != nil {
170177
if isInteractive {
171-
fmt.Printf("\n\033[31mProcess stopped: %v\033[0m\n", lastErr)
172-
fmt.Println("\033[33mPress 'r' to restart or 'q' to quit\033[0m")
178+
fmt.Printf("\r\n\033[31mProcess stopped: %v\033[0m\r\n", lastErr)
179+
fmt.Print("\033[33mPress 'r' to restart or 'q' to quit\033[0m\r\n")
173180
waitForRestartOrQuit(keyboard, sigChan, pm, ctx)
174181
} else {
175182
return lastErr
@@ -204,7 +211,7 @@ func waitForRestartOrQuit(keyboard *KeyboardListener, sigChan chan os.Signal, pm
204211
}
205212
return
206213
case KeyQuit:
207-
fmt.Println("\n\033[33m→ Shutting down...\033[0m")
214+
fmt.Print("\r\n\033[33m→ Shutting down...\033[0m\r\n")
208215
if err := pm.Stop(); err != nil {
209216
logger.Debug("Error during shutdown", "error", err)
210217
}

internal/cli/dev/writer.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package dev
2+
3+
import (
4+
"bytes"
5+
"io"
6+
)
7+
8+
type crlfWriter struct {
9+
w io.Writer
10+
}
11+
12+
func newCRLFWriter(w io.Writer) *crlfWriter {
13+
return &crlfWriter{w: w}
14+
}
15+
16+
func (c *crlfWriter) Write(p []byte) (n int, err error) {
17+
written := 0
18+
for len(p) > 0 {
19+
idx := bytes.IndexByte(p, '\n')
20+
if idx == -1 {
21+
n, err := c.w.Write(p)
22+
written += n
23+
return written, err
24+
}
25+
26+
if idx > 0 && p[idx-1] == '\r' {
27+
n, err := c.w.Write(p[:idx+1])
28+
written += n
29+
if err != nil {
30+
return written, err
31+
}
32+
p = p[idx+1:]
33+
continue
34+
}
35+
36+
if idx > 0 {
37+
n, err := c.w.Write(p[:idx])
38+
written += n
39+
if err != nil {
40+
return written, err
41+
}
42+
}
43+
44+
_, err := c.w.Write([]byte("\r\n"))
45+
if err != nil {
46+
return written, err
47+
}
48+
written += 1
49+
p = p[idx+1:]
50+
}
51+
return written, nil
52+
}

0 commit comments

Comments
 (0)