Skip to content
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

Feat/wish #33

Merged
merged 41 commits into from
Jul 9, 2024
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
904d1ab
add support for stdin
Jun 12, 2024
9e82096
update languages to allow lowercases names
Jun 12, 2024
05e2ad9
Merge branch 'main' into feat/stdin-input
puria Jun 18, 2024
6da2bb0
Merge branch 'main' into feat/stdin-input
FilippoTrotter Jun 18, 2024
96b7de4
fix merging error
Jun 18, 2024
0c8cee3
fix test
Jun 18, 2024
4b17ad5
refactor with cobra
Jun 18, 2024
c941a61
update build action
Jun 18, 2024
f5b4c6f
solve merge conflicts
Jun 18, 2024
dc02115
add some comments
Jun 18, 2024
af4da50
better error handling
Jun 18, 2024
577ba06
add ssh connection to remote folder
Jun 20, 2024
1b36af2
add possibility to specify port
Jun 21, 2024
476d03c
update first wish version
Jun 26, 2024
e234c18
add tui remote support
Jun 28, 2024
7d3dd52
fix label view
Jul 1, 2024
6e8e889
better pty handling
Jul 1, 2024
6d5027f
remove debug print
Jul 1, 2024
8ce0910
add a better error handling
Jul 1, 2024
5e1e26d
add labels case
Jul 1, 2024
077e840
fix applyChanges
Jul 1, 2024
d729dc3
remove log.fatal for better error handling
Jul 1, 2024
22baa5f
feat: support zenroom and slangroom contracts
puria Jul 3, 2024
0bba042
add tui flag
Jul 3, 2024
7be875d
fix: error handling
Jul 3, 2024
d7cf4e4
add no selected file check
Jul 3, 2024
1144458
fix: label request view
Jul 3, 2024
8ece73a
remove possibility to add non character keys as input
Jul 3, 2024
371c869
fix: remove wrong exit key
Jul 3, 2024
f0e3fdf
fix: c language
Jul 3, 2024
f5f10c7
feat: add .cs extension support
Jul 4, 2024
1162807
feat: add go back possibility
Jul 4, 2024
d6ddccf
fix: view functions
Jul 4, 2024
2421789
add unit tests for option and label models
Jul 4, 2024
957b226
add unit tests for file selector model
Jul 5, 2024
db293bd
add some extra test
Jul 5, 2024
3e17b26
fix: remove mode selection for single file
Jul 5, 2024
9666295
add main model unit tests
Jul 9, 2024
fe7167e
refactor
Jul 9, 2024
0d44323
refactor
Jul 9, 2024
13c1c4d
Resolve merge conflict
Jul 9, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,5 @@ jobs:
run: go test -v ./...

- name: Build
run: go build -o tgcom ./cmd/tgcom
run: go build -o tgcom

214 changes: 214 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
package cmd

import (
"fmt"
"log"
"os"
"os/exec"
"runtime"
"strings"

tea "github.com/charmbracelet/bubbletea"
"github.com/dyne/tgcom/utils/modfile"
"github.com/dyne/tgcom/utils/tui"
"github.com/dyne/tgcom/utils/tui/modelutils"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)

var (
FileToRead string
inputFlag modfile.Config
remotePath string
Tui bool
)

var rootCmd = &cobra.Command{
Use: "tgcom",
Short: "tgcom is a tool that allows users to comment or uncomment pieces of code",
Long: `tgcom is a CLI library written in Go that allows users to
comment or uncomment pieces of code. It supports many different
languages including Go, C, Java, Python, Bash, and many others...`,
Run: func(cmd *cobra.Command, args []string) {
if remotePath != "" {
executeRemoteCommand(remotePath)
return
}

if noFlagsGiven(cmd) {
customUsageFunc(cmd)
os.Exit(1)
}
ReadFlags(cmd)
},
}

func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment for the exported function Execute.

The Execute function is exported and should have a comment for proper documentation.

+// Execute executes the root command.
 func Execute() {
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}
// Execute executes the root command.
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}


func init() {
rootCmd.SetHelpFunc(customHelpFunc)
rootCmd.SetUsageFunc(customUsageFunc)

rootCmd.PersistentFlags().StringVarP(&FileToRead, "file", "f", "", "pass argument to the flag and will modify the file content")
rootCmd.PersistentFlags().StringVarP(&inputFlag.LineNum, "line", "l", "", "pass argument to line flag and will modify the line in the specified range")
rootCmd.PersistentFlags().BoolVarP(&inputFlag.DryRun, "dry-run", "d", false, "pass argument to dry-run flag and will print the result")
rootCmd.PersistentFlags().StringVarP(&inputFlag.Action, "action", "a", "toggle", "pass argument to action to comment/uncomment/toggle some lines")
rootCmd.PersistentFlags().StringVarP(&inputFlag.StartLabel, "start-label", "s", "", "pass argument to start-label to modify lines after start-label")
rootCmd.PersistentFlags().StringVarP(&inputFlag.EndLabel, "end-label", "e", "", "pass argument to end-label to modify lines up to end-label")
rootCmd.PersistentFlags().StringVarP(&inputFlag.Lang, "language", "L", "", "pass argument to language to specify the language of the input code")
rootCmd.PersistentFlags().StringVarP(&remotePath, "remote", "w", "", "pass remote user, host, and directory in the format user@host:/path/to/directory")
rootCmd.PersistentFlags().BoolVarP(&Tui, "tui", "t", false, "run the terminal user interface")
// Mark flags based on command name
rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
if cmd.Name() != "server" {
cmd.MarkFlagsRequiredTogether("start-label", "end-label")
cmd.MarkFlagsMutuallyExclusive("line", "start-label")
cmd.MarkFlagsMutuallyExclusive("line", "end-label")
cmd.MarkFlagsOneRequired("file", "language", "remote", "tui")
cmd.MarkFlagsMutuallyExclusive("file", "language")
}
return nil
}

// Register server command
rootCmd.AddCommand(serverCmd)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment for the init function.

The init function sets help and usage functions, registers flags, and adds the server command.

+// init sets help and usage functions, registers flags, and adds the server command.
 func init() {
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func init() {
rootCmd.SetHelpFunc(customHelpFunc)
rootCmd.SetUsageFunc(customUsageFunc)
rootCmd.PersistentFlags().StringVarP(&FileToRead, "file", "f", "", "pass argument to the flag and will modify the file content")
rootCmd.PersistentFlags().StringVarP(&inputFlag.LineNum, "line", "l", "", "pass argument to line flag and will modify the line in the specified range")
rootCmd.PersistentFlags().BoolVarP(&inputFlag.DryRun, "dry-run", "d", false, "pass argument to dry-run flag and will print the result")
rootCmd.PersistentFlags().StringVarP(&inputFlag.Action, "action", "a", "toggle", "pass argument to action to comment/uncomment/toggle some lines")
rootCmd.PersistentFlags().StringVarP(&inputFlag.StartLabel, "start-label", "s", "", "pass argument to start-label to modify lines after start-label")
rootCmd.PersistentFlags().StringVarP(&inputFlag.EndLabel, "end-label", "e", "", "pass argument to end-label to modify lines up to end-label")
rootCmd.PersistentFlags().StringVarP(&inputFlag.Lang, "language", "L", "", "pass argument to language to specify the language of the input code")
rootCmd.PersistentFlags().StringVarP(&remotePath, "remote", "w", "", "pass remote user, host, and directory in the format user@host:/path/to/directory")
rootCmd.PersistentFlags().BoolVarP(&Tui, "tui", "t", false, "run the terminal user interface")
// Mark flags based on command name
rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
if cmd.Name() != "server" {
cmd.MarkFlagsRequiredTogether("start-label", "end-label")
cmd.MarkFlagsMutuallyExclusive("line", "start-label")
cmd.MarkFlagsMutuallyExclusive("line", "end-label")
cmd.MarkFlagsOneRequired("file", "language", "remote", "tui")
cmd.MarkFlagsMutuallyExclusive("file", "language")
}
return nil
}
// Register server command
rootCmd.AddCommand(serverCmd)
}
// init sets help and usage functions, registers flags, and adds the server command.
func init() {
rootCmd.SetHelpFunc(customHelpFunc)
rootCmd.SetUsageFunc(customUsageFunc)
rootCmd.PersistentFlags().StringVarP(&FileToRead, "file", "f", "", "pass argument to the flag and will modify the file content")
rootCmd.PersistentFlags().StringVarP(&inputFlag.LineNum, "line", "l", "", "pass argument to line flag and will modify the line in the specified range")
rootCmd.PersistentFlags().BoolVarP(&inputFlag.DryRun, "dry-run", "d", false, "pass argument to dry-run flag and will print the result")
rootCmd.PersistentFlags().StringVarP(&inputFlag.Action, "action", "a", "toggle", "pass argument to action to comment/uncomment/toggle some lines")
rootCmd.PersistentFlags().StringVarP(&inputFlag.StartLabel, "start-label", "s", "", "pass argument to start-label to modify lines after start-label")
rootCmd.PersistentFlags().StringVarP(&inputFlag.EndLabel, "end-label", "e", "", "pass argument to end-label to modify lines up to end-label")
rootCmd.PersistentFlags().StringVarP(&inputFlag.Lang, "language", "L", "", "pass argument to language to specify the language of the input code")
rootCmd.PersistentFlags().StringVarP(&remotePath, "remote", "w", "", "pass remote user, host, and directory in the format user@host:/path/to/directory")
rootCmd.PersistentFlags().BoolVarP(&Tui, "tui", "t", false, "run the terminal user interface")
// Mark flags based on command name
rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
if cmd.Name() != "server" {
cmd.MarkFlagsRequiredTogether("start-label", "end-label")
cmd.MarkFlagsMutuallyExclusive("line", "start-label")
cmd.MarkFlagsMutuallyExclusive("line", "end-label")
cmd.MarkFlagsOneRequired("file", "language", "remote", "tui")
cmd.MarkFlagsMutuallyExclusive("file", "language")
}
return nil
}
// Register server command
rootCmd.AddCommand(serverCmd)
}


func noFlagsGiven(cmd *cobra.Command) bool {
hasFlags := false
cmd.Flags().VisitAll(func(f *pflag.Flag) {
if f.Changed {
hasFlags = true
}
})
return !hasFlags
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment for the noFlagsGiven function.

The noFlagsGiven function checks if any flags were provided.

+// noFlagsGiven checks if any flags were provided.
 func noFlagsGiven(cmd *cobra.Command) bool {
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func noFlagsGiven(cmd *cobra.Command) bool {
hasFlags := false
cmd.Flags().VisitAll(func(f *pflag.Flag) {
if f.Changed {
hasFlags = true
}
})
return !hasFlags
}
// noFlagsGiven checks if any flags were provided.
func noFlagsGiven(cmd *cobra.Command) bool {
hasFlags := false
cmd.Flags().VisitAll(func(f *pflag.Flag) {
if f.Changed {
hasFlags = true
}
})
return !hasFlags
}


func ReadFlags(cmd *cobra.Command) {
if Tui {
currentDir, err := os.Getwd()
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting current working directory: %v\n", err)
os.Exit(1)
}

// Initialize your model with the current directory
model := tui.Model{
State: "FileSelection",
FilesSelector: modelutils.InitialModel(currentDir, 20),
}
clearScreen()
// Bubble Tea program
p := tea.NewProgram(model)

// Start the program
if _, err := p.Run(); err != nil {
os.Exit(1)
}
clearScreen()
} else {

if strings.Contains(FileToRead, ",") {
if cmd.Flags().Changed("line") {
fmt.Println("Warning: when passing multiple files to flag -f, don't use -l flag")
}
if cmd.Flags().Changed("start-label") && cmd.Flags().Changed("end-label") {
fileInfo := strings.Split(FileToRead, ",")
for i := 0; i < len(fileInfo); i++ {
inputFlag.Filename = fileInfo[i]
if err := modfile.ChangeFile(inputFlag); err != nil {
log.Fatal(err)
}
}
} else {
fileInfo := strings.Split(FileToRead, ",")
for i := 0; i < len(fileInfo); i++ {
if strings.Contains(fileInfo[i], ":") {
parts := strings.Split(fileInfo[i], ":")
if len(parts) != 2 {
log.Fatalf("invalid syntax. Use 'File:lines'")
}
inputFlag.Filename = parts[0]
inputFlag.LineNum = parts[1]
if err := modfile.ChangeFile(inputFlag); err != nil {
log.Fatal(err)
}
} else {
log.Fatalf("invalid syntax. Use 'File:lines'")
}
}
}
} else {
if cmd.Flags().Changed("line") || cmd.Flags().Changed("start-label") && cmd.Flags().Changed("end-label") {
inputFlag.Filename = FileToRead
if err := modfile.ChangeFile(inputFlag); err != nil {
log.Fatal(err)
}
} else {
log.Fatalf("Not specified what you want to modify: add -l flag or -s and -e flags")
}
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment for the ReadFlags function.

The ReadFlags function reads and processes the flags provided by the user.

+// ReadFlags reads and processes the flags provided by the user.
 func ReadFlags(cmd *cobra.Command) {
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func ReadFlags(cmd *cobra.Command) {
if Tui {
currentDir, err := os.Getwd()
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting current working directory: %v\n", err)
os.Exit(1)
}
// Initialize your model with the current directory
model := tui.Model{
State: "FileSelection",
FilesSelector: modelutils.InitialModel(currentDir, 20),
}
clearScreen()
// Bubble Tea program
p := tea.NewProgram(model)
// Start the program
if _, err := p.Run(); err != nil {
os.Exit(1)
}
clearScreen()
} else {
if strings.Contains(FileToRead, ",") {
if cmd.Flags().Changed("line") {
fmt.Println("Warning: when passing multiple files to flag -f, don't use -l flag")
}
if cmd.Flags().Changed("start-label") && cmd.Flags().Changed("end-label") {
fileInfo := strings.Split(FileToRead, ",")
for i := 0; i < len(fileInfo); i++ {
inputFlag.Filename = fileInfo[i]
if err := modfile.ChangeFile(inputFlag); err != nil {
log.Fatal(err)
}
}
} else {
fileInfo := strings.Split(FileToRead, ",")
for i := 0; i < len(fileInfo); i++ {
if strings.Contains(fileInfo[i], ":") {
parts := strings.Split(fileInfo[i], ":")
if len(parts) != 2 {
log.Fatalf("invalid syntax. Use 'File:lines'")
}
inputFlag.Filename = parts[0]
inputFlag.LineNum = parts[1]
if err := modfile.ChangeFile(inputFlag); err != nil {
log.Fatal(err)
}
} else {
log.Fatalf("invalid syntax. Use 'File:lines'")
}
}
}
} else {
if cmd.Flags().Changed("line") || cmd.Flags().Changed("start-label") && cmd.Flags().Changed("end-label") {
inputFlag.Filename = FileToRead
if err := modfile.ChangeFile(inputFlag); err != nil {
log.Fatal(err)
}
} else {
log.Fatalf("Not specified what you want to modify: add -l flag or -s and -e flags")
}
}
}
}
// ReadFlags reads and processes the flags provided by the user.
func ReadFlags(cmd *cobra.Command) {
if Tui {
currentDir, err := os.Getwd()
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting current working directory: %v\n", err)
os.Exit(1)
}
// Initialize your model with the current directory
model := tui.Model{
State: "FileSelection",
FilesSelector: modelutils.InitialModel(currentDir, 20),
}
clearScreen()
// Bubble Tea program
p := tea.NewProgram(model)
// Start the program
if _, err := p.Run(); err != nil {
os.Exit(1)
}
clearScreen()
} else {
if strings.Contains(FileToRead, ",") {
if cmd.Flags().Changed("line") {
fmt.Println("Warning: when passing multiple files to flag -f, don't use -l flag")
}
if cmd.Flags().Changed("start-label") && cmd.Flags().Changed("end-label") {
fileInfo := strings.Split(FileToRead, ",")
for i := 0; i < len(fileInfo); i++ {
inputFlag.Filename = fileInfo[i]
if err := modfile.ChangeFile(inputFlag); err != nil {
log.Fatal(err)
}
}
} else {
fileInfo := strings.Split(FileToRead, ",")
for i := 0; i < len(fileInfo); i++ {
if strings.Contains(fileInfo[i], ":") {
parts := strings.Split(fileInfo[i], ":")
if len(parts) != 2 {
log.Fatalf("invalid syntax. Use 'File:lines'")
}
inputFlag.Filename = parts[0]
inputFlag.LineNum = parts[1]
if err := modfile.ChangeFile(inputFlag); err != nil {
log.Fatal(err)
}
} else {
log.Fatalf("invalid syntax. Use 'File:lines'")
}
}
}
} else {
if cmd.Flags().Changed("line") || cmd.Flags().Changed("start-label") && cmd.Flags().Changed("end-label") {
inputFlag.Filename = FileToRead
if err := modfile.ChangeFile(inputFlag); err != nil {
log.Fatal(err)
}
} else {
log.Fatalf("Not specified what you want to modify: add -l flag or -s and -e flags")
}
}
}
}


func customHelpFunc(cmd *cobra.Command, args []string) {
fmt.Println("Tgcom CLI Application")
fmt.Println()
fmt.Println(cmd.Long)
fmt.Println()
fmt.Println("Usage:")
fmt.Println(" tgcom [flags]")
fmt.Println()
fmt.Println("Flags:")
cmd.Flags().VisitAll(func(flag *pflag.Flag) {
if flag.Name == "action" {
fmt.Printf(" -%s, --%s: %s (default: %s)\n", flag.Shorthand, flag.Name, flag.Usage, flag.DefValue)
} else {
fmt.Printf(" -%s, --%s: %s\n", flag.Shorthand, flag.Name, flag.Usage)
}
})
fmt.Println()
fmt.Println("Supported Languages:")
for lang := range modfile.CommentChars {
fmt.Println(lang)
}
fmt.Println()
fmt.Println("Examples:")
fmt.Println(" # Toggle comments on lines 1-5 in example.go")
fmt.Println(" tgcom -f example.go -l 1-5 -a toggle")
fmt.Println()
fmt.Println(" # Dry run: show the changes without modifying the file")
fmt.Println(" tgcom -f example.go -s START -e END -a toggle -d")
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment for the customHelpFunc function.

The customHelpFunc function provides custom help information.

+// customHelpFunc provides custom help information.
 func customHelpFunc(cmd *cobra.Command, args []string) {
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func customHelpFunc(cmd *cobra.Command, args []string) {
fmt.Println("Tgcom CLI Application")
fmt.Println()
fmt.Println(cmd.Long)
fmt.Println()
fmt.Println("Usage:")
fmt.Println(" tgcom [flags]")
fmt.Println()
fmt.Println("Flags:")
cmd.Flags().VisitAll(func(flag *pflag.Flag) {
if flag.Name == "action" {
fmt.Printf(" -%s, --%s: %s (default: %s)\n", flag.Shorthand, flag.Name, flag.Usage, flag.DefValue)
} else {
fmt.Printf(" -%s, --%s: %s\n", flag.Shorthand, flag.Name, flag.Usage)
}
})
fmt.Println()
fmt.Println("Supported Languages:")
for lang := range modfile.CommentChars {
fmt.Println(lang)
}
fmt.Println()
fmt.Println("Examples:")
fmt.Println(" # Toggle comments on lines 1-5 in example.go")
fmt.Println(" tgcom -f example.go -l 1-5 -a toggle")
fmt.Println()
fmt.Println(" # Dry run: show the changes without modifying the file")
fmt.Println(" tgcom -f example.go -s START -e END -a toggle -d")
}
// customHelpFunc provides custom help information.
func customHelpFunc(cmd *cobra.Command, args []string) {
fmt.Println("Tgcom CLI Application")
fmt.Println()
fmt.Println(cmd.Long)
fmt.Println()
fmt.Println("Usage:")
fmt.Println(" tgcom [flags]")
fmt.Println()
fmt.Println("Flags:")
cmd.Flags().VisitAll(func(flag *pflag.Flag) {
if flag.Name == "action" {
fmt.Printf(" -%s, --%s: %s (default: %s)\n", flag.Shorthand, flag.Name, flag.Usage, flag.DefValue)
} else {
fmt.Printf(" -%s, --%s: %s\n", flag.Shorthand, flag.Name, flag.Usage)
}
})
fmt.Println()
fmt.Println("Supported Languages:")
for lang := range modfile.CommentChars {
fmt.Println(lang)
}
fmt.Println()
fmt.Println("Examples:")
fmt.Println(" # Toggle comments on lines 1-5 in example.go")
fmt.Println(" tgcom -f example.go -l 1-5 -a toggle")
fmt.Println()
fmt.Println(" # Dry run: show the changes without modifying the file")
fmt.Println(" tgcom -f example.go -s START -e END -a toggle -d")
}


func customUsageFunc(cmd *cobra.Command) error {
fmt.Println(cmd.Short)
fmt.Println("Usage:")
fmt.Printf(" %s\n", cmd.UseLine())
fmt.Println()
fmt.Println("Flags:")
cmd.Flags().VisitAll(func(flag *pflag.Flag) {
if flag.Name == "action" {
fmt.Printf(" -%s, --%s: %s (default: %s)\n", flag.Shorthand, flag.Name, flag.Usage, flag.DefValue)
} else {
fmt.Printf(" -%s, --%s: %s\n", flag.Shorthand, flag.Name, flag.Usage)
}
})
return nil
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment for the customUsageFunc function.

The customUsageFunc function provides custom usage information.

+// customUsageFunc provides custom usage information.
 func customUsageFunc(cmd *cobra.Command) error {
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func customUsageFunc(cmd *cobra.Command) error {
fmt.Println(cmd.Short)
fmt.Println("Usage:")
fmt.Printf(" %s\n", cmd.UseLine())
fmt.Println()
fmt.Println("Flags:")
cmd.Flags().VisitAll(func(flag *pflag.Flag) {
if flag.Name == "action" {
fmt.Printf(" -%s, --%s: %s (default: %s)\n", flag.Shorthand, flag.Name, flag.Usage, flag.DefValue)
} else {
fmt.Printf(" -%s, --%s: %s\n", flag.Shorthand, flag.Name, flag.Usage)
}
})
return nil
}
// customUsageFunc provides custom usage information.
func customUsageFunc(cmd *cobra.Command) error {
fmt.Println(cmd.Short)
fmt.Println("Usage:")
fmt.Printf(" %s\n", cmd.UseLine())
fmt.Println()
fmt.Println("Flags:")
cmd.Flags().VisitAll(func(flag *pflag.Flag) {
if flag.Name == "action" {
fmt.Printf(" -%s, --%s: %s (default: %s)\n", flag.Shorthand, flag.Name, flag.Usage, flag.DefValue)
} else {
fmt.Printf(" -%s, --%s: %s\n", flag.Shorthand, flag.Name, flag.Usage)
}
})
return nil
}

func clearScreen() {
var cmd *exec.Cmd
switch runtime.GOOS {
case "windows":
cmd = exec.Command("cmd", "/c", "cls")
default:
cmd = exec.Command("clear")
}
cmd.Stdout = os.Stdout
cmd.Run()
}
Comment on lines +204 to +214
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment for the clearScreen function.

The clearScreen function clears the terminal screen.

+// clearScreen clears the terminal screen.
 func clearScreen() {
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func clearScreen() {
var cmd *exec.Cmd
switch runtime.GOOS {
case "windows":
cmd = exec.Command("cmd", "/c", "cls")
default:
cmd = exec.Command("clear")
}
cmd.Stdout = os.Stdout
cmd.Run()
}
// clearScreen clears the terminal screen.
func clearScreen() {
var cmd *exec.Cmd
switch runtime.GOOS {
case "windows":
cmd = exec.Command("cmd", "/c", "cls")
default:
cmd = exec.Command("clear")
}
cmd.Stdout = os.Stdout
cmd.Run()
}

125 changes: 125 additions & 0 deletions cmd/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package cmd

import (
"fmt"
"io"
"log"
"os"
"os/exec"
"os/signal"
"strings"
"syscall"
"time"

"github.com/creack/pty"
"github.com/dyne/tgcom/utils/server"
"github.com/spf13/cobra"
"golang.org/x/term"
)

// serverCmd represents the server command
var serverCmd = &cobra.Command{
Use: "server",
Short: "Start the SSH server",
Long: `Start the SSH server that allows remote interactions with tgcom.`,
Run: func(cmd *cobra.Command, args []string) {
server.StartServer()
},
}

func init() {
// Register the server command
rootCmd.AddCommand(serverCmd)
}

func executeRemoteCommand(remotePath string) {
parts := strings.SplitN(remotePath, "@", 2)
if len(parts) != 2 {
fmt.Println("Invalid format. Usage: tgcom -w user@remote:/path/folder")
os.Exit(1)
}

userHost := parts[0]
pathParts := strings.SplitN(parts[1], ":", 2)
if len(pathParts) != 2 {
fmt.Println("Invalid format. Usage: tgcom -w user@remote:/path/folder")
os.Exit(1)
}

host := pathParts[0]
dir := pathParts[1]

sshCmd := "ssh"
sshArgs := []string{"-t", "-p", "2222", userHost + "@" + host, "tgcom", dir}

// Start SSH command with PTY
if err := startSSHWithPTY(sshCmd, sshArgs); err != nil {
log.Fatalf("Error starting SSH with PTY: %v", err)
}
}

func startSSHWithPTY(cmd string, args []string) error {
// Create SSH command
sshCommand := exec.Command(cmd, args...)

// Start PTY
ptmx, err := pty.Start(sshCommand)
if err != nil {
return fmt.Errorf("failed to start PTY: %w", err)
}
defer ptmx.Close()

// Set terminal attributes
oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
if err != nil {
return fmt.Errorf("failed to make terminal raw: %w", err)
}
defer term.Restore(int(os.Stdin.Fd()), oldState)

// Resize PTY to current terminal size
if err := resizePTY(ptmx); err != nil {
return fmt.Errorf("failed to resize PTY: %w", err)
}

// Forward input to PTY
go func() {
_, _ = io.Copy(ptmx, os.Stdin)
}()

// Forward output from PTY
go func() {
_, _ = io.Copy(os.Stdout, ptmx)
}()

// Handle PTY signals and resize
go func() {
ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGWINCH)
for range ch {
if err := resizePTY(ptmx); err != nil {
log.Printf("Error resizing PTY: %v", err)
}
}
}()

// Wait for SSH command to finish
if err := sshCommand.Wait(); err != nil {
return fmt.Errorf("SSH command failed: %w", err)
}

// Wait a bit before exiting to ensure all output is processed
time.Sleep(100 * time.Millisecond)

return nil
}
Comment on lines +35 to +114
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review of SSH command execution functions: Enhance error handling and resource management.

  1. Resource Management: Ensure that all resources like PTY and terminal states are properly managed. Consider adding more robust cleanup in error scenarios.
  2. Error Handling: The error handling could be more descriptive, providing more context about the failures.

Refactor the error handling to provide more detailed error messages and ensure resources are cleaned up even when errors occur.


func resizePTY(ptmx *os.File) error {
size, err := pty.GetsizeFull(os.Stdin)
if err != nil {
return fmt.Errorf("failed to get terminal size: %w", err)
}
if err := pty.Setsize(ptmx, size); err != nil {
return fmt.Errorf("failed to set terminal size: %w", err)
}
return nil
}
Loading