Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix 470: missing chords #511

Merged
merged 4 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 72 additions & 55 deletions exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ type Exectab struct {
// TODO(rjk): This could be more idiomatic: each command implements an
// interface. Flags would then be unnecessary.

var exectab = []Exectab{
var globalexectab = []Exectab{
// { "Abort", doabort, false, true /*unused*/, true /*unused*/, },
{"Cut", cut, true, true, true},
{"Del", del, false, false, true /*unused*/},
Expand Down Expand Up @@ -88,7 +88,8 @@ var exectab = []Exectab{

var wsre = regexp.MustCompile("[ \t\n]+")

func lookup(r string) *Exectab {
// TODO(rjk): Exectab is sorted. Consider using a binary search
func lookup(r string, exectab []Exectab) *Exectab {
r = wsre.ReplaceAllString(r, " ")
r = strings.TrimLeft(r, " ")
words := strings.SplitN(r, " ", 2)
Expand Down Expand Up @@ -146,12 +147,12 @@ func getarg(argt *Text, doaddr bool, dofile bool) (string, string) {
return string(r), a
}

// execute must run with an existing lock on t's Window
func execute(t *Text, aq0 int, aq1 int, external bool, argt *Text) {
var n, f int

q0 := aq0
q1 := aq1
// expandRuneOffsetsToWord expands the rune offsets on a middle click to
// the word boundaries. TODO(rjk): Conceivably, what we think of as a
// "word boundary" should be configurable in some way and not embedded in
// this function.
// TODO(rjk): Consider if this method should really be part of Text.
func expandRuneOffsetsToWord(t *Text, q0 int, q1 int) (int, int) {
if q1 == q0 { // expand to find word (actually file name)
// if in selection, choose selection
if t.inSelection(q0) {
Expand All @@ -175,62 +176,78 @@ func execute(t *Text, aq0 int, aq1 int, external bool, argt *Text) {
}
}
if q1 == q0 {
return
return q0, q1
}
}
}
r := make([]rune, q1-q0)
t.file.Read(q0, r)
e := lookup(string(r))
return q0, q1
}

// Send commands to external client if the target window's event file is
// in use.
if !external && t.w != nil && t.w.nopen[QWevent] > 0 {
f = 0
if e != nil {
f |= 1
}
if q0 != aq0 || q1 != aq1 {
r = make([]rune, aq1-aq0)
t.file.Read(aq0, r)
f |= 2
}
a, aa := getarg(argt, true, true)
if a != "" {
if len(a) > EVENTSIZE { // too big; too bad
warning(nil, "argument string too long\n")
return
}
f |= 8
}
c := 'x'
if t.what == Body {
c = 'X'
// delegateExecution handles the situation where an external command is
// using the event file to control the operation of Edwood via the
// filesystem.
func delegateExecution(t *Text, e *Exectab, aq0, aq1, q0, q1 int, argt *Text) {
var r []rune

f := 0
if e != nil {
f |= 1
}
if q0 != aq0 || q1 != aq1 {
r = make([]rune, aq1-aq0)
t.file.Read(aq0, r)
f |= 2
}
a, aa := getarg(argt, true, true)
if a != "" {
if len(a) > EVENTSIZE { // too big; too bad
warning(nil, "argument string too long\n")
return
}
n = aq1 - aq0
f |= 8
}
c := 'x'
if t.what == Body {
c = 'X'
}
n := aq1 - aq0
if n <= EVENTSIZE {
t.w.Eventf("%c%d %d %d %d %v\n", c, aq0, aq1, f, n, string(r))
} else {
t.w.Eventf("%c%d %d %d 0 \n", c, aq0, aq1, f)
}
if q0 != aq0 || q1 != aq1 {
n = q1 - q0
r = make([]rune, n)
t.file.Read(q0, r)
if n <= EVENTSIZE {
t.w.Eventf("%c%d %d %d %d %v\n", c, aq0, aq1, f, n, string(r))
t.w.Eventf("%c%d %d 0 %d %v\n", c, q0, q1, n, string(r))
} else {
t.w.Eventf("%c%d %d %d 0 \n", c, aq0, aq1, f)
}
if q0 != aq0 || q1 != aq1 {
n = q1 - q0
r := make([]rune, n)
t.file.Read(q0, r)
if n <= EVENTSIZE {
t.w.Eventf("%c%d %d 0 %d %v\n", c, q0, q1, n, string(r))
} else {
t.w.Eventf("%c%d %d 0 0 \n", c, q0, q1)
}
t.w.Eventf("%c%d %d 0 0 \n", c, q0, q1)
}
if a != "" {
t.w.Eventf("%c0 0 0 %d %v\n", c, utf8.RuneCountInString(a), a)
if aa != "" {
t.w.Eventf("%c0 0 0 %d %v\n", c, utf8.RuneCountInString(aa), aa)
} else {
t.w.Eventf("%c0 0 0 0 \n", c)
}
}
if a != "" {
t.w.Eventf("%c0 0 0 %d %v\n", c, utf8.RuneCountInString(a), a)
if aa != "" {
t.w.Eventf("%c0 0 0 %d %v\n", c, utf8.RuneCountInString(aa), aa)
} else {
t.w.Eventf("%c0 0 0 0 \n", c)
}
}
}

// execute must run with an existing lock on t's Window
func execute(t *Text, aq0 int, aq1 int, external bool, argt *Text) {
q0, q1 := expandRuneOffsetsToWord(t, aq0, aq1)

r := make([]rune, q1-q0)
t.file.Read(q0, r)
e := lookup(string(r), globalexectab)

// Send commands to external client if the target window's event file is
// in use.
if !external && t.w != nil && t.w.nopen[QWevent] > 0 {
delegateExecution(t, e, aq0, aq1, q0, q1, argt)
return
}

Expand Down
67 changes: 67 additions & 0 deletions exec_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"image"
"io/ioutil"
"os"
"path/filepath"
Expand All @@ -11,6 +12,7 @@ import (

"github.com/google/go-cmp/cmp"
"github.com/rjkroege/edwood/dumpfile"
"github.com/rjkroege/edwood/edwoodtest"
"github.com/rjkroege/edwood/file"
)

Expand Down Expand Up @@ -783,3 +785,68 @@ func TestUndoRedo(t *testing.T) {
})
}
}

func TestDelegateExecution(t *testing.T) {
const hello_世界 = "/Hello, 世界"
runic_hello_世界 := []rune(hello_世界)
const bye_さようなら = "Bye, さようなら"
runic_bye_さようなら := []rune(bye_さようなら)

// build some state...
display := edwoodtest.NewDisplay(image.Rectangle{})
global.configureGlobals(display)
w := NewWindow().initHeadless(nil)
w.display = display
w.body = Text{
display: display,
fr: &MockFrame{},
file: file.MakeObservableEditableBuffer("hello_世界", runic_bye_さようなら),
what: Body,
}
w.tag = Text{
display: display,
fr: &MockFrame{},
file: file.MakeObservableEditableBuffer("", runic_hello_世界),
what: Tag,
}
w.tag.w = w
w.body.w = w

// This is typically set as part of the lock regimen which is why we
// get different letters in Acme.
// TODO(rjk): Test that the Edwood sets the source letters correctly.
w.owner = 'T'

// Pretend that we have an event reader.
w.nopen[QWevent] = 1

w.tag.q0 = 8
w.tag.q1 = 10

for _, tc := range []struct {
e *Exectab
// NB: aq0, aq1 is the original selection.
// NB: q0, q1 is the expanded selection.
aq0, aq1 int
t, argt *Text
want string
q0, q1 int
}{
// Correctness is based on equivalence to Acme.
{nil, 0, 0, &w.body, nil, "TX0 0 2 0 \nTX0 3 0 3 Bye\n", 0, 0},
{nil, 1, 1, &w.body, &w.tag, "TX1 1 10 0 \nTX0 3 0 3 Bye\nTX0 0 0 2 世界\nTX0 0 0 0 \n", 0, 0},
{nil, 1, 1, &w.tag, nil, "Tx1 1 2 0 \nTx0 6 0 6 /Hello\n", 0, 0},
{nil, 1, 1, &w.body, &w.body, "TX1 1 10 0 \nTX0 3 0 3 Bye\nTX0 0 0 5 さようなら\nTX0 0 0 15 hello_世界:#5,#10\n", 5, 10},
} {
w.body.q0, w.body.q1 = tc.q0, tc.q1
q0, q1 := expandRuneOffsetsToWord(tc.t, tc.aq0, tc.aq1)
delegateExecution(tc.t, tc.e, tc.aq0, tc.aq1, q0, q1, tc.argt)
want, got := tc.want, string(w.events)
if diff := cmp.Diff(want, got); diff != "" {
t.Log(q0, q1)
t.Log(got)
t.Errorf("delegateExection mismatch (-want +got):\n%s", diff)
}
w.events = w.events[0:0]
}
}
1 change: 1 addition & 0 deletions guide
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ X:edwood/.*\.go: w

{go build -tags debug}
./testedwood.sh
./testacme.sh

{go test -covermode=count -coverprofile=count.out}
go tool cover -html=count.out
Expand Down
Loading