LoginSignup
6
6

More than 5 years have passed since last update.

Google ChromeのテキストエリアをEmacsで編集する'Edit with Emacs'から任意のエディタを起動するデーモンをGo 1.4 for Windowsで書いてみたわけだが、エディタがブラウザの後ろに出てしまってダメかもしれない

Last updated at Posted at 2015-06-18

どういう経緯か忘れたのですが

という記事を拝見しました。概要としては

  • Google Chrome は Firefox とは違い、外部プロセスを起動することは禁止されている
  • そこで Chrome Extension の Edit with Emacs は、Emacs のサービスを 127.0.0.1:9292 に立ち上げて、XMLHttpRequest を使って、テキストエリアの内容を Emacs と交換している。
  • 上記の記事では、Emacs のサーバーのかわりに Perl でサービスを立ち上げて、任意のエディターを Chrome で使えるようにしている

という感じです。記事の方法だと Perl 一式が必要だし、Linux でしか動作が検証されていないようでした(別に Windows で動かないとは書いてないが)。が、記事のおかげで原理はだいたい分かったので、勉強がてら Go 1.4 for Windows で「だいたい同じもの」を作ってみました。

editsrv.go
package main

import (
    "bufio"
    "fmt"
    "html"
    "io"
    "io/ioutil"
    "net/http"
    "os"
    "os/exec"
    "strings"
)

func HasHtml(headers map[string][]string) bool {
    xurl, ok := headers["X-Url"]
    if !ok || len(xurl) <= 0 {
        return false
    }
    return strings.HasPrefix(xurl[0], "https://twitter.com")
}

func typeHeaders(h map[string][]string, w io.Writer) {
    for key, vals := range h {
        for _, val := range vals {
            fmt.Fprintf(w, "%s: %s\n", key, val)
        }
    }
}

func html2text(out io.Writer, in io.Reader) {
    scanner := bufio.NewScanner(in)
    for scanner.Scan() {
        line := scanner.Text()
        fmt.Fprintln(os.Stderr, line)
        line = strings.TrimSpace(line)
        line = strings.Replace(line, "<div><br></div>", "\n", -1)
        line = strings.Replace(line, "<div>", "", -1)
        line = strings.Replace(line, "</div>", "\n", -1)
        line = html.UnescapeString(line)
        io.WriteString(out, line)
    }
}

func text2html(out io.Writer, in io.Reader) {
    scanner := bufio.NewScanner(in)
    for scanner.Scan() {
        line := scanner.Text()
        line = strings.TrimSpace(line)
        line = html.EscapeString(line)
        if line == "" {
            line = "<br>"
        }
        fmt.Fprintf(out, "<div>%s</div>", line)
    }
}

func handler(w http.ResponseWriter, req *http.Request) {
    fmt.Fprintf(os.Stderr, "From: %s\n", req.RemoteAddr)
    typeHeaders(req.Header, os.Stderr)
    tmpfd, tmpfdErr := ioutil.TempFile("", "editsrv")
    if tmpfdErr != nil {
        fmt.Fprintln(os.Stderr, tmpfdErr)
        return
    }
    io.WriteString(tmpfd, "\xEF\xBB\xBF")
    tmpName := tmpfd.Name()
    defer os.Remove(tmpName)

    hasHtml := HasHtml(req.Header)
    if hasHtml {
        html2text(tmpfd, req.Body)
    } else {
        io.CopyN(tmpfd, req.Body, req.ContentLength)
    }
    tmpfd.Close()

    var editorName string
    if len(os.Args) >= 2 {
        editorName = os.Args[1]
    } else {
        editorName = "notepad.exe"
    }
    editorArgs := make([]string, 0, len(os.Args))
    for i := 2; i < len(os.Args); i++ {
        editorArgs = append(editorArgs, os.Args[i])
    }
    editorArgs = append(editorArgs, tmpName)
    cmd1 := exec.Command(editorName, editorArgs...)
    fmt.Fprintf(os.Stderr, "Call %s ", editorName)
    for _, arg1 := range editorArgs {
        fmt.Fprintf(os.Stderr, " %s", arg1)
    }
    fmt.Fprint(os.Stderr, "\n")
    if err := cmd1.Run(); err != nil {
        fmt.Fprintln(os.Stderr, err)
        return
    }
    tmpfd, tmpfdErr = os.Open(tmpName)
    if tmpfdErr != nil {
        fmt.Fprintln(os.Stderr, tmpfdErr)
        tmpfd.Close()
        return
    }
    fmt.Fprintf(os.Stderr, "Send '%s' to Chrome\n", tmpName)
    if hasHtml {
        text2html(w, tmpfd)
    } else {
        _, copyErr2 := io.Copy(w, tmpfd)
        if copyErr2 != nil {
            fmt.Fprintln(os.Stderr, copyErr2)
        }
    }
    tmpfd.Close()
    fmt.Fprintln(os.Stderr, "Done")
}

func main() {
    fmt.Println("Any Editor Server for chrome-extension 'Edit with Emacs'")
    http.HandleFunc("/edit", handler)
    http.ListenAndServe(":9292", nil)
}

最初、よー分からんで何故か TCP のレベルからやろうとして挫折したのですが、net/http で書き直したところ、比較的簡単に出来ました。「editsrv.exe gvim.exe」を走らせておくと、テキストエリアの右下に出る edit のマークをクリックした時に gvim.exe が起動するようになります。(なお、UTF8 対応エディターである必要があります)

ただ、twitter の本家サイトについては、テキストエリアの内容が何故か HTML で飛んでくるので、このサイトだけは特別扱いして、HTML ⇔ プレーンテキストの変換を相互にやってます。やりすぎかな

普通、この手のものは「思いついたのはいいが、なかなかうまいこと作れない」ものなんですが、自分にしては、あっさりできて驚きでした。ま、多分、似たようなものの Go 版は先人がとっくの昔に作ってそうではありますが…まぁ、手段が目的みたいなもんですから、そこんところは目をつぶってくださいませ。

しかし…起動したエディターがブラウザの後ろに隠れてしまうのはどうしたもんかな…

6
6
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6