Skip to content

Commit

Permalink
internal/lsp/diff: new diff implementation to replace go-diff
Browse files Browse the repository at this point in the history
The new implementation is based on Myers' paper, and is in the
package diff/lcs.

There is a new option newDiff, that can be set to 'old', 'new',
or 'both'. The default is 'both', although that may not be
the right choice for a release. See gopls/hooks/diff.go.
'both' runs both the old and new diff algorithm and saves some
statistics in a file in os.Tempdir(),

When (or if) the new code becomes the default, this logging (and
some internal checking) will be removed.

The new implementation has internal checking, which currently
panics. The code in gopls/hooks/diff.go tries to save an encrypted
(for privacy) version of the failing input.

The package diff/myers has not been replaced, but it could be.

Fixes golang/go#52966

Change-Id: Id38d76ed383c4330d9373580561765b5a2412587
Reviewed-on: https://go-review.googlesource.com/c/tools/+/396855
Reviewed-by: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Peter Weinberger <pjw@google.com>
  • Loading branch information
pjweinbgo committed Jul 24, 2022
1 parent a2a2477 commit 3d474c8
Showing 19 changed files with 1,804 additions and 47 deletions.
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
module golang.org/x/tools

go 1.17
go 1.18

require (
github.com/yuin/goldmark v1.4.1
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a
golang.org/x/text v0.3.7
)
22 changes: 2 additions & 20 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,30 +1,12 @@
github.com/yuin/goldmark v1.4.1 h1:/vn0k+RBvwlxEmP5E7SZMqNxPhfMVFEJiykr15/0XKM=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
10 changes: 10 additions & 0 deletions gopls/doc/settings.md
Original file line number Diff line number Diff line change
@@ -452,6 +452,16 @@ Default: `false`.

<!-- END User: DO NOT MANUALLY EDIT THIS SECTION -->

#### **newDiff** *string*

newDiff enables the new diff implementation. If this is "both",
for now both diffs will be run and statistics will be generateted in
a file in $TMPDIR. This is a risky setting; help in trying it
is appreciated. If it is "old" the old implementation is used,
and if it is "new", just the new implementation is used.

Default: 'old'.

## Code Lenses

These are the code lenses that `gopls` currently supports. They can be enabled
4 changes: 2 additions & 2 deletions gopls/go.mod
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ require (
github.com/jba/templatecheck v0.6.0
github.com/sergi/go-diff v1.1.0
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4
golang.org/x/sys v0.0.0-20220209214540-3681064d5158
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a
golang.org/x/tools v0.1.11-0.20220523181440-ccb10502d1a5
golang.org/x/vuln v0.0.0-20220718121659-b9a3ad919698
honnef.co/go/tools v0.3.2
@@ -20,7 +20,7 @@ require (
github.com/BurntSushi/toml v1.0.0 // indirect
github.com/google/safehtml v0.0.2 // indirect
golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/text v0.3.7 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
)
20 changes: 2 additions & 18 deletions gopls/go.sum
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU=
github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/frankban/quicktest v1.14.2 h1:SPb1KFFmM+ybpEjPUhCCkZOM5xlovT5UbrMvWnXyBns=
github.com/frankban/quicktest v1.14.2/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
github.com/google/go-cmdtest v0.4.0/go.mod h1:apVn/GCasLZUVpAJ6oWAuyP7Ne7CEsQbTnc0plM3m+o=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/safehtml v0.0.2 h1:ZOt2VXg4x24bW0m2jtzAOkhoXV0iM8vNKc0paByCZqM=
github.com/google/safehtml v0.0.2/go.mod h1:L4KWwDsUJdECRAEpZoBn3O64bQaywRscowZjJAzjHnU=
github.com/jba/printsrc v0.2.2 h1:9OHK51UT+/iMAEBlQIIXW04qvKyF3/vvLuwW/hL8tDU=
@@ -47,8 +40,6 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e h1:qyrTQ++p1afMkO4DPEeLGq/3oTsdlvdH4vqZUBWzUKM=
golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -61,18 +52,15 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211213223007-03aa0b5f6827/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/vuln v0.0.0-20220503210553-a5481fb0c8be h1:jokAF1mfylAi1iTQx7C44B7vyXUcSEMw8eDv0PzNu8s=
golang.org/x/vuln v0.0.0-20220503210553-a5481fb0c8be/go.mod h1:twca1SxmF6/i2wHY/mj1vLIkkHdp+nil/yA32ZOP4kg=
golang.org/x/vuln v0.0.0-20220613164644-4eb5ba49563c h1:r5bbIROBQtRRgoutV8Q3sFY58VzHW6jMBYl48ANSyS4=
golang.org/x/vuln v0.0.0-20220613164644-4eb5ba49563c/go.mod h1:UZshlUPxXeGUM9I14UOawXQg6yosDE9cr1vKY/DzgWo=
golang.org/x/vuln v0.0.0-20220718121659-b9a3ad919698 h1:9lgpkUgjzoIcZYp7/UPFO/0jIlYcokcEjqWm0hj9pzE=
@@ -88,14 +76,10 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.2.2/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY=
honnef.co/go/tools v0.3.0 h1:2LdYUZ7CIxnYgskbUZfY7FPggmqnh6shBqfWa8Tn3XU=
honnef.co/go/tools v0.3.0/go.mod h1:vlRD9XErLMGT+mDuofSr0mMMquscM/1nQqtRSsh6m70=
honnef.co/go/tools v0.3.2 h1:ytYb4rOqyp1TSa2EPvNVwtPQJctSELKaMyLfqNP4+34=
honnef.co/go/tools v0.3.2/go.mod h1:jzwdWgg7Jdq75wlfblQxO4neNaFFSvgc1tD5Wv8U0Yw=
mvdan.cc/gofumpt v0.3.0 h1:kTojdZo9AcEYbQYhGuLf/zszYthRdhDNDUi2JKTxas4=
mvdan.cc/gofumpt v0.3.0/go.mod h1:0+VyGZWleeIj5oostkOex+nDBA0eyavuDnDusAJ8ylo=
mvdan.cc/unparam v0.0.0-20211214103731-d0ef000c54e5 h1:Jh3LAeMt1eGpxomyu3jVkmVZWW2MxZ1qIIV2TZ/nRio=
mvdan.cc/unparam v0.0.0-20211214103731-d0ef000c54e5/go.mod h1:b8RRCBm0eeiWR8cfN88xeq2G5SG3VKGO+5UPWi5FSOY=
mvdan.cc/xurls/v2 v2.4.0 h1:tzxjVAj+wSBmDcF6zBB7/myTy3gX9xvi8Tyr28AuQgc=
mvdan.cc/xurls/v2 v2.4.0/go.mod h1:+GEjq9uNjqs8LQfM9nVnM8rff0OQ5Iash5rzX+N1CSg=
164 changes: 164 additions & 0 deletions gopls/internal/hooks/diff.go
Original file line number Diff line number Diff line change
@@ -5,13 +5,177 @@
package hooks

import (
"crypto/rand"
"encoding/json"
"fmt"
"io"
"log"
"math/big"
"os"
"path/filepath"
"runtime"
"strings"
"sync"
"time"
"unicode"

"github.com/sergi/go-diff/diffmatchpatch"
"golang.org/x/tools/internal/lsp/diff"
"golang.org/x/tools/internal/span"
)

// structure for saving information about diffs
// while the new code is being rolled out
type diffstat struct {
Before, After int
Oldedits, Newedits int
Oldtime, Newtime time.Duration
Stack string
Msg string `json:",omitempty"` // for errors
Ignored int `json:",omitempty"` // numbr of skipped records with 0 edits
}

var (
mu sync.Mutex // serializes writes and protects ignored
difffd io.Writer
ignored int // lots of the diff calls have 0 diffs
)

var fileonce sync.Once

func (s *diffstat) save() {
// save log records in a file in os.TempDir().
// diff is frequently called with identical strings, so
// these are somewhat compressed out
fileonce.Do(func() {
fname := filepath.Join(os.TempDir(), fmt.Sprintf("gopls-diff-%x", os.Getpid()))
fd, err := os.Create(fname)
if err != nil {
// now what?
}
difffd = fd
})

mu.Lock()
defer mu.Unlock()
if s.Oldedits == 0 && s.Newedits == 0 {
if ignored < 15 {
// keep track of repeated instances of no diffs
// but only print every 15th
ignored++
return
}
s.Ignored = ignored + 1
} else {
s.Ignored = ignored
}
ignored = 0
// it would be really nice to see why diff was called
_, f, l, ok := runtime.Caller(2)
if ok {
var fname string
fname = filepath.Base(f) // diff is only called from a few places
s.Stack = fmt.Sprintf("%s:%d", fname, l)
}
x, err := json.Marshal(s)
if err != nil {
log.Print(err) // failure to print statistics should not stop gopls
}
fmt.Fprintf(difffd, "%s\n", x)
}

// save encrypted versions of the broken input and return the file name
// (the saved strings will have the same diff behavior as the user's strings)
func disaster(before, after string) string {
// encrypt before and after for privacy. (randomized monoalphabetic cipher)
// got will contain the substitution cipher
// for the runes in before and after
got := map[rune]rune{}
for _, r := range before {
got[r] = ' ' // value doesn't matter
}
for _, r := range after {
got[r] = ' '
}
repl := initrepl(len(got))
i := 0
for k := range got { // randomized
got[k] = repl[i]
i++
}
// use got to encrypt before and after
subst := func(r rune) rune { return got[r] }
first := strings.Map(subst, before)
second := strings.Map(subst, after)

// one failure per session is enough, and more private.
// this saves the last one.
fname := fmt.Sprintf("%s/gopls-failed-%x", os.TempDir(), os.Getpid())
fd, err := os.Create(fname)
defer fd.Close()
_, err = fd.Write([]byte(fmt.Sprintf("%s\n%s\n", string(first), string(second))))
if err != nil {
// what do we tell the user?
return ""
}
// ask the user to send us the file, somehow
return fname
}

func initrepl(n int) []rune {
repl := make([]rune, 0, n)
for r := rune(0); len(repl) < n; r++ {
if unicode.IsLetter(r) || unicode.IsNumber(r) {
repl = append(repl, r)
}
}
// randomize repl
rdr := rand.Reader
lim := big.NewInt(int64(len(repl)))
for i := 1; i < n; i++ {
v, _ := rand.Int(rdr, lim)
k := v.Int64()
repl[i], repl[k] = repl[k], repl[i]
}
return repl
}

// BothDiffs edits calls both the new and old diffs, checks that the new diffs
// change before into after, and attempts to preserve some statistics.
func BothDiffs(uri span.URI, before, after string) (edits []diff.TextEdit, err error) {
// The new diff code contains a lot of internal checks that panic when they
// fail. This code catches the panics, or other failures, tries to save
// the failing example (and ut wiykd ask the user to send it back to us, and
// changes options.newDiff to 'old', if only we could figure out how.)
stat := diffstat{Before: len(before), After: len(after)}
now := time.Now()
Oldedits, oerr := ComputeEdits(uri, before, after)
if oerr != nil {
stat.Msg += fmt.Sprintf("old:%v", oerr)
}
stat.Oldedits = len(Oldedits)
stat.Oldtime = time.Since(now)
defer func() {
if r := recover(); r != nil {
disaster(before, after)
edits, err = Oldedits, oerr
}
}()
now = time.Now()
Newedits, rerr := diff.NComputeEdits(uri, before, after)
stat.Newedits = len(Newedits)
stat.Newtime = time.Now().Sub(now)
got := diff.ApplyEdits(before, Newedits)
if got != after {
stat.Msg += "FAIL"
disaster(before, after)
stat.save()
return Oldedits, oerr
}
stat.save()
return Newedits, rerr
}

func ComputeEdits(uri span.URI, before, after string) (edits []diff.TextEdit, err error) {
// The go-diff library has an unresolved panic (see golang/go#278774).
// TODO(rstambler): Remove the recover once the issue has been fixed
47 changes: 44 additions & 3 deletions gopls/internal/hooks/diff_test.go
Original file line number Diff line number Diff line change
@@ -2,15 +2,56 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package hooks_test
package hooks

import (
"fmt"
"io/ioutil"
"os"
"testing"
"unicode/utf8"

"golang.org/x/tools/gopls/internal/hooks"
"golang.org/x/tools/internal/lsp/diff/difftest"
)

func TestDiff(t *testing.T) {
difftest.DiffTest(t, hooks.ComputeEdits)
difftest.DiffTest(t, ComputeEdits)
}

func TestRepl(t *testing.T) {
t.Skip("just for checking repl by looking at it")
repl := initrepl(800)
t.Errorf("%q", string(repl))
t.Errorf("%d", len(repl))
}

func TestDisaster(t *testing.T) {
a := "This is a string,(\u0995) just for basic functionality"
b := "Ths is another string, (\u0996) to see if disaster will store stuff correctly"
fname := disaster(a, b)
buf, err := ioutil.ReadFile(fname)
if err != nil {
t.Errorf("error %v reading %s", err, fname)
}
var x, y string
n, err := fmt.Sscanf(string(buf), "%s\n%s\n", &x, &y)
if n != 2 {
t.Errorf("got %d, expected 2", n)
t.Logf("read %q", string(buf))
}
if a == x || b == y {
t.Error("failed to encrypt")
}
err = os.Remove(fname)
if err != nil {
t.Errorf("%v removing %s", err, fname)
}
alen, blen := utf8.RuneCount([]byte(a)), utf8.RuneCount([]byte(b))
xlen, ylen := utf8.RuneCount([]byte(x)), utf8.RuneCount([]byte(y))
if alen != xlen {
t.Errorf("a; got %d, expected %d", xlen, alen)
}
if blen != ylen {
t.Errorf("b: got %d expected %d", ylen, blen)
}
}
Loading

0 comments on commit 3d474c8

Please sign in to comment.