-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added support for Plan9Ports plumber
Instead of poorly imitating the plumbing service, de can now use the real p9p plumber on unix-like systems. Since de itself is single-buffer, the new service deplumber must be run running in order to receive plumbing messages and coordinate whether a new de window should be created, or the existing one re-used. If the plumbing message came from de and the buffer is clean, it will tell that window to open the new file. Otherwise, it'll open a new one. This frees de from having to do window management (leaving that job to the window manager) while still getting the benefits of using the real plumber.
- Loading branch information
Showing
10 changed files
with
740 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# Plumbing | ||
|
||
(If you're unfamiliar with the Plan 9 concept of "plumbing", you may want | ||
to start by reading [the paper](http://doc.cat-v.org/plan_9/4th_edition/papers/plumb)) | ||
|
||
de now supports directly using the plan9ports plumber for interpreting | ||
interactions, if it's available, with a couple caveats. | ||
|
||
1. Since de is a single-buffer editor, we need another process (the deplumber) | ||
to handle the incoming messages. (If de handled the edit port itself either | ||
either every window would process each edit message and the windows | ||
would multiply like rabbits, or the plumbing would stop working after the | ||
first window closed.) | ||
2. The default p9p plumbing rules don't plumb directories to $editor. The file | ||
plumbing.sample is a sample config that you can put in $HOME/lib/plumbing | ||
in order to use deplumber to listen on the edit port, and also plumb | ||
directories to edit. | ||
|
||
So to use de with plumbing the steps are: | ||
1. Run "plumber" (from p9p) | ||
2. Run "deplumber" (from de, you may need to `go get github.com/driusan/de/...` first) in the background) | ||
3. Run de, or plumb a message some other way to test it. | ||
|
||
You can plumb from de either by hitting the Enter key or right clicking | ||
somewhere. If the message couldn't be successfully plumbed or deplumber isn't | ||
running, de will fall back on the old behaviour (find next for right-click, and | ||
execute for enter.) | ||
|
||
de is a text editor, not a window manager. If you're running under X11, you may | ||
want to use a tiling window manager in order to have your windows managed in a | ||
way that makes de more closely resemble acme. Otherwise, de doesn't pretend | ||
that it knows how you like your windows arranged better than you and your window | ||
manager. | ||
|
||
The integration of plumbing directly into de is relatively new, so if you have | ||
any problems, please file a bug report. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,209 @@ | ||
package actions | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"strconv" | ||
|
||
"9fans.net/go/plan9" | ||
plumblib "9fans.net/go/plumb" | ||
|
||
"github.com/driusan/de/demodel" | ||
) | ||
|
||
// This boolean indicates whether plumbing is ready to be used. It should | ||
// generally only be called by the main thread to flag that plumb messages | ||
// are fine to use. | ||
// | ||
// Until the main thread does that, plumb will fail, assuming that it hasn't | ||
// been properly initialized. | ||
var PlumbingReady bool | ||
|
||
func plumb(content []byte, buff *demodel.CharBuffer, v demodel.Viewport, click int) error { | ||
if !PlumbingReady { | ||
return fmt.Errorf("Plumbing unavailable") | ||
} | ||
fid, err := plumblib.Open("send", plan9.OWRITE) | ||
if err != nil { | ||
fmt.Printf("%v", err) | ||
return err | ||
} | ||
|
||
wd, _ := os.Getwd() | ||
m := plumblib.Message{ | ||
Src: "de", | ||
Dst: "", | ||
Dir: wd, | ||
Type: "text", | ||
Data: content, | ||
} | ||
if click != 0 { | ||
m.Attr = &plumblib.Attribute{Name: "click", Value: strconv.Itoa(click)} | ||
} | ||
return m.Send(fid) | ||
} | ||
|
||
func PlumbExecuteOrFindNext(From, To demodel.Position, buff *demodel.CharBuffer, v demodel.Viewport) { | ||
if buff == nil { | ||
return | ||
} | ||
dot := demodel.Dot{} | ||
i, err := From(*buff) | ||
if err != nil { | ||
return | ||
} | ||
dot.Start = i | ||
|
||
i, err = To(*buff) | ||
if err != nil { | ||
return | ||
} | ||
dot.End = i + 1 | ||
|
||
word := string(buff.Buffer[dot.Start:dot.End]) | ||
|
||
// Don't bother trying if there's nothing listening yet. | ||
if PlumbingReady { | ||
// Create a new []byte for the plumber, because if nothing | ||
// was selected we want to send a message with a click | ||
// attribute, and if plumbing fails we don't want to have | ||
// touched the word for the fallbacks. | ||
var plumbword []byte | ||
var click int | ||
var pdot demodel.Dot | ||
|
||
if dot.Start == dot.End-1 { | ||
// Nothing was selected, to add a "click" attribute | ||
if dot.Start < 100 { | ||
click = int(dot.Start) | ||
pdot.Start = 0 | ||
} else { | ||
click = 100 | ||
pdot.Start = dot.Start - 100 | ||
} | ||
if dot.End+100 < uint(len(buff.Buffer)) { | ||
pdot.End = pdot.End + 100 | ||
} else { | ||
pdot.End = uint(len(buff.Buffer)) | ||
} | ||
plumbword = buff.Buffer[pdot.Start:pdot.End] | ||
} else { | ||
// Default to "word" (the selected text) | ||
// with no click attribute | ||
plumbword = []byte(word) | ||
} | ||
|
||
// If the message was successfully plumbed, we're done. | ||
if err := plumb(plumbword, buff, v, click); err == nil { | ||
return | ||
} | ||
} | ||
// Try executing the command. If it works, we're done. | ||
if err := RunOrExec(word, buff, v); err == nil { | ||
return | ||
} | ||
|
||
// We couldn't plumb it, we couldn't execute it, so give up and search for | ||
// the word | ||
lenword := dot.End - dot.Start | ||
for i := dot.End; i < uint(len(buff.Buffer))-lenword; i++ { | ||
if string(buff.Buffer[i:i+lenword]) == word { | ||
buff.Dot.Start = i | ||
buff.Dot.End = i + lenword - 1 | ||
return | ||
} | ||
} | ||
|
||
} | ||
func PlumbOrFindNext(From, To demodel.Position, buff *demodel.CharBuffer, v demodel.Viewport) { | ||
if buff == nil { | ||
return | ||
} | ||
dot := demodel.Dot{} | ||
i, err := From(*buff) | ||
if err != nil { | ||
return | ||
} | ||
dot.Start = i | ||
|
||
i, err = To(*buff) | ||
if err != nil { | ||
return | ||
} | ||
dot.End = i + 1 | ||
|
||
var word string | ||
|
||
// If nothing is selected, instead send 100 characters before and after | ||
// and include a "click" attribute in the plumbing message. | ||
var click int | ||
if dot.Start == dot.End-1 { | ||
if dot.Start < 100 { | ||
click = int(dot.Start) | ||
dot.Start = 0 | ||
} else { | ||
click = 100 | ||
dot.Start -= 100 | ||
} | ||
if dot.End+100 < uint(len(buff.Buffer)) { | ||
dot.End += 100 | ||
} else { | ||
dot.End = uint(len(buff.Buffer)) | ||
} | ||
} | ||
word = string(buff.Buffer[dot.Start:dot.End]) | ||
|
||
if PlumbingReady { | ||
if err := plumb([]byte(word), buff, v, click); err == nil { | ||
return | ||
} | ||
} | ||
|
||
// the file doesn't exist, so find the next instance of word. | ||
lenword := dot.End - dot.Start | ||
for i := dot.End; i < uint(len(buff.Buffer))-lenword; i++ { | ||
if string(buff.Buffer[i:i+lenword]) == word { | ||
buff.Dot.Start = i | ||
buff.Dot.End = i + lenword - 1 | ||
return | ||
} | ||
} | ||
} | ||
|
||
func TagPlumbOrFindNext(From, To demodel.Position, buff *demodel.CharBuffer, v demodel.Viewport) { | ||
if buff == nil || buff.Tagline == nil { | ||
return | ||
} | ||
dot := demodel.Dot{} | ||
i, err := From(*buff) | ||
if err != nil { | ||
return | ||
} | ||
dot.Start = i | ||
|
||
i, err = To(*buff) | ||
if err != nil { | ||
return | ||
} | ||
dot.End = i + 1 | ||
|
||
// find the word between From and To in the tagline | ||
word := string(buff.Tagline.Buffer[dot.Start:dot.End]) | ||
|
||
if PlumbingReady { | ||
if err := plumb([]byte(word), buff, v, 0); err == nil { | ||
return | ||
} | ||
} | ||
|
||
// the file doesn't exist, so find the next instance of word inside | ||
// the *non-tag* buffer. | ||
lenword := dot.End - dot.Start | ||
for i := buff.Dot.End; i < uint(len(buff.Buffer))-lenword; i++ { | ||
if string(buff.Buffer[i:i+lenword]) == word { | ||
buff.Dot.Start = i | ||
buff.Dot.End = i + lenword - 1 | ||
return | ||
} | ||
} | ||
} |
Oops, something went wrong.