Skip to content
Chris McGee edited this page Jan 22, 2014 · 11 revisions

Introduction

With each sprint GoDev has gained new features making it a richer IDE. For the most part these features have made use of existing tools written by others (e.g. godef, oracle, gocode, godbg). In many cases the authors of these tools intend for them to be plugged into text editors such as vim and emacs by providing configuration files that the user can install. This extensibility allows third parties to integrate interesting features independently.

Moving forward, one of the concerns with GoDev is that the standard install becomes too bloated with features making it harder to understand, slower to run and more complicated to navigate. Extensions allow the user to choose what features they care about and install only those pieces. GoDev should retain a basic set of functionality that most users will expect

Requirements

  1. Extensions shall be trivial to install
  2. Extensions shall be easy to write (documented, examples provided, minimal boilerplate)

Design

Installing a new extension to GoDev should be like adding any other Go library using "go get." Go developers quickly become accustomed to getting libraries and Go commands this way. It gives the user the code in case they want to read, tweak or customize it. It works with different VCS systems (git, hg, svn). Finally, it can calculate dependency closures to retrieve everything you need. This is an obvious choice to retrieve GoDev extensions.

$ go get github.com/sirnewton01/mygodev-extension

Features in GoDev usually have two aspects to them. There is a UI that is plugged in via Orion services (page, edit command) leveraging browser-side capabilities in HTML and Javascript. There is an http service written in Go to leverage such facilities as the Go standard library, third party Go libraries or a command-line tool (maybe a Go command, maybe something like git or hg). The UI portion of the extension often calls back to the http service using XMLHttpRequest or similar. Any design will need to support extensions that have either aspect or both.

UI Bundle

For the UI portion, GoDev is built on top of the Eclipse Orion project (http://www.eclipse.org/orion/). There are well-defined extensions to allow you to contribute new UI's. For example, there is a way to plug in your own "page," which appears in the top-left menu. See the "Go Doc" and "Debug" pages for examples. Also, there are "edit commands" to contribute buttons on the top of the editor. The "Go Doc" and "Format" buttons are examples of edit commands. The GoDev server provides a special JSON object at a predefined location to tell Orion what extension should be loaded in the browser. GoDev actively scans your GOPATH looking for godev extensions and provides up-to-date list of extensions to the browser. With this mechanism in place you can simply "go get" a new extension and reload your web browser page to see the new pages and buttons.

src/github.com/sirnewton01/mygodev-extension
                                             /godev-bundle
                                                           mygodev-extension/
                                                                             bundle.html
                                                                             bundle.js
                                                                             coolhtmlpage.html

The file structure above represents a typical Go package except it has some special files and folders. The "godev-bundle" folder is there to tell the godev server that this is a UI bundle. The "mygodev-extension" folder underneath provides a unique namespace to distinguish web resources in your bundle from others. The bundle.html and bundle.js are known to the godev server to be your UI bundle's initializers, which declare the pages, edit commands and Orion extension that you want to define (see http://wiki.eclipse.org/Orion/Documentation/Developer_Guide/Plugging_into_the_editor and http://wiki.eclipse.org/Orion/Documentation/Developer_Guide/Plugging_into_Orion_pages). Finally, you can put whatever web content you want inside your "mygodev-extension" folder such as HTML pages, images, css, javascript. Your bundle will reference these things using the URL prefix "/mygodev-extension" (e.g. "/mygodev-extension/coolhtmlpage.html").

Service Bundle

Ideally, extensions would only require UI bundles as above. Go get will complain that there are no Go sources to be compiled but then you can always put a dummy.go file in your project, which should squelch the error. It is often the case that you want to integrate your UI with something that is only available outside of the web browser such as your own Go code, modules in the Go standard library, third party libraries or some system command. To bridge the gap between your HTML UI and the browser you need an http service.

For better or for worse, Go has no dynamic linking built into the language. As a result of this, godev can't dynamically load a service and register it with its http server without statically importing, recompiling and restarting. It will be impossible to do all of this within a simple "go get." Restarting godev currently forces you to re-authenticate, which is not great for the workflow either.

If Go does not support dynamic linking then an alternative is to use a form of RPC (Remote Process Communication). One platform independent way of doing this is to launch the program and establish a TCP port to communicate. Since godev will control the lifecycle of the service we could launch it and use stdin/stdout. We could make stdin and stdout send a dump of http requests and responses. In other words, we are essentially re-inventing CGI (Common Gateway Interface) from the days of yore. So, why not use CGI itself? It turns out that Go has a standard library for both client and server.

GoDev registers a handler for any requests with the "/go/bundle-cgi" prefix, take the command name in the third segment (e.g. "mygodev-extension" in "/go/bundle-cgi/mygodev-extension") and search for it in the GOPATH. If it finds it, it will run the command with the "-godev" argument and begin a CGI protocol with your command. The "-godev" argument is provided in case your command is not just a godev extension but also serves other purposes. The idea is that any command-line tool could optionally hook into GoDev as an extension.

import (
    "fmt"
    "net/http"
    "net/http/cgi"
    "strings"
)

func main() {
    // TODO Check for the "-godev" parameter
    req, err := cgi.Request()

    if err != nil {
        fmt.Printf("Error trying to set up the CGI request", err.Error())
        return
    }

    if strings.HasSuffix(req.URL.Path, "/greeting") {
        fmt.Printf("Status: 200 OK\r\n\r\n{Greeting: 'Hello World!'}")
    } else {
        fmt.Printf("Status: 404 Unrecognized request\r\n\r\n")
    }
}

CGI does have its limitations. Every time you make a request the CGI routine will launch the Go command, which has some overhead. Luckily, Go programs start very quickly. We don't anticipate that GoDev extension services will be called rapidly in the UI since it is a single user environment at the moment. Extensions for a multi-user environment will have different requirements and will need a different design.

It is unfortunate that the Go CGI library doesn't have a utility to write out the HTTP headers, status codes, etc. It is not difficult to either do as the example does above and concatenate your own output strings. For more complicated services it is not very difficult to implement your own http.ResponseWriter that writes to stdout. GoDev may provide some utilities to help in this area and/or try to get a response writer added to the net/http/cgi package.

Conclusion

Over the next few sprints GoDev will be restructuring itself to pull out some of the current features into separate github projects. This will help with the current bloat as well as provide examples of how to write GoDev extensions. Where possible, we will attempt to provide pull requests to other tool authors to add GoDev integrations.

Clone this wiki locally