diff --git a/pkg/commands/exec_live_default.go b/pkg/commands/exec_live_default.go deleted file mode 100644 index 0e43a67..0000000 --- a/pkg/commands/exec_live_default.go +++ /dev/null @@ -1,95 +0,0 @@ -// +build !windows - -package commands - -import ( - "bufio" - "bytes" - "strings" - "unicode/utf8" - - "github.com/go-errors/errors" - - "github.com/creack/pty" -) - -// RunCommandWithOutputLiveWrapper runs a command and return every word that gets written in stdout -// Output is a function that executes by every word that gets read by bufio -// As return of output you need to give a string that will be written to stdin -// NOTE: If the return data is empty it won't written anything to stdin -func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(string) string) error { - cmd := c.ExecutableFromString(command) - cmd.Env = append(cmd.Env, "LANG=en_US.UTF-8", "LC_ALL=en_US.UTF-8") - - var stderr bytes.Buffer - cmd.Stderr = &stderr - - ptmx, err := pty.Start(cmd) - - if err != nil { - return err - } - - go func() { - scanner := bufio.NewScanner(ptmx) - scanner.Split(scanWordsWithNewLines) - for scanner.Scan() { - toOutput := strings.Trim(scanner.Text(), " ") - _, _ = ptmx.WriteString(output(toOutput)) - } - }() - - err = cmd.Wait() - ptmx.Close() - if err != nil { - return errors.New(stderr.String()) - } - - return nil -} - -// scanWordsWithNewLines is a copy of bufio.ScanWords but this also captures new lines -// For specific comments about this function take a look at: bufio.ScanWords -func scanWordsWithNewLines(data []byte, atEOF bool) (advance int, token []byte, err error) { - start := 0 - for width := 0; start < len(data); start += width { - var r rune - r, width = utf8.DecodeRune(data[start:]) - if !isSpace(r) { - break - } - } - for width, i := 0, start; i < len(data); i += width { - var r rune - r, width = utf8.DecodeRune(data[i:]) - if isSpace(r) { - return i + width, data[start:i], nil - } - } - if atEOF && len(data) > start { - return len(data), data[start:], nil - } - return start, nil, nil -} - -// isSpace is also copied from the bufio package and has been modified to also captures new lines -// For specific comments about this function take a look at: bufio.isSpace -func isSpace(r rune) bool { - if r <= '\u00FF' { - switch r { - case ' ', '\t', '\v', '\f': - return true - case '\u0085', '\u00A0': - return true - } - return false - } - if '\u2000' <= r && r <= '\u200a' { - return true - } - switch r { - case '\u1680', '\u2028', '\u2029', '\u202f', '\u205f', '\u3000': - return true - } - return false -} diff --git a/pkg/commands/exec_live_win.go b/pkg/commands/exec_live_win.go deleted file mode 100644 index d06cb92..0000000 --- a/pkg/commands/exec_live_win.go +++ /dev/null @@ -1,9 +0,0 @@ -// +build windows - -package commands - -// RunCommandWithOutputLiveWrapper runs a command live but because of windows compatibility this command can't be ran there -// TODO: Remove this hack and replace it with a proper way to run commands live on windows -func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(string) string) error { - return c.RunCommand(command) -} diff --git a/pkg/commands/os.go b/pkg/commands/os.go index a255e39..ce86027 100644 --- a/pkg/commands/os.go +++ b/pkg/commands/os.go @@ -7,7 +7,6 @@ import ( "os" "os/exec" "path/filepath" - "regexp" "strings" "sync" @@ -117,36 +116,6 @@ func (c *OSCommand) ExecutableFromString(commandStr string) *exec.Cmd { return cmd } -// RunCommandWithOutputLive runs RunCommandWithOutputLiveWrapper -func (c *OSCommand) RunCommandWithOutputLive(command string, output func(string) string) error { - return RunCommandWithOutputLiveWrapper(c, command, output) -} - -// DetectUnamePass detect a username / password question in a command -// ask is a function that gets executen when this function detect you need to fillin a password -// The ask argument will be "username" or "password" and expects the user's password or username back -func (c *OSCommand) DetectUnamePass(command string, ask func(string) string) error { - ttyText := "" - errMessage := c.RunCommandWithOutputLive(command, func(word string) string { - ttyText = ttyText + " " + word - - prompts := map[string]string{ - "password": `Password\s*for\s*'.+':`, - "username": `Username\s*for\s*'.+':`, - } - - for askFor, pattern := range prompts { - if match, _ := regexp.MatchString(pattern, ttyText); match { - ttyText = "" - return ask(askFor) - } - } - - return "" - }) - return errMessage -} - // RunCommand runs a command and just returns the error func (c *OSCommand) RunCommand(formatString string, formatArgs ...interface{}) error { _, err := c.RunCommandWithOutput(formatString, formatArgs...) diff --git a/pkg/config/app_config.go b/pkg/config/app_config.go index b1e73c9..7337da7 100644 --- a/pkg/config/app_config.go +++ b/pkg/config/app_config.go @@ -305,6 +305,8 @@ keybinding: prevScreenMode: '_' undo: 'z' redo: '' + install: 'i' + cleanInstall: 'I' status: checkForUpdate: 'u' main: diff --git a/pkg/gui/dependencies_panel.go b/pkg/gui/dependencies_panel.go index 958d3d1..ac3c00d 100644 --- a/pkg/gui/dependencies_panel.go +++ b/pkg/gui/dependencies_panel.go @@ -25,3 +25,14 @@ func (gui *Gui) handleDepSelect(g *gocui.Gui, v *gocui.View) error { } return nil } + +// linkPathMap returns the set of link paths of the current package's dependencies +func (gui *Gui) linkPathMap() map[string]bool { + linkPathMap := map[string]bool{} + for _, dep := range gui.State.Deps { + if dep.Linked() { + linkPathMap[dep.LinkPath] = true + } + } + return linkPathMap +} diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index f0a453e..c6eeb12 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -439,6 +439,12 @@ func (gui *Gui) GetInitialKeybindings() []*Binding { Modifier: gocui.ModNone, Handler: gui.handleLinkPackage, }, + { + ViewName: "", + Key: gui.getKey("universal.install"), + Modifier: gocui.ModNone, + Handler: gui.wrappedHandler(gui.handleInstall), + }, } for _, viewName := range []string{"status", "packages", "deps", "scripts", "menu"} { diff --git a/pkg/gui/packages_panel.go b/pkg/gui/packages_panel.go index 7fdc42c..af54089 100644 --- a/pkg/gui/packages_panel.go +++ b/pkg/gui/packages_panel.go @@ -116,15 +116,31 @@ func (gui *Gui) handleLinkPackage(g *gocui.Gui, v *gocui.View) error { var cmdStr string if selectedPkg == currentPkg { - cmdStr = "npm link" + if gui.linkPathMap()[selectedPkg.Path] { + cmdStr = "npm unlink" + } else { + cmdStr = "npm link" + } } else { - cmdStr = fmt.Sprintf("npm link %s", selectedPkg.Config.Name) + if gui.linkPathMap()[selectedPkg.Path] { + cmdStr = fmt.Sprintf("npm unlink --no-save %s", selectedPkg.Config.Name) + } else { + cmdStr = fmt.Sprintf("npm link %s", selectedPkg.Config.Name) + } } cmd := gui.OSCommand.ExecutableFromString(cmdStr) - if err := gui.newCmdTask("main", cmd); err != nil { + if err := gui.newPtyTask("main", cmd); err != nil { gui.Log.Error(err) } return nil } + +func (gui *Gui) handleInstall() error { + cmd := gui.OSCommand.ExecutableFromString("npm install") + if err := gui.newPtyTask("main", cmd); err != nil { + gui.Log.Error(err) + } + return nil +}