-
Notifications
You must be signed in to change notification settings - Fork 1
Add format command for .cmdt files #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| package cli | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "io" | ||
| "os" | ||
|
|
||
| "github.com/deref/transcript/internal/core" | ||
| "github.com/natefinch/atomic" | ||
| "github.com/spf13/cobra" | ||
| ) | ||
|
|
||
| func init() { | ||
| rootCmd.AddCommand(formatCmd) | ||
| } | ||
|
|
||
| var formatCmd = &cobra.Command{ | ||
| Use: "format [transcripts...]", | ||
| Short: "Formats transcript files", | ||
| Long: `Formats transcript files by normalizing comments, blank lines, | ||
| trailing whitespace (except in command output), trailing newline, | ||
| and special directive syntax. | ||
|
|
||
| If no files are provided, reads from stdin and writes to stdout. | ||
| If files are provided, formats them in-place. | ||
| `, | ||
| RunE: func(cmd *cobra.Command, args []string) error { | ||
| ctx := cmd.Context() | ||
| if len(args) == 0 { | ||
| // Read from stdin, write to stdout | ||
| return formatStdin(ctx) | ||
| } | ||
| // Format files in-place | ||
| for _, filename := range args { | ||
| if err := formatFile(ctx, filename); err != nil { | ||
| return fmt.Errorf("formatting %q: %w", filename, err) | ||
| } | ||
| } | ||
| return nil | ||
| }, | ||
| } | ||
|
|
||
| func formatStdin(ctx context.Context) error { | ||
| formatter := &core.Formatter{} | ||
| transcript, err := formatter.FormatTranscript(ctx, os.Stdin) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| _, err = io.Copy(os.Stdout, transcript) | ||
| return err | ||
| } | ||
|
|
||
| func formatFile(ctx context.Context, filename string) error { | ||
| f, err := os.Open(filename) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| defer f.Close() | ||
|
|
||
| formatter := &core.Formatter{} | ||
| transcript, err := formatter.FormatTranscript(ctx, f) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| return atomic.WriteFile(filename, transcript) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| package core | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "context" | ||
| "io" | ||
| "strconv" | ||
| "strings" | ||
| ) | ||
|
|
||
| type Formatter struct { | ||
| buf *bytes.Buffer | ||
| } | ||
|
|
||
| func (f *Formatter) FormatTranscript(ctx context.Context, r io.Reader) (transcript *bytes.Buffer, err error) { | ||
| f.buf = &bytes.Buffer{} | ||
|
|
||
| // Use the regular interpreter with the formatter as handler. | ||
| interp := &Interpreter{ | ||
| Handler: f, | ||
| } | ||
| if err := interp.ExecTranscript(ctx, r); err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| // Ensure file ends with exactly one newline | ||
| content := f.buf.Bytes() | ||
| content = bytes.TrimRight(content, "\n") | ||
| if len(content) > 0 { | ||
| content = append(content, '\n') | ||
| } | ||
|
|
||
| return bytes.NewBuffer(content), nil | ||
| } | ||
|
|
||
| func (f *Formatter) HandleComment(ctx context.Context, text string) error { | ||
| // Normalize comments and blank lines | ||
| trimmed := strings.TrimSpace(text) | ||
| if trimmed == "" { | ||
| // Blank line | ||
| f.buf.WriteString("\n") | ||
| } else if strings.HasPrefix(trimmed, "#") { | ||
| // Normalize comment formatting | ||
| comment := strings.TrimPrefix(trimmed, "#") | ||
| comment = strings.TrimSpace(comment) | ||
| if comment == "" { | ||
| f.buf.WriteString("#\n") | ||
| } else { | ||
| f.buf.WriteString("# " + comment + "\n") | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. prefer printf over string concatenation |
||
| } | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| func (f *Formatter) HandleRun(ctx context.Context, command string) error { | ||
| // Write the command with normalized formatting | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Comments that are sentences should end with periods. |
||
| f.buf.WriteString("$ " + strings.TrimSpace(command) + "\n") | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| func (f *Formatter) HandleOutput(ctx context.Context, fd int, line string) error { | ||
| // Output lines preserve their exact content (including whitespace) | ||
| f.buf.WriteString(strconv.Itoa(fd) + " " + line + "\n") | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| func (f *Formatter) HandleFileOutput(ctx context.Context, fd int, filepath string) error { | ||
| // File output references with normalized formatting | ||
| f.buf.WriteString(strconv.Itoa(fd) + "< " + strings.TrimSpace(filepath) + "\n") | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| func (f *Formatter) HandleNoNewline(ctx context.Context, fd int) error { | ||
| // No-newline directive with normalized formatting | ||
| f.buf.WriteString("% no-newline\n") | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| func (f *Formatter) HandleExitCode(ctx context.Context, exitCode int) error { | ||
| // Exit code with normalized formatting | ||
| f.buf.WriteString("? " + strconv.Itoa(exitCode) + "\n") | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| func (f *Formatter) HandleEnd(ctx context.Context) error { | ||
| // No special handling needed for end | ||
| return nil | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| # Test basic formatting | ||
|
|
||
| $ echo hello | ||
| 1 hello | ||
|
|
||
| # Multiple blank lines should be normalized | ||
|
|
||
|
|
||
| $ echo world | ||
| 1 world | ||
|
|
||
| # Comment with irregular spacing | ||
| $ false | ||
| ? 1 | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This file doesn't end with a trailing newline. Did you run the formatter you created on these files?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. still an issue, but you can run the formatter now |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| # Test basic formatting | ||
|
|
||
| $ echo hello | ||
| 1 hello | ||
|
|
||
| # Multiple blank lines should be normalized | ||
|
|
||
| $ echo world | ||
| 1 world | ||
|
|
||
| # Comment with irregular spacing | ||
| $ false | ||
| ? 1 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| # Test format command functionality | ||
|
|
||
| # Create a file with formatting issues | ||
| $ cat > unformatted.cmdt <<'EOF' | ||
| #comment without space | ||
|
|
||
|
|
||
| $ echo hello | ||
| 1 hello | ||
|
|
||
| # irregular comment spacing | ||
| $ false | ||
| ? 1 | ||
| EOF | ||
|
|
||
| # Test stdin/stdout formatting | ||
| $ transcript format < unformatted.cmdt | ||
| 1 # comment without space | ||
| 1 | ||
| 1 $ echo hello | ||
| 1 1 hello | ||
| 1 | ||
| 1 # irregular comment spacing | ||
| 1 $ false | ||
| 1 ? 1 | ||
|
|
||
| # Test actual formatting (in-place) | ||
| $ transcript format unformatted.cmdt | ||
|
|
||
| # Verify the file was formatted correctly | ||
| $ cat unformatted.cmdt | ||
| 1 # comment without space | ||
| 1 | ||
| 1 $ echo hello | ||
| 1 1 hello | ||
| 1 | ||
| 1 # irregular comment spacing | ||
| 1 $ false | ||
| 1 ? 1 | ||
|
|
||
| # Clean up | ||
| $ rm unformatted.cmdt |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| # Test simple format command | ||
|
|
||
| # Create a file with whitespace and formatting issues | ||
| $ cat > test.cmdt <<'EOF' | ||
| #comment | ||
| $ echo test | ||
| 1 test | ||
| #another comment | ||
| ? 0 | ||
| EOF | ||
|
|
||
| # Format it using stdin/stdout | ||
| $ transcript format < test.cmdt | ||
| 1 # comment | ||
| 1 $ echo test | ||
| 1 1 test | ||
| 1 # another comment | ||
| 1 ? 0 | ||
|
|
||
| # Clean up | ||
| $ rm test.cmdt |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The input file remains open until function return, which can cause file‐lock errors on Windows when renaming over it. Consider closing the reader (f.Close()) before calling atomic.WriteFile.