mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-10-03 04:26:50 -04:00
Compare commits
17
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbe2379fa5 | ||
|
|
f77c1d37f1 | ||
|
|
27fcf4b729 | ||
|
|
93b8f343bb | ||
|
|
c18a0c6680 | ||
|
|
c8bc1928f2 | ||
|
|
4b22b844e7 | ||
|
|
ec577f1afa | ||
|
|
70427c8ff5 | ||
|
|
2f06724b80 | ||
|
|
f9b790a1f9 | ||
|
|
ddceff6962 | ||
|
|
f416a4ba6a | ||
|
|
2399cac0db | ||
|
|
d6cf948dca | ||
|
|
4b257646ad | ||
|
|
d3a2c87293 |
@@ -8,7 +8,7 @@ jobs:
|
||||
check-required-label:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: mheap/github-action-required-labels@0ac283b4e65c1fb28ce6079dea5546ceca98ccbe # v5
|
||||
- uses: mheap/github-action-required-labels@23e10fde7e062233401931a0eece796cd9bf3177 # v5
|
||||
with:
|
||||
mode: exactly
|
||||
count: 1
|
||||
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v6
|
||||
uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version: 1.25.x
|
||||
- name: Test code
|
||||
@@ -93,7 +93,7 @@ jobs:
|
||||
path: ~/git-${{matrix.git-version}}
|
||||
key: ${{runner.os}}-git-${{matrix.git-version}}
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v6
|
||||
uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version: 1.25.x
|
||||
- name: Print git version
|
||||
@@ -130,7 +130,7 @@ jobs:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v6
|
||||
uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version: 1.25.x
|
||||
- name: Build linux binary
|
||||
@@ -157,7 +157,7 @@ jobs:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v6
|
||||
uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version: 1.25.x
|
||||
- name: Check Vendor Directory
|
||||
@@ -183,7 +183,7 @@ jobs:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v6
|
||||
uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version: 1.25.x
|
||||
- name: Check formatting
|
||||
@@ -206,7 +206,7 @@ jobs:
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v6
|
||||
uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version: 1.25.x
|
||||
|
||||
|
||||
@@ -160,7 +160,7 @@ jobs:
|
||||
git push origin "refs/tags/$NEW_TAG"
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v6
|
||||
uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version: 1.25.x
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Generate Sponsors 💖
|
||||
uses: JamesIves/github-sponsors-readme-action@2fd9142e765f755780202122261dc85e78459405 # v1.6.0
|
||||
uses: JamesIves/github-sponsors-readme-action@02650b8cd445fc16dfef73195f9c406dce041623 # v1.6.1
|
||||
with:
|
||||
token: ${{ secrets.SPONSORS_TOKEN }}
|
||||
file: "README.md"
|
||||
|
||||
@@ -21,8 +21,8 @@ require (
|
||||
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
||||
github.com/karimkhaleel/jsonschema v0.0.0-20231001195015-d933f0d94ea3
|
||||
github.com/kyokomi/emoji/v2 v2.2.13
|
||||
github.com/lucasb-eyer/go-colorful v1.4.0
|
||||
github.com/kyokomi/emoji/v2 v2.2.14
|
||||
github.com/lucasb-eyer/go-colorful v1.4.1
|
||||
github.com/mgutz/str v1.2.0
|
||||
github.com/mitchellh/go-ps v1.0.0
|
||||
github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe
|
||||
|
||||
@@ -73,10 +73,10 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/kyokomi/emoji/v2 v2.2.13 h1:GhTfQa67venUUvmleTNFnb+bi7S3aocF7ZCXU9fSO7U=
|
||||
github.com/kyokomi/emoji/v2 v2.2.13/go.mod h1:JUcn42DTdsXJo1SWanHh4HKDEyPaR5CqkmoirZZP9qE=
|
||||
github.com/lucasb-eyer/go-colorful v1.4.0 h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW4TvVgFr4=
|
||||
github.com/lucasb-eyer/go-colorful v1.4.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/kyokomi/emoji/v2 v2.2.14 h1:YOF6VL52613M0Qr9v4puJDD9QQPmyyjXedDDlrGzH80=
|
||||
github.com/kyokomi/emoji/v2 v2.2.14/go.mod h1:1AnYl9IgmJZXKd5m1PEijyyUw85SqYsuAr8lpU/s+9s=
|
||||
github.com/lucasb-eyer/go-colorful v1.4.1 h1:1EO+WB73+EH8EVbzlrG3KLAfEypQWVHIBqlTf+2hNss=
|
||||
github.com/lucasb-eyer/go-colorful v1.4.1/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -160,9 +162,51 @@ func fetchPullRequestsQuery(branches []string, owner string, repo string) (strin
|
||||
return queryString, variables
|
||||
}
|
||||
|
||||
// GetAuthToken returns the token to authenticate against the given host with,
|
||||
// or an empty string if there is none.
|
||||
//
|
||||
// The token has to come from gh itself rather than from an in-process lookup
|
||||
// with go-gh: that reads gh's config file once per process and answers from
|
||||
// that snapshot ever after, whereas gh rewrites the file whenever the active
|
||||
// account changes, and keeps the active account's token either there or in the
|
||||
// system keyring. Under a long-running lazygit the snapshot therefore drifts
|
||||
// out of date, leaving us with a token for an account that is no longer active,
|
||||
// or with no token at all.
|
||||
func (self *GitHubCommands) GetAuthToken(host string) string {
|
||||
token, _ := auth.TokenForHost(host)
|
||||
return token
|
||||
ghExe := ghExecutable()
|
||||
if ghExe == "" {
|
||||
// Without gh installed, the environment variables and config file that
|
||||
// gh would have consulted are still worth a look.
|
||||
token, _ := auth.TokenFromEnvOrConfig(host)
|
||||
return token
|
||||
}
|
||||
|
||||
cmdArgs := []string{ghExe, "auth", "token", "--hostname", host}
|
||||
output, _, err := self.cmd.New(cmdArgs).DontLog().RunWithOutputs()
|
||||
if err != nil {
|
||||
// Not being logged in to this host is a normal state rather than
|
||||
// something to report; the runner logs gh's stderr for the rest.
|
||||
return ""
|
||||
}
|
||||
|
||||
return strings.TrimSpace(output)
|
||||
}
|
||||
|
||||
// ghExecutable returns the path of the gh binary, or an empty string if it
|
||||
// isn't installed.
|
||||
func ghExecutable() string {
|
||||
if ghExe := os.Getenv("GH_PATH"); ghExe != "" {
|
||||
return ghExe
|
||||
}
|
||||
|
||||
// A gh found in the current directory rather than on PATH comes back as
|
||||
// exec.ErrDot, which we treat as not having found one at all.
|
||||
ghExe, err := exec.LookPath("gh")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return ghExe
|
||||
}
|
||||
|
||||
// FetchRecentPRs fetches recent pull requests using GraphQL. serviceInfo
|
||||
|
||||
+27
-8
@@ -38,6 +38,11 @@ var (
|
||||
|
||||
// ErrKeybindingNotHandled is returned when a keybinding is not handled, so that the key can be dispatched further
|
||||
ErrKeybindingNotHandled = standardErrors.New("keybinding not handled")
|
||||
|
||||
// ErrLoopExited is returned by OnUIThreadAndWait when MainLoop has already
|
||||
// returned. Nothing dequeues user events after that, so the callback it was
|
||||
// asked to run on the main goroutine never will be.
|
||||
ErrLoopExited = standardErrors.New("main loop exited")
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -893,36 +898,50 @@ func (g *Gui) EndBlockingEvents() error {
|
||||
}
|
||||
|
||||
// OnUIThreadAndWait runs f on the main event-loop goroutine and blocks the
|
||||
// caller until f has run, returning f's error. Use it to read UI-thread-owned
|
||||
// state (the model, contexts) from a worker without racing the UI thread.
|
||||
// caller until f has run. Use it to read UI-thread-owned state (the model,
|
||||
// contexts) from a worker without racing the UI thread.
|
||||
//
|
||||
// The error it returns is the wait's own, never f's: it reports that f was not
|
||||
// run at all, which happens when the main loop has exited (ErrLoopExited). f
|
||||
// doesn't report an error because what callers want on the UI thread — reading
|
||||
// and mutating state — doesn't fail.
|
||||
//
|
||||
// It must be called from a worker goroutine, never from the UI thread itself:
|
||||
// the UI thread would block waiting for a callback only it can run, which
|
||||
// deadlocks. Callers arrange this by construction (see the refresh helper's
|
||||
// RefreshFromWorker); a debug-only assertion there guards against getting it
|
||||
// wrong.
|
||||
func (g *Gui) OnUIThreadAndWait(f func() error) error {
|
||||
func (g *Gui) OnUIThreadAndWait(f func()) error {
|
||||
return g.onUIThreadAndWait(f, false)
|
||||
}
|
||||
|
||||
// Like OnUIThreadAndWait, but the enqueued work belongs to a background routine,
|
||||
// so it doesn't count towards the program being busy (see UpdateBackground).
|
||||
func (g *Gui) OnUIThreadAndWaitBackground(f func() error) error {
|
||||
func (g *Gui) OnUIThreadAndWaitBackground(f func()) error {
|
||||
return g.onUIThreadAndWait(f, true)
|
||||
}
|
||||
|
||||
func (g *Gui) onUIThreadAndWait(f func() error, background bool) error {
|
||||
func (g *Gui) onUIThreadAndWait(f func(), background bool) error {
|
||||
enqueue := g.Update
|
||||
if background {
|
||||
enqueue = g.UpdateBackground
|
||||
}
|
||||
|
||||
result := make(chan error, 1)
|
||||
ran := make(chan struct{})
|
||||
enqueue(func(*Gui) error {
|
||||
result <- f()
|
||||
f()
|
||||
close(ran)
|
||||
return nil
|
||||
})
|
||||
return <-result
|
||||
|
||||
select {
|
||||
case <-ran:
|
||||
return nil
|
||||
case <-g.loopExited:
|
||||
// The queue we just enqueued onto is no longer being served, so waiting
|
||||
// on `ran` here would mean waiting for the rest of the process's life.
|
||||
return ErrLoopExited
|
||||
}
|
||||
}
|
||||
|
||||
// Calls a function in a goroutine. Handles panics gracefully and tracks
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package gocui
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// errStillWaiting stands in for the result of a wait that hasn't produced one.
|
||||
var errStillWaiting = errors.New("still waiting")
|
||||
|
||||
// resultOrTimeout reports what a wait returned, or errStillWaiting if it hasn't
|
||||
// returned by the time we give up on it.
|
||||
func resultOrTimeout(result chan error) error {
|
||||
select {
|
||||
case err := <-result:
|
||||
return err
|
||||
case <-time.After(time.Second):
|
||||
return errStillWaiting
|
||||
}
|
||||
}
|
||||
|
||||
// A worker waiting for the UI thread must not be left parked there once the
|
||||
// main loop has stopped: nothing will ever run its callback, and the shutdown
|
||||
// that follows blocks until such workers have finished (see
|
||||
// tasks.ViewBufferManager.Close).
|
||||
func TestOnUIThreadAndWaitGivesUpWhenTheLoopExits(t *testing.T) {
|
||||
g := newTestGui(t)
|
||||
|
||||
// Closing this is what MainLoop returning does. From here on nothing
|
||||
// dequeues user events, so the callback below is never going to run.
|
||||
close(g.loopExited)
|
||||
|
||||
result := make(chan error, 1)
|
||||
go func() {
|
||||
result <- g.OnUIThreadAndWait(func() {})
|
||||
}()
|
||||
|
||||
err := resultOrTimeout(result)
|
||||
assert.ErrorIs(t, err, ErrLoopExited)
|
||||
}
|
||||
@@ -119,13 +119,12 @@ func (self *BackgroundRoutineMgr) startBackgroundFetch() {
|
||||
var appStatusHelper *helpers.AppStatusHelper
|
||||
var branchesHelper *helpers.BranchesHelper
|
||||
var fetchGeneration int
|
||||
if err := self.gui.g.OnUIThreadAndWaitBackground(func() error {
|
||||
if err := self.gui.g.OnUIThreadAndWaitBackground(func() {
|
||||
git = self.gui.git
|
||||
appStatusHelper = self.gui.helpers.AppStatus
|
||||
branchesHelper = self.gui.helpers.BranchesHelper
|
||||
fetchGeneration = self.gui.c.State().GetRepoGeneration()
|
||||
self.gui.State.LastBackgroundFetchTime = time.Now()
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -184,10 +183,9 @@ func (self *BackgroundRoutineMgr) checkForExternalChanges() {
|
||||
// reading them from this background goroutine would race the reassignment.
|
||||
var git *commands.GitCommand
|
||||
var refreshHelper *helpers.RefreshHelper
|
||||
if err := self.gui.g.OnUIThreadAndWaitBackground(func() error {
|
||||
if err := self.gui.g.OnUIThreadAndWaitBackground(func() {
|
||||
git = self.gui.git
|
||||
refreshHelper = self.gui.helpers.Refresh
|
||||
return nil
|
||||
}); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -188,9 +188,8 @@ func (self *MergeAndRebaseHelper) hasExecTodos(calledFromWorker bool) bool {
|
||||
}
|
||||
|
||||
result := false
|
||||
_ = self.c.GocuiGui().OnUIThreadAndWait(func() error {
|
||||
_ = self.c.GocuiGui().OnUIThreadAndWait(func() {
|
||||
result = check()
|
||||
return nil
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -223,10 +223,12 @@ func (self *RefreshHelper) performRefresh(options types.RefreshOptions, calledFr
|
||||
background: options.Background || options.DontBlockRepoSwitch,
|
||||
backgroundRoutine: options.Background,
|
||||
}
|
||||
self.captureOnUIThread(calledFromWorker, env.background, func() {
|
||||
if !self.captureOnUIThread(calledFromWorker, env.background, func() {
|
||||
env.generation = self.c.State().GetRepoGeneration()
|
||||
env.git = self.c.Git()
|
||||
})
|
||||
}) {
|
||||
return
|
||||
}
|
||||
if options.BatchUIUpdates {
|
||||
env.batch = &refreshBounceBatch{}
|
||||
}
|
||||
@@ -321,11 +323,13 @@ func (self *RefreshHelper) performRefresh(options types.RefreshOptions, calledFr
|
||||
var capturedCommits capturedCommitState
|
||||
var capturedReflog capturedReflogState
|
||||
var capturedBranches capturedBranchState
|
||||
self.captureOnUIThread(calledFromWorker, env.background, func() {
|
||||
if !self.captureOnUIThread(calledFromWorker, env.background, func() {
|
||||
capturedCommits = self.captureCommitsState()
|
||||
capturedReflog = self.captureReflogState()
|
||||
capturedBranches = self.captureBranchState()
|
||||
})
|
||||
}) {
|
||||
return
|
||||
}
|
||||
refresh("commits and commit files", func() {
|
||||
self.refreshCommitsAndCommitFiles(capturedCommits, options.CommitSelection, env)
|
||||
})
|
||||
@@ -355,35 +359,43 @@ func (self *RefreshHelper) performRefresh(options types.RefreshOptions, calledFr
|
||||
// if we've asked specifically for rebase commits and not those other things
|
||||
var rebaseHashPool *utils.StringPool
|
||||
var rebaseCommits []*models.Commit
|
||||
self.captureOnUIThread(calledFromWorker, env.background, func() {
|
||||
if !self.captureOnUIThread(calledFromWorker, env.background, func() {
|
||||
rebaseHashPool, rebaseCommits = self.captureRebaseCommitState()
|
||||
})
|
||||
}) {
|
||||
return
|
||||
}
|
||||
refresh("rebase commits", func() { _ = self.refreshRebaseCommits(rebaseHashPool, rebaseCommits, env) })
|
||||
}
|
||||
|
||||
if scopeSet.Includes(types.SUB_COMMITS) {
|
||||
var capturedSubCommits capturedSubCommitState
|
||||
self.captureOnUIThread(calledFromWorker, env.background, func() {
|
||||
if !self.captureOnUIThread(calledFromWorker, env.background, func() {
|
||||
capturedSubCommits = self.captureSubCommitState()
|
||||
})
|
||||
}) {
|
||||
return
|
||||
}
|
||||
refresh("sub commits", func() { _ = self.refreshSubCommitsWithLimit(capturedSubCommits, env) })
|
||||
}
|
||||
|
||||
// reason we're not doing this if the COMMITS type is included is that if the COMMITS type _is_ included we will refresh the commit files context anyway
|
||||
if scopeSet.Includes(types.COMMIT_FILES) && !scopeSet.Includes(types.COMMITS) {
|
||||
var capturedCommitFiles capturedCommitFilesState
|
||||
self.captureOnUIThread(calledFromWorker, env.background, func() {
|
||||
if !self.captureOnUIThread(calledFromWorker, env.background, func() {
|
||||
capturedCommitFiles = self.captureCommitFilesState()
|
||||
})
|
||||
}) {
|
||||
return
|
||||
}
|
||||
refresh("commit files", func() { _ = self.refreshCommitFilesContext(capturedCommitFiles, env) })
|
||||
}
|
||||
|
||||
fileWg := sync.WaitGroup{}
|
||||
if scopeSet.Includes(types.FILES) {
|
||||
var capturedFiles capturedFilesState
|
||||
self.captureOnUIThread(calledFromWorker, env.background, func() {
|
||||
if !self.captureOnUIThread(calledFromWorker, env.background, func() {
|
||||
capturedFiles = self.captureFilesState()
|
||||
})
|
||||
}) {
|
||||
return
|
||||
}
|
||||
fileWg.Add(1)
|
||||
refresh("files", func() {
|
||||
_ = self.refreshFilesAndSubmodules(capturedFiles, env)
|
||||
@@ -393,9 +405,11 @@ func (self *RefreshHelper) performRefresh(options types.RefreshOptions, calledFr
|
||||
|
||||
if scopeSet.Includes(types.STASH) {
|
||||
var stashFilterPath string
|
||||
self.captureOnUIThread(calledFromWorker, env.background, func() {
|
||||
if !self.captureOnUIThread(calledFromWorker, env.background, func() {
|
||||
stashFilterPath = self.c.Modes().Filtering.GetPath()
|
||||
})
|
||||
}) {
|
||||
return
|
||||
}
|
||||
refresh("stash", func() { self.refreshStashEntries(stashFilterPath, env) })
|
||||
}
|
||||
|
||||
@@ -408,9 +422,11 @@ func (self *RefreshHelper) performRefresh(options types.RefreshOptions, calledFr
|
||||
// needs it to keep the remote-branches selection valid, and reading
|
||||
// the Remotes context off the UI thread races its render.
|
||||
var prevSelectedRemote *models.Remote
|
||||
self.captureOnUIThread(calledFromWorker, env.background, func() {
|
||||
if !self.captureOnUIThread(calledFromWorker, env.background, func() {
|
||||
prevSelectedRemote = self.c.Contexts().Remotes.GetSelected()
|
||||
})
|
||||
}) {
|
||||
return
|
||||
}
|
||||
branchesAndRemotesWg.Add(1)
|
||||
refresh("remotes", func() {
|
||||
loadedRemotes, _ = self.refreshRemotes(prevSelectedRemote, env)
|
||||
@@ -1248,21 +1264,20 @@ func (self *RefreshHelper) onUIThread(background bool, f func() error) {
|
||||
// waiting for a callback that only it can run), and capturing inline also
|
||||
// guarantees the snapshot reflects the state at the moment Refresh was called,
|
||||
// before the calling handler regains control and can mutate it.
|
||||
func (self *RefreshHelper) captureOnUIThread(calledFromWorker bool, background bool, fn func()) {
|
||||
//
|
||||
// It returns false when fn didn't run because the app is shutting down, in
|
||||
// which case the caller must abandon the refresh rather than compute from a
|
||||
// snapshot that was never taken.
|
||||
func (self *RefreshHelper) captureOnUIThread(calledFromWorker bool, background bool, fn func()) bool {
|
||||
if !calledFromWorker {
|
||||
fn()
|
||||
return
|
||||
return true
|
||||
}
|
||||
|
||||
wrapped := func() error {
|
||||
fn()
|
||||
return nil
|
||||
}
|
||||
if background {
|
||||
_ = self.c.GocuiGui().OnUIThreadAndWaitBackground(wrapped)
|
||||
} else {
|
||||
_ = self.c.GocuiGui().OnUIThreadAndWait(wrapped)
|
||||
return self.c.GocuiGui().OnUIThreadAndWaitBackground(fn) == nil
|
||||
}
|
||||
return self.c.GocuiGui().OnUIThreadAndWait(fn) == nil
|
||||
}
|
||||
|
||||
// capturedFilesState holds the files refresh's context/model inputs, gathered
|
||||
|
||||
@@ -230,9 +230,8 @@ func (self *PatchBuildingController) discardSelectionFromCommit() error {
|
||||
err := self.c.Git().Patch.DeletePatchesFromCommit(commits, commitIndex)
|
||||
// Escape pops the patch-building context, so run it on the UI thread
|
||||
// before the refresh below.
|
||||
_ = self.c.GocuiGui().OnUIThreadAndWait(func() error {
|
||||
_ = self.c.GocuiGui().OnUIThreadAndWait(func() {
|
||||
self.c.Helpers().PatchBuilding.Escape()
|
||||
return nil
|
||||
})
|
||||
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebaseWithRefreshOptions(
|
||||
err, types.RefreshOptions{})
|
||||
|
||||
@@ -82,7 +82,7 @@ func (self *GuiDriver) WaitUntilIdle() {
|
||||
}
|
||||
|
||||
func (self *GuiDriver) OnUIThreadAndWait(f func()) {
|
||||
_ = self.gui.g.OnUIThreadAndWait(func() error { f(); return nil })
|
||||
_ = self.gui.g.OnUIThreadAndWait(f)
|
||||
}
|
||||
|
||||
func (self *GuiDriver) replayMouseEvent(x, y int, buttons tcell.ButtonMask) {
|
||||
|
||||
@@ -80,9 +80,8 @@ func (gui *Gui) newStringTaskWithoutScroll(view *gocui.View, str string) error {
|
||||
manager := gui.getManager(view)
|
||||
|
||||
f := func(tasks.TaskOpts) error {
|
||||
return gui.g.OnUIThreadAndWaitBackground(func() error {
|
||||
return gui.g.OnUIThreadAndWaitBackground(func() {
|
||||
gui.c.SetViewContent(view, str)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -97,10 +96,9 @@ func (gui *Gui) newStringTaskWithScroll(view *gocui.View, str string, originX in
|
||||
manager := gui.getManager(view)
|
||||
|
||||
f := func(tasks.TaskOpts) error {
|
||||
return gui.g.OnUIThreadAndWaitBackground(func() error {
|
||||
return gui.g.OnUIThreadAndWaitBackground(func() {
|
||||
gui.c.SetViewContent(view, str)
|
||||
view.SetOrigin(originX, originY)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -115,10 +113,9 @@ func (gui *Gui) newStringTaskWithKey(view *gocui.View, str string, key string) e
|
||||
manager := gui.getManager(view)
|
||||
|
||||
f := func(tasks.TaskOpts) error {
|
||||
return gui.g.OnUIThreadAndWaitBackground(func() error {
|
||||
return gui.g.OnUIThreadAndWaitBackground(func() {
|
||||
gui.c.ResetViewOrigin(view)
|
||||
gui.c.SetViewContent(view, str)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+4
-10
@@ -87,7 +87,7 @@ type ViewBufferManager struct {
|
||||
// of the view happen through this, so that the view is only ever touched on
|
||||
// the UI thread (where it is also laid out and drawn), never on the task's
|
||||
// own goroutine.
|
||||
onUIThread func(f func() error) error
|
||||
onUIThread func(f func()) error
|
||||
|
||||
// if the user flicks through a heap of items, with each one
|
||||
// spawning a process to render something to the main view,
|
||||
@@ -126,7 +126,7 @@ func NewViewBufferManager(
|
||||
onEndOfInput func(),
|
||||
onNewKey func(),
|
||||
newGocuiTask func() gocui.Task,
|
||||
onUIThread func(f func() error) error,
|
||||
onUIThread func(f func()) error,
|
||||
) *ViewBufferManager {
|
||||
return &ViewBufferManager{
|
||||
Log: log,
|
||||
@@ -358,10 +358,7 @@ func (self *ViewBufferManager) NewCmdTask(start func() (Cmd, io.Reader), prefix
|
||||
// onEndOfInput reads the view's dimensions (to decide
|
||||
// whether to scroll) and sets the origin, both of which
|
||||
// are UI-thread-only, so run it there.
|
||||
_ = self.onUIThread(func() error {
|
||||
self.onEndOfInput()
|
||||
return nil
|
||||
})
|
||||
_ = self.onUIThread(self.onEndOfInput)
|
||||
callThen()
|
||||
break outer
|
||||
}
|
||||
@@ -502,10 +499,7 @@ func (self *ViewBufferManager) NewTask(f func(TaskOpts) error, key string) error
|
||||
// must happen after releasing taskIDMutex: it blocks until the UI
|
||||
// thread runs it, and a NewTask call on the UI thread takes
|
||||
// taskIDMutex, so holding it here would deadlock.
|
||||
_ = self.onUIThread(func() error {
|
||||
self.onNewKey()
|
||||
return nil
|
||||
})
|
||||
_ = self.onUIThread(self.onNewKey)
|
||||
}
|
||||
|
||||
self.waitingMutex.Lock()
|
||||
|
||||
@@ -40,7 +40,7 @@ func TestNewCmdTaskInstantStop(t *testing.T) {
|
||||
onNewKey,
|
||||
newTask,
|
||||
// no UI thread in the test; run the view mutations inline
|
||||
func(f func() error) error { return f() },
|
||||
func(f func()) error { f(); return nil },
|
||||
)
|
||||
|
||||
stop := make(chan struct{})
|
||||
@@ -107,7 +107,7 @@ func TestNewCmdTask(t *testing.T) {
|
||||
onNewKey,
|
||||
newTask,
|
||||
// no UI thread in the test; run the view mutations inline
|
||||
func(f func() error) error { return f() },
|
||||
func(f func()) error { f(); return nil },
|
||||
)
|
||||
|
||||
stop := make(chan struct{})
|
||||
@@ -242,7 +242,7 @@ func TestNewCmdTaskRefresh(t *testing.T) {
|
||||
func() {},
|
||||
newTask,
|
||||
// no UI thread in the test; run the view mutations inline
|
||||
func(f func() error) error { return f() },
|
||||
func(f func()) error { f(); return nil },
|
||||
)
|
||||
|
||||
stop := make(chan struct{})
|
||||
|
||||
+97
-63
@@ -62,6 +62,7 @@ func emojiCode() map[string]string {
|
||||
":Leo:": "\u264c",
|
||||
":Libra:": "\u264e",
|
||||
":Mrs._Claus:": "\U0001f936",
|
||||
":Mx_Claus:": "\U0001f9d1\u200d\U0001f384",
|
||||
":NEW_button:": "\U0001f195",
|
||||
":NG_button:": "\U0001f196",
|
||||
":OK_button:": "\U0001f197",
|
||||
@@ -220,6 +221,7 @@ func emojiCode() map[string]string {
|
||||
":bald_man:": "\U0001f468\u200d\U0001f9b2",
|
||||
":bald_person:": "\U0001f9d1\u200d\U0001f9b2",
|
||||
":bald_woman:": "\U0001f469\u200d\U0001f9b2",
|
||||
":ballet_dancer:": "\U0001f9d1\u200d\U0001fa70",
|
||||
":ballet_shoes:": "\U0001fa70",
|
||||
":balloon:": "\U0001f388",
|
||||
":ballot_box:": "\U0001f5f3",
|
||||
@@ -764,6 +766,7 @@ func emojiCode() map[string]string {
|
||||
":disappointed_face:": "\U0001f61e",
|
||||
":disappointed_relieved:": "\U0001f625",
|
||||
":disguised_face:": "\U0001f978",
|
||||
":distorted_face:": "\U0001faea",
|
||||
":divide:": "\u2797",
|
||||
":dividers:": "\U0001f5c2",
|
||||
":diving_mask:": "\U0001f93f",
|
||||
@@ -890,6 +893,7 @@ func emojiCode() map[string]string {
|
||||
":face_savoring_food:": "\U0001f60b",
|
||||
":face_screaming_in_fear:": "\U0001f631",
|
||||
":face_vomiting:": "\U0001f92e",
|
||||
":face_with_bags_under_eyes:": "\U0001fae9",
|
||||
":face_with_cowboy_hat:": "\U0001f920",
|
||||
":face_with_crossed-out_eyes:": "\U0001f635",
|
||||
":face_with_diagonal_mouth:": "\U0001fae4",
|
||||
@@ -1018,12 +1022,14 @@ func emojiCode() map[string]string {
|
||||
":ferry:": "\u26f4\ufe0f",
|
||||
":field_hockey:": "\U0001f3d1",
|
||||
":field_hockey_stick_and_ball:": "\U0001f3d1",
|
||||
":fight_cloud:": "\U0001faef",
|
||||
":fiji:": "\U0001f1eb\U0001f1ef",
|
||||
":file_cabinet:": "\U0001f5c4\ufe0f",
|
||||
":file_folder:": "\U0001f4c1",
|
||||
":film_frames:": "\U0001f39e\ufe0f",
|
||||
":film_projector:": "\U0001f4fd\ufe0f",
|
||||
":film_strip:": "\U0001f39e\ufe0f",
|
||||
":fingerprint:": "\U0001fac6",
|
||||
":fingers_crossed:": "\U0001f91e",
|
||||
":fingers_crossed_tone1:": "\U0001f91e\U0001f3fb",
|
||||
":fingers_crossed_tone2:": "\U0001f91e\U0001f3fc",
|
||||
@@ -1251,6 +1257,7 @@ func emojiCode() map[string]string {
|
||||
":flag-rs:": "\U0001f1f7\U0001f1f8",
|
||||
":flag-rw:": "\U0001f1f7\U0001f1fc",
|
||||
":flag-sa:": "\U0001f1f8\U0001f1e6",
|
||||
":flag-sark:": "\U0001f1e8\U0001f1f6",
|
||||
":flag-sb:": "\U0001f1f8\U0001f1e7",
|
||||
":flag-sc:": "\U0001f1f8\U0001f1e8",
|
||||
":flag-scotland:": "\U0001f3f4\U000e0067\U000e0062\U000e0073\U000e0063\U000e0074\U000e007f",
|
||||
@@ -1504,6 +1511,7 @@ func emojiCode() map[string]string {
|
||||
":flag_Réunion:": "\U0001f1f7\U0001f1ea",
|
||||
":flag_Samoa:": "\U0001f1fc\U0001f1f8",
|
||||
":flag_San_Marino:": "\U0001f1f8\U0001f1f2",
|
||||
":flag_Sark:": "\U0001f1e8\U0001f1f6",
|
||||
":flag_Saudi_Arabia:": "\U0001f1f8\U0001f1e6",
|
||||
":flag_Scotland:": "\U0001f3f4\U000e0067\U000e0062\U000e0073\U000e0063\U000e0074\U000e007f",
|
||||
":flag_Senegal:": "\U0001f1f8\U0001f1f3",
|
||||
@@ -1995,6 +2003,7 @@ func emojiCode() map[string]string {
|
||||
":haircut:": "\U0001f487\u200d\u2640\ufe0f",
|
||||
":haircut_man:": "\U0001f487\u200d\u2642\ufe0f",
|
||||
":haircut_woman:": "\U0001f487\u200d\u2640\ufe0f",
|
||||
":hairy_creature:": "\U0001fac8",
|
||||
":haiti:": "\U0001f1ed\U0001f1f9",
|
||||
":hamburger:": "\U0001f354",
|
||||
":hammer:": "\U0001f528",
|
||||
@@ -2012,43 +2021,44 @@ func emojiCode() map[string]string {
|
||||
":hand_splayed_tone5:": "\U0001f590\U0001f3ff",
|
||||
":hand_with_fingers_splayed:": "\U0001f590",
|
||||
":hand_with_index_finger_and_thumb_crossed:": "\U0001faf0",
|
||||
":handbag:": "\U0001f45c",
|
||||
":handball:": "\U0001f93e",
|
||||
":handball_person:": "\U0001f93e",
|
||||
":handshake:": "\U0001f91d",
|
||||
":hankey:": "\U0001f4a9",
|
||||
":hash:": "#\ufe0f\u20e3",
|
||||
":hatched_chick:": "\U0001f425",
|
||||
":hatching_chick:": "\U0001f423",
|
||||
":head_bandage:": "\U0001f915",
|
||||
":head_shaking_horizontally:": "\U0001f642\u200d\u2194\ufe0f",
|
||||
":head_shaking_vertically:": "\U0001f642\u200d\u2195\ufe0f",
|
||||
":headphone:": "\U0001f3a7",
|
||||
":headphones:": "\U0001f3a7",
|
||||
":headstone:": "\U0001faa6",
|
||||
":health_worker:": "\U0001f9d1\u200d\u2695\ufe0f",
|
||||
":hear-no-evil_monkey:": "\U0001f649",
|
||||
":hear_no_evil:": "\U0001f649",
|
||||
":heard_mcdonald_islands:": "\U0001f1ed\U0001f1f2",
|
||||
":heart:": "\u2764\ufe0f",
|
||||
":heart_decoration:": "\U0001f49f",
|
||||
":heart_exclamation:": "\u2763",
|
||||
":heart_eyes:": "\U0001f60d",
|
||||
":heart_eyes_cat:": "\U0001f63b",
|
||||
":heart_hands:": "\U0001faf6",
|
||||
":heart_on_fire:": "\u2764\ufe0f\u200d\U0001f525",
|
||||
":heart_suit:": "\u2665",
|
||||
":heart_with_arrow:": "\U0001f498",
|
||||
":heart_with_ribbon:": "\U0001f49d",
|
||||
":heartbeat:": "\U0001f493",
|
||||
":heartpulse:": "\U0001f497",
|
||||
":hearts:": "\u2665\ufe0f",
|
||||
":heavy_check_mark:": "\u2714\ufe0f",
|
||||
":heavy_division_sign:": "\u2797",
|
||||
":heavy_dollar_sign:": "\U0001f4b2",
|
||||
":heavy_equals_sign:": "\U0001f7f0",
|
||||
":heavy_exclamation_mark:": "\u2757",
|
||||
":heavy_heart_exclamation:": "\u2763\ufe0f",
|
||||
":handbag:": "\U0001f45c",
|
||||
":handball:": "\U0001f93e",
|
||||
":handball_person:": "\U0001f93e",
|
||||
":handshake:": "\U0001f91d",
|
||||
":hankey:": "\U0001f4a9",
|
||||
":harp:": "\U0001fa89",
|
||||
":hash:": "#\ufe0f\u20e3",
|
||||
":hatched_chick:": "\U0001f425",
|
||||
":hatching_chick:": "\U0001f423",
|
||||
":head_bandage:": "\U0001f915",
|
||||
":head_shaking_horizontally:": "\U0001f642\u200d\u2194\ufe0f",
|
||||
":head_shaking_vertically:": "\U0001f642\u200d\u2195\ufe0f",
|
||||
":headphone:": "\U0001f3a7",
|
||||
":headphones:": "\U0001f3a7",
|
||||
":headstone:": "\U0001faa6",
|
||||
":health_worker:": "\U0001f9d1\u200d\u2695\ufe0f",
|
||||
":hear-no-evil_monkey:": "\U0001f649",
|
||||
":hear_no_evil:": "\U0001f649",
|
||||
":heard_mcdonald_islands:": "\U0001f1ed\U0001f1f2",
|
||||
":heart:": "\u2764\ufe0f",
|
||||
":heart_decoration:": "\U0001f49f",
|
||||
":heart_exclamation:": "\u2763",
|
||||
":heart_eyes:": "\U0001f60d",
|
||||
":heart_eyes_cat:": "\U0001f63b",
|
||||
":heart_hands:": "\U0001faf6",
|
||||
":heart_on_fire:": "\u2764\ufe0f\u200d\U0001f525",
|
||||
":heart_suit:": "\u2665",
|
||||
":heart_with_arrow:": "\U0001f498",
|
||||
":heart_with_ribbon:": "\U0001f49d",
|
||||
":heartbeat:": "\U0001f493",
|
||||
":heartpulse:": "\U0001f497",
|
||||
":hearts:": "\u2665\ufe0f",
|
||||
":heavy_check_mark:": "\u2714\ufe0f",
|
||||
":heavy_division_sign:": "\u2797",
|
||||
":heavy_dollar_sign:": "\U0001f4b2",
|
||||
":heavy_equals_sign:": "\U0001f7f0",
|
||||
":heavy_exclamation_mark:": "\u2757",
|
||||
":heavy_heart_exclamation:": "\u2763\ufe0f",
|
||||
":heavy_heart_exclamation_mark_ornament:": "\u2763\ufe0f",
|
||||
":heavy_minus_sign:": "\u2796",
|
||||
":heavy_multiplication_x:": "\u2716\ufe0f",
|
||||
@@ -2237,6 +2247,7 @@ func emojiCode() map[string]string {
|
||||
":ladder:": "\U0001fa9c",
|
||||
":lady_beetle:": "\U0001f41e",
|
||||
":ladybug:": "\U0001f41e",
|
||||
":landslide:": "\U0001f6d8",
|
||||
":lantern:": "\U0001f3ee",
|
||||
":laos:": "\U0001f1f1\U0001f1e6",
|
||||
":laptop:": "\U0001f4bb",
|
||||
@@ -2263,6 +2274,7 @@ func emojiCode() map[string]string {
|
||||
":latvia:": "\U0001f1f1\U0001f1fb",
|
||||
":laughing:": "\U0001f606",
|
||||
":leaf_fluttering_in_wind:": "\U0001f343",
|
||||
":leafless_tree:": "\U0001fabe",
|
||||
":leafy_green:": "\U0001f96c",
|
||||
":leaves:": "\U0001f343",
|
||||
":lebanon:": "\U0001f1f1\U0001f1e7",
|
||||
@@ -3146,6 +3158,7 @@ func emojiCode() map[string]string {
|
||||
":orange_heart:": "\U0001f9e1",
|
||||
":orange_square:": "\U0001f7e7",
|
||||
":orangutan:": "\U0001f9a7",
|
||||
":orca:": "\U0001facd",
|
||||
":orthodox_cross:": "\u2626\ufe0f",
|
||||
":otter:": "\U0001f9a6",
|
||||
":outbox_tray:": "\U0001f4e4",
|
||||
@@ -3702,6 +3715,7 @@ func emojiCode() map[string]string {
|
||||
":rolling_on_the_floor_laughing:": "\U0001f923",
|
||||
":romania:": "\U0001f1f7\U0001f1f4",
|
||||
":rooster:": "\U0001f413",
|
||||
":root_vegetable:": "\U0001fadc",
|
||||
":rose:": "\U0001f339",
|
||||
":rosette:": "\U0001f3f5\ufe0f",
|
||||
":rotating_light:": "\U0001f6a8",
|
||||
@@ -3810,6 +3824,7 @@ func emojiCode() map[string]string {
|
||||
":shopping_trolley:": "\U0001f6d2",
|
||||
":shortcake:": "\U0001f370",
|
||||
":shorts:": "\U0001fa73",
|
||||
":shovel:": "\U0001fa8f",
|
||||
":shower:": "\U0001f6bf",
|
||||
":shrimp:": "\U0001f990",
|
||||
":shrug:": "\U0001f937",
|
||||
@@ -3938,6 +3953,7 @@ func emojiCode() map[string]string {
|
||||
":spiral_note_pad:": "\U0001f5d2\ufe0f",
|
||||
":spiral_notepad:": "\U0001f5d2",
|
||||
":spiral_shell:": "\U0001f41a",
|
||||
":splatter:": "\U0001fadf",
|
||||
":spock-hand:": "\U0001f596",
|
||||
":sponge:": "\U0001f9fd",
|
||||
":spoon:": "\U0001f944",
|
||||
@@ -4129,6 +4145,7 @@ func emojiCode() map[string]string {
|
||||
":tram_car:": "\U0001f68b",
|
||||
":transgender_flag:": "\U0001f3f3\ufe0f\u200d\u26a7\ufe0f",
|
||||
":transgender_symbol:": "\u26a7\ufe0f",
|
||||
":treasure_chest:": "\U0001fa8e",
|
||||
":triangular_flag:": "\U0001f6a9",
|
||||
":triangular_flag_on_post:": "\U0001f6a9",
|
||||
":triangular_ruler:": "\U0001f4d0",
|
||||
@@ -4139,6 +4156,7 @@ func emojiCode() map[string]string {
|
||||
":triumph:": "\U0001f624",
|
||||
":troll:": "\U0001f9cc",
|
||||
":trolleybus:": "\U0001f68e",
|
||||
":trombone:": "\U0001fa8a",
|
||||
":trophy:": "\U0001f3c6",
|
||||
":tropical_drink:": "\U0001f379",
|
||||
":tropical_fish:": "\U0001f420",
|
||||
@@ -4881,6 +4899,7 @@ func emojiRevCode() map[string][]string {
|
||||
"\U0001f1e8\U0001f1f3": {":cn:", ":flag_cn:", ":flag_China:"},
|
||||
"\U0001f1e8\U0001f1f4": {":flag-co:", ":flag_co:", ":colombia:", ":flag_Colombia:"},
|
||||
"\U0001f1e8\U0001f1f5": {":flag-cp:", ":flag_cp:", ":clipperton_island:", ":flag_Clipperton_Island:"},
|
||||
"\U0001f1e8\U0001f1f6": {":flag-sark:", ":flag_Sark:"},
|
||||
"\U0001f1e8\U0001f1f7": {":flag-cr:", ":flag_cr:", ":costa_rica:", ":flag_Costa_Rica:"},
|
||||
"\U0001f1e8\U0001f1fa": {":cuba:", ":flag-cu:", ":flag_cu:", ":flag_Cuba:"},
|
||||
"\U0001f1e8\U0001f1fb": {":flag-cv:", ":flag_cv:", ":cape_verde:", ":flag_Cape_Verde:"},
|
||||
@@ -5246,7 +5265,7 @@ func emojiRevCode() map[string][]string {
|
||||
"\U0001f381": {":gift:", ":wrapped_gift:"},
|
||||
"\U0001f382": {":birthday:", ":birthday_cake:"},
|
||||
"\U0001f383": {":jack-o-lantern:", ":jack_o_lantern:"},
|
||||
"\U0001f384": {":Christmas_tree:", ":christmas_tree:"},
|
||||
"\U0001f384": {":christmas_tree:", ":Christmas_tree:"},
|
||||
"\U0001f385": {":santa:", ":Santa_Claus:"},
|
||||
"\U0001f385\U0001f3fb": {":santa_tone1:"},
|
||||
"\U0001f385\U0001f3fc": {":santa_tone2:"},
|
||||
@@ -5465,7 +5484,7 @@ func emojiRevCode() map[string][]string {
|
||||
"\U0001f3ec": {":department_store:"},
|
||||
"\U0001f3ed": {":factory:"},
|
||||
"\U0001f3ee": {":lantern:", ":izakaya_lantern:", ":red_paper_lantern:"},
|
||||
"\U0001f3ef": {":Japanese_castle:", ":japanese_castle:"},
|
||||
"\U0001f3ef": {":japanese_castle:", ":Japanese_castle:"},
|
||||
"\U0001f3f0": {":castle:", ":european_castle:"},
|
||||
"\U0001f3f3": {":flag_white:", ":white_flag:"},
|
||||
"\U0001f3f3\ufe0f": {":waving_white_flag:"},
|
||||
@@ -5608,7 +5627,7 @@ func emojiRevCode() map[string][]string {
|
||||
"\U0001f44b\U0001f3fd": {":wave_tone3:"},
|
||||
"\U0001f44b\U0001f3fe": {":wave_tone4:"},
|
||||
"\U0001f44b\U0001f3ff": {":wave_tone5:"},
|
||||
"\U0001f44c": {":OK_hand:", ":ok_hand:"},
|
||||
"\U0001f44c": {":ok_hand:", ":OK_hand:"},
|
||||
"\U0001f44c\U0001f3fb": {":ok_hand_tone1:"},
|
||||
"\U0001f44c\U0001f3fc": {":ok_hand_tone2:"},
|
||||
"\U0001f44c\U0001f3fd": {":ok_hand_tone3:"},
|
||||
@@ -6169,7 +6188,7 @@ func emojiRevCode() map[string][]string {
|
||||
"\U0001f4a1": {":bulb:", ":light_bulb:"},
|
||||
"\U0001f4a2": {":anger:", ":anger_symbol:"},
|
||||
"\U0001f4a3": {":bomb:"},
|
||||
"\U0001f4a4": {":ZZZ:", ":zzz:"},
|
||||
"\U0001f4a4": {":zzz:", ":ZZZ:"},
|
||||
"\U0001f4a5": {":boom:", ":collision:"},
|
||||
"\U0001f4a6": {":sweat_drops:", ":sweat_droplets:"},
|
||||
"\U0001f4a7": {":droplet:"},
|
||||
@@ -6466,8 +6485,8 @@ func emojiRevCode() map[string][]string {
|
||||
"\U0001f5fa": {":map:"},
|
||||
"\U0001f5fa\ufe0f": {":world_map:"},
|
||||
"\U0001f5fb": {":mount_fuji:"},
|
||||
"\U0001f5fc": {":Tokyo_tower:", ":tokyo_tower:"},
|
||||
"\U0001f5fd": {":Statue_of_Liberty:", ":statue_of_liberty:"},
|
||||
"\U0001f5fc": {":tokyo_tower:", ":Tokyo_tower:"},
|
||||
"\U0001f5fd": {":statue_of_liberty:", ":Statue_of_Liberty:"},
|
||||
"\U0001f5fe": {":japan:", ":map_of_Japan:"},
|
||||
"\U0001f5ff": {":moai:", ":moyai:"},
|
||||
"\U0001f600": {":grinning:", ":grinning_face:"},
|
||||
@@ -6544,7 +6563,7 @@ func emojiRevCode() map[string][]string {
|
||||
"\U0001f642\u200d\u2195\ufe0f": {":head_shaking_vertically:"},
|
||||
"\U0001f643": {":upside_down:", ":upside-down_face:", ":upside_down_face:"},
|
||||
"\U0001f644": {":roll_eyes:", ":rolling_eyes:", ":face_with_rolling_eyes:"},
|
||||
"\U0001f645": {":person_gesturing_NO:", ":person_gesturing_no:"},
|
||||
"\U0001f645": {":person_gesturing_no:", ":person_gesturing_NO:"},
|
||||
"\U0001f645\U0001f3fb": {":person_gesturing_no_tone1:"},
|
||||
"\U0001f645\U0001f3fb\u200d\u2640\ufe0f": {":woman_gesturing_no_tone1:"},
|
||||
"\U0001f645\U0001f3fb\u200d\u2642\ufe0f": {":man_gesturing_no_tone1:"},
|
||||
@@ -6560,9 +6579,9 @@ func emojiRevCode() map[string][]string {
|
||||
"\U0001f645\U0001f3ff": {":person_gesturing_no_tone5:"},
|
||||
"\U0001f645\U0001f3ff\u200d\u2640\ufe0f": {":woman_gesturing_no_tone5:"},
|
||||
"\U0001f645\U0001f3ff\u200d\u2642\ufe0f": {":man_gesturing_no_tone5:"},
|
||||
"\U0001f645\u200d\u2640\ufe0f": {":no_good:", ":ng_woman:", ":no_good_woman:", ":woman-gesturing-no:", ":woman_gesturing_NO:", ":woman_gesturing_no:"},
|
||||
"\U0001f645\u200d\u2642\ufe0f": {":ng_man:", ":no_good_man:", ":man-gesturing-no:", ":man_gesturing_NO:", ":man_gesturing_no:"},
|
||||
"\U0001f646": {":ok_person:", ":person_gesturing_OK:", ":person_gesturing_ok:"},
|
||||
"\U0001f645\u200d\u2640\ufe0f": {":no_good:", ":ng_woman:", ":no_good_woman:", ":woman-gesturing-no:", ":woman_gesturing_no:", ":woman_gesturing_NO:"},
|
||||
"\U0001f645\u200d\u2642\ufe0f": {":ng_man:", ":no_good_man:", ":man-gesturing-no:", ":man_gesturing_no:", ":man_gesturing_NO:"},
|
||||
"\U0001f646": {":ok_person:", ":person_gesturing_ok:", ":person_gesturing_OK:"},
|
||||
"\U0001f646\U0001f3fb": {":person_gesturing_ok_tone1:"},
|
||||
"\U0001f646\U0001f3fb\u200d\u2640\ufe0f": {":woman_gesturing_ok_tone1:"},
|
||||
"\U0001f646\U0001f3fb\u200d\u2642\ufe0f": {":man_gesturing_ok_tone1:"},
|
||||
@@ -6578,8 +6597,8 @@ func emojiRevCode() map[string][]string {
|
||||
"\U0001f646\U0001f3ff": {":person_gesturing_ok_tone5:"},
|
||||
"\U0001f646\U0001f3ff\u200d\u2640\ufe0f": {":woman_gesturing_ok_tone5:"},
|
||||
"\U0001f646\U0001f3ff\u200d\u2642\ufe0f": {":man_gesturing_ok_tone5:"},
|
||||
"\U0001f646\u200d\u2640\ufe0f": {":ok_woman:", ":woman-gesturing-ok:", ":woman_gesturing_OK:", ":woman_gesturing_ok:"},
|
||||
"\U0001f646\u200d\u2642\ufe0f": {":ok_man:", ":man-gesturing-ok:", ":man_gesturing_OK:", ":man_gesturing_ok:"},
|
||||
"\U0001f646\u200d\u2640\ufe0f": {":ok_woman:", ":woman-gesturing-ok:", ":woman_gesturing_ok:", ":woman_gesturing_OK:"},
|
||||
"\U0001f646\u200d\u2642\ufe0f": {":ok_man:", ":man-gesturing-ok:", ":man_gesturing_ok:", ":man_gesturing_OK:"},
|
||||
"\U0001f647": {":bow:", ":person_bowing:"},
|
||||
"\U0001f647\U0001f3fb": {":person_bowing_tone1:"},
|
||||
"\U0001f647\U0001f3fb\u200d\u2640\ufe0f": {":woman_bowing_tone1:"},
|
||||
@@ -6831,6 +6850,7 @@ func emojiRevCode() map[string][]string {
|
||||
"\U0001f6d5": {":hindu_temple:"},
|
||||
"\U0001f6d6": {":hut:"},
|
||||
"\U0001f6d7": {":elevator:"},
|
||||
"\U0001f6d8": {":landslide:"},
|
||||
"\U0001f6dc": {":wireless:"},
|
||||
"\U0001f6dd": {":playground_slide:"},
|
||||
"\U0001f6de": {":wheel:"},
|
||||
@@ -7186,7 +7206,7 @@ func emojiRevCode() map[string][]string {
|
||||
"\U0001f993": {":zebra:", ":zebra_face:"},
|
||||
"\U0001f994": {":hedgehog:"},
|
||||
"\U0001f995": {":sauropod:"},
|
||||
"\U0001f996": {":T-Rex:", ":t-rex:", ":t_rex:"},
|
||||
"\U0001f996": {":t-rex:", ":T-Rex:", ":t_rex:"},
|
||||
"\U0001f997": {":cricket:"},
|
||||
"\U0001f998": {":kangaroo:"},
|
||||
"\U0001f999": {":llama:"},
|
||||
@@ -7267,7 +7287,7 @@ func emojiRevCode() map[string][]string {
|
||||
"\U0001f9d1\u200d\U0001f33e": {":farmer:"},
|
||||
"\U0001f9d1\u200d\U0001f373": {":cook:"},
|
||||
"\U0001f9d1\u200d\U0001f37c": {":person_feeding_baby:"},
|
||||
"\U0001f9d1\u200d\U0001f384": {":mx_claus:"},
|
||||
"\U0001f9d1\u200d\U0001f384": {":mx_claus:", ":Mx_Claus:"},
|
||||
"\U0001f9d1\u200d\U0001f393": {":student:"},
|
||||
"\U0001f9d1\u200d\U0001f3a4": {":singer:"},
|
||||
"\U0001f9d1\u200d\U0001f3a8": {":artist:"},
|
||||
@@ -7294,6 +7314,7 @@ func emojiRevCode() map[string][]string {
|
||||
"\U0001f9d1\u200d\U0001f9d1\u200d\U0001f9d2\u200d\U0001f9d2": {":family_adult_adult_child_child:"},
|
||||
"\U0001f9d1\u200d\U0001f9d2": {":family_adult_child:"},
|
||||
"\U0001f9d1\u200d\U0001f9d2\u200d\U0001f9d2": {":family_adult_child_child:"},
|
||||
"\U0001f9d1\u200d\U0001fa70": {":ballet_dancer:"},
|
||||
"\U0001f9d1\u200d\u2695\ufe0f": {":health_worker:"},
|
||||
"\U0001f9d1\u200d\u2696\ufe0f": {":judge:"},
|
||||
"\U0001f9d1\u200d\u2708\ufe0f": {":pilot:"},
|
||||
@@ -7520,6 +7541,10 @@ func emojiRevCode() map[string][]string {
|
||||
"\U0001fa86": {":nesting_dolls:"},
|
||||
"\U0001fa87": {":maracas:"},
|
||||
"\U0001fa88": {":flute:"},
|
||||
"\U0001fa89": {":harp:"},
|
||||
"\U0001fa8a": {":trombone:"},
|
||||
"\U0001fa8e": {":treasure_chest:"},
|
||||
"\U0001fa8f": {":shovel:"},
|
||||
"\U0001fa90": {":ringed_planet:"},
|
||||
"\U0001fa91": {":chair:"},
|
||||
"\U0001fa92": {":razor:"},
|
||||
@@ -7566,6 +7591,7 @@ func emojiRevCode() map[string][]string {
|
||||
"\U0001fabb": {":hyacinth:"},
|
||||
"\U0001fabc": {":jellyfish:"},
|
||||
"\U0001fabd": {":wing:"},
|
||||
"\U0001fabe": {":leafless_tree:"},
|
||||
"\U0001fabf": {":goose:"},
|
||||
"\U0001fac0": {":anatomical_heart:"},
|
||||
"\U0001fac1": {":lungs:"},
|
||||
@@ -7573,6 +7599,9 @@ func emojiRevCode() map[string][]string {
|
||||
"\U0001fac3": {":pregnant_man:"},
|
||||
"\U0001fac4": {":pregnant_person:"},
|
||||
"\U0001fac5": {":person_with_crown:"},
|
||||
"\U0001fac6": {":fingerprint:"},
|
||||
"\U0001fac8": {":hairy_creature:"},
|
||||
"\U0001facd": {":orca:"},
|
||||
"\U0001face": {":moose:"},
|
||||
"\U0001facf": {":donkey:"},
|
||||
"\U0001fad0": {":blueberries:"},
|
||||
@@ -7587,6 +7616,8 @@ func emojiRevCode() map[string][]string {
|
||||
"\U0001fad9": {":jar:"},
|
||||
"\U0001fada": {":ginger_root:"},
|
||||
"\U0001fadb": {":pea_pod:"},
|
||||
"\U0001fadc": {":root_vegetable:"},
|
||||
"\U0001fadf": {":splatter:"},
|
||||
"\U0001fae0": {":melting_face:"},
|
||||
"\U0001fae1": {":saluting_face:"},
|
||||
"\U0001fae2": {":face_with_open_eyes_and_hand_over_mouth:"},
|
||||
@@ -7596,6 +7627,9 @@ func emojiRevCode() map[string][]string {
|
||||
"\U0001fae6": {":biting_lip:"},
|
||||
"\U0001fae7": {":bubbles:"},
|
||||
"\U0001fae8": {":shaking_face:"},
|
||||
"\U0001fae9": {":face_with_bags_under_eyes:"},
|
||||
"\U0001faea": {":distorted_face:"},
|
||||
"\U0001faef": {":fight_cloud:"},
|
||||
"\U0001faf0": {":hand_with_index_finger_and_thumb_crossed:"},
|
||||
"\U0001faf1": {":rightwards_hand:"},
|
||||
"\U0001faf2": {":leftwards_hand:"},
|
||||
@@ -7709,18 +7743,18 @@ func emojiRevCode() map[string][]string {
|
||||
"\u263a\ufe0f": {":relaxed:"},
|
||||
"\u2640\ufe0f": {":female_sign:"},
|
||||
"\u2642\ufe0f": {":male_sign:"},
|
||||
"\u2648": {":Aries:", ":aries:"},
|
||||
"\u2649": {":Taurus:", ":taurus:"},
|
||||
"\u264a": {":Gemini:", ":gemini:"},
|
||||
"\u264b": {":Cancer:", ":cancer:"},
|
||||
"\u264c": {":Leo:", ":leo:"},
|
||||
"\u264d": {":Virgo:", ":virgo:"},
|
||||
"\u264e": {":Libra:", ":libra:"},
|
||||
"\u2648": {":aries:", ":Aries:"},
|
||||
"\u2649": {":taurus:", ":Taurus:"},
|
||||
"\u264a": {":gemini:", ":Gemini:"},
|
||||
"\u264b": {":cancer:", ":Cancer:"},
|
||||
"\u264c": {":leo:", ":Leo:"},
|
||||
"\u264d": {":virgo:", ":Virgo:"},
|
||||
"\u264e": {":libra:", ":Libra:"},
|
||||
"\u264f": {":Scorpio:", ":scorpius:"},
|
||||
"\u2650": {":Sagittarius:", ":sagittarius:"},
|
||||
"\u2651": {":Capricorn:", ":capricorn:"},
|
||||
"\u2652": {":Aquarius:", ":aquarius:"},
|
||||
"\u2653": {":Pisces:", ":pisces:"},
|
||||
"\u2650": {":sagittarius:", ":Sagittarius:"},
|
||||
"\u2651": {":capricorn:", ":Capricorn:"},
|
||||
"\u2652": {":aquarius:", ":Aquarius:"},
|
||||
"\u2653": {":pisces:", ":Pisces:"},
|
||||
"\u265f\ufe0f": {":chess_pawn:"},
|
||||
"\u2660": {":spade_suit:"},
|
||||
"\u2660\ufe0f": {":spades:"},
|
||||
@@ -7763,7 +7797,7 @@ func emojiRevCode() map[string][]string {
|
||||
"\u26c5": {":partly_sunny:", ":sun_behind_cloud:"},
|
||||
"\u26c8": {":thunder_cloud_rain:", ":cloud_with_lightning_and_rain:"},
|
||||
"\u26c8\ufe0f": {":thunder_cloud_and_rain:"},
|
||||
"\u26ce": {":Ophiuchus:", ":ophiuchus:"},
|
||||
"\u26ce": {":ophiuchus:", ":Ophiuchus:"},
|
||||
"\u26cf\ufe0f": {":pick:"},
|
||||
"\u26d1": {":helmet_with_cross:", ":rescue_worker’s_helmet:"},
|
||||
"\u26d1\ufe0f": {":rescue_worker_helmet:", ":helmet_with_white_cross:"},
|
||||
|
||||
+4
@@ -8,6 +8,10 @@ but only releases after v1.0.3 properly adhere to it.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [1.4.1] - 2026-08-02
|
||||
### Fixed
|
||||
- Corrected `D50ToD65` to use the CSS Color 4 matrix inverse of `D65ToD50` (#85).
|
||||
|
||||
## [1.4.0] - 2026-03-28
|
||||
### Added
|
||||
- Constructors, decomposers, and blend functions for the CSS Color Level 4 wide-gamut RGB color spaces `DisplayP3`, `A98Rgb`, `ProPhotoRgb`, and `Rec2020` (#81)
|
||||
|
||||
+3
-3
@@ -10,9 +10,9 @@ import "math"
|
||||
// Bradford chromatic adaptation between D50 and D65 illuminants.
|
||||
|
||||
func D50ToD65(x, y, z float64) (xo, yo, zo float64) {
|
||||
xo = 0.9555766*x - 0.0230393*y + 0.0631636*z
|
||||
yo = -0.0282895*x + 1.0099416*y + 0.0210077*z
|
||||
zo = 0.0122982*x - 0.0204830*y + 1.3299098*z
|
||||
xo = 0.9554734527042182*x - 0.023098536874261423*y + 0.06325964552894382*z
|
||||
yo = -0.028369706963208136*x + 1.0099954580058226*y + 0.021041398966943008*z
|
||||
zo = 0.012314001688319899*x - 0.020507696433477912*y + 1.3303659366080753*z
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Vendored
+3
-3
@@ -96,10 +96,10 @@ github.com/karimkhaleel/jsonschema
|
||||
# github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515
|
||||
## explicit
|
||||
github.com/kr/logfmt
|
||||
# github.com/kyokomi/emoji/v2 v2.2.13
|
||||
## explicit; go 1.14
|
||||
# github.com/kyokomi/emoji/v2 v2.2.14
|
||||
## explicit; go 1.21
|
||||
github.com/kyokomi/emoji/v2
|
||||
# github.com/lucasb-eyer/go-colorful v1.4.0
|
||||
# github.com/lucasb-eyer/go-colorful v1.4.1
|
||||
## explicit; go 1.12
|
||||
github.com/lucasb-eyer/go-colorful
|
||||
# github.com/mailru/easyjson v0.7.7
|
||||
|
||||
Reference in New Issue
Block a user