Trying to Launch an External Editor from Within a Go Program

Trying to launch an external editor from within a Go program

Apparently, you have to set Stdin, Stdout and Stderr on the Cmd object to os.Std(in|out|err). Like this (assuming that the object is called cmd):

cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

Credit for solving this goes to the guys on #go-nuts on freenode.

Calling an external command in Go

You need to use the exec package : start a command using Command and use Run to wait for completion.

cmd := exec.Command("yourcommand", "some", "args")
if err := cmd.Run(); err != nil {
fmt.Println("Error: ", err)
}

If you just want to read the result, you may use Output instead of Run.

How do I open a file with a text editor in Go?

You should call exec.Command on a string whose content is a executable command path, with some arguments for this command as other parameters, like:

    // shortcut for command in ENV PATH
//exepath := "notepad"
exepath := "C:\\Windows\\system32\\notepad.exe"
file := "H:\\code\\txt_projects\\hello6.txt"
cmd := exec.Command(exepath, file)

Refer to document.

os.Wait() does not wait for program termination in golang

Shouldn't cmd.Wait() be handling this?

Yes, and it does. Go waits as intended, it's your invocation of /usr/local/bin/code which is incorrect, and does not wait. The default behavior of code is to exit immediately after spawning the VSCode window. It does not wait for the window to close, and so Go cannot wait for the window to close.

Try simply typing code in your terminal. You'll find that it exits immediately, even thought your VSCode window is still open.

To make code block until the editor window is closed (thereby allowing Go to wait), you need to pass the -w or --wait flag to it. Again, try code -w in your terminal. You'll find the terminal command blocks until the VSCode window is closed.

Practically, you only need to change this...

    cmd := exec.Command("/usr/local/bin/code", fpath)

to this:

    cmd := exec.Command("/usr/local/bin/code", "-w", fpath)
// or
// cmd := exec.Command("/usr/local/bin/code", "--wait", fpath)

VSCode: Could not import Golang package

This happens to me in a few specific situations. This is my troubleshooting process :

  1. Did you run go get github.com/gomodule/redigo/redis?

  2. Sometimes I have a similar issue when I open my editor in a root different than my project.

.  <- editor open here
|
|_Folder
| main.go

  1. Make sure your tools are up to date: run ctrl + shift + p, type Go and chose Install/Update tools.

  2. Try moving your project out of the GOPATH, and setting up go.mod for it.

  3. Restart the editor

How do you open an external Vim editor containing the active Eclipse editor file

There's two ways to do this, the canonical way, and the hack. First:

The Canonical Way

  1. Window > Preferences... Fold out General > Editors > File Associations.
  2. Choose the file type you want to edit
  3. Click the "Add" button beside the "Associated editors" box.
  4. Click "External programs" and "Browse...", then find gvim, or enter /usr/bin/gvim, or generally make it go to gvim.

Now, files of that type should show up in an external vim instance. Awesome, right? But that doesn't solve your problem of pushing the current buffer out to vim.


The Hack

Instead, we're going to set vim as a "build tool", and have eclipse send it the current file as arguments. This may have some unaffected side-effects, based on your project settings, but look into them carefully if you experience things like unexpected re-building of your files.

  1. Run > External Tools > External Tools Configuration...
  2. Select "Program" on the left and click the "New" icon.
  3. Enter "Send to vim" for the Name
  4. Enter "/usr/bin/gvim" (or whatever your path is) for the Location
  5. Under "Working Directory" enter ${project_loc} (this is a variable representing your project's top directory)
  6. Under arguments enter ${resource_loc} (this represents your current resource's path)
  7. On the "Build" tab, un-check "Build before Launch" (unless you really want that)
  8. On the Common tab, check "External Tools" under the "Display in favorites menu"

You should be all set! Now you can send your file to vim by using the menu

Run > External Tools > Send to vim

If you want to get fancy, you can even add a button to your toolbar.

Be advised, I've used gvim in the examples. If you'd like to use terminal vim, you'll have to call it appropriately based on the terminal you're using. For xterm, this would be /usr/bin/xterm -e /usr/bin/vim, instead of /usr/bin/gvim

How to read/write from/to a file using Go

Let's make a Go 1-compatible list of all the ways to read and write files in Go.

Because file API has changed recently and most other answers don't work with Go 1. They also miss bufio which is important IMHO.

In the following examples I copy a file by reading from it and writing to the destination file.

Start with the basics

package main

import (
"io"
"os"
)

func main() {
// open input file
fi, err := os.Open("input.txt")
if err != nil {
panic(err)
}
// close fi on exit and check for its returned error
defer func() {
if err := fi.Close(); err != nil {
panic(err)
}
}()

// open output file
fo, err := os.Create("output.txt")
if err != nil {
panic(err)
}
// close fo on exit and check for its returned error
defer func() {
if err := fo.Close(); err != nil {
panic(err)
}
}()

// make a buffer to keep chunks that are read
buf := make([]byte, 1024)
for {
// read a chunk
n, err := fi.Read(buf)
if err != nil && err != io.EOF {
panic(err)
}
if n == 0 {
break
}

// write a chunk
if _, err := fo.Write(buf[:n]); err != nil {
panic(err)
}
}
}

Here I used os.Open and os.Create which are convenient wrappers around os.OpenFile. We usually don't need to call OpenFile directly.

Notice treating EOF. Read tries to fill buf on each call, and returns io.EOF as error if it reaches end of file in doing so. In this case buf will still hold data. Consequent calls to Read returns zero as the number of bytes read and same io.EOF as error. Any other error will lead to a panic.

Using bufio

package main

import (
"bufio"
"io"
"os"
)

func main() {
// open input file
fi, err := os.Open("input.txt")
if err != nil {
panic(err)
}
// close fi on exit and check for its returned error
defer func() {
if err := fi.Close(); err != nil {
panic(err)
}
}()
// make a read buffer
r := bufio.NewReader(fi)

// open output file
fo, err := os.Create("output.txt")
if err != nil {
panic(err)
}
// close fo on exit and check for its returned error
defer func() {
if err := fo.Close(); err != nil {
panic(err)
}
}()
// make a write buffer
w := bufio.NewWriter(fo)

// make a buffer to keep chunks that are read
buf := make([]byte, 1024)
for {
// read a chunk
n, err := r.Read(buf)
if err != nil && err != io.EOF {
panic(err)
}
if n == 0 {
break
}

// write a chunk
if _, err := w.Write(buf[:n]); err != nil {
panic(err)
}
}

if err = w.Flush(); err != nil {
panic(err)
}
}

bufio is just acting as a buffer here, because we don't have much to do with data. In most other situations (specially with text files) bufio is very useful by giving us a nice API for reading and writing easily and flexibly, while it handles buffering behind the scenes.


Note: The following code is for older Go versions (Go 1.15 and before). Things have changed. For the new way, take a look at this answer.

Using ioutil

package main

import (
"io/ioutil"
)

func main() {
// read the whole file at once
b, err := ioutil.ReadFile("input.txt")
if err != nil {
panic(err)
}

// write the whole body at once
err = ioutil.WriteFile("output.txt", b, 0644)
if err != nil {
panic(err)
}
}

Easy as pie! But use it only if you're sure you're not dealing with big files.



Related Topics



Leave a reply



Submit