summaryrefslogtreecommitdiffstats
path: root/pkg/gui
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2020-08-16 10:05:45 +1000
committerJesse Duffield <jessedduffield@gmail.com>2020-08-23 14:29:18 +1000
commit0ea0c486310558e26af7ad6e4fcf17f57c2b62e3 (patch)
treeeef43cc81d0ea0741e42056ff1c7a33716e8fe09 /pkg/gui
parentcec4cb48cb8d4ba6ed3def0194f4443a1fc51cbd (diff)
WIP
Diffstat (limited to 'pkg/gui')
-rw-r--r--pkg/gui/commits_panel.go2
-rw-r--r--pkg/gui/context.go67
-rw-r--r--pkg/gui/gui.go2
-rw-r--r--pkg/gui/keybindings.go30
-rw-r--r--pkg/gui/list_view.go230
-rw-r--r--pkg/gui/stack/stack.go21
6 files changed, 221 insertions, 131 deletions
diff --git a/pkg/gui/commits_panel.go b/pkg/gui/commits_panel.go
index 4dcea9876..018782a34 100644
--- a/pkg/gui/commits_panel.go
+++ b/pkg/gui/commits_panel.go
@@ -742,7 +742,7 @@ func (gui *Gui) handleGotoBottomForCommitsPanel(g *gocui.Gui, v *gocui.View) err
}
for _, view := range gui.getListViews() {
- if view.viewName == "commits" {
+ if view.ViewName == "commits" {
return view.handleGotoBottom(g, v)
}
}
diff --git a/pkg/gui/context.go b/pkg/gui/context.go
index bf3ec2778..06664571f 100644
--- a/pkg/gui/context.go
+++ b/pkg/gui/context.go
@@ -1,5 +1,9 @@
package gui
+import (
+ "github.com/jesseduffield/lazygit/pkg/gui/stack"
+)
+
// changeContext is a helper function for when we want to change a 'main' context
// which currently just means a context that affects both the main and secondary views
// other views can have their context changed directly but this function helps
@@ -17,3 +21,66 @@ func (gui *Gui) changeMainViewsContext(context string) {
gui.State.MainContext = context
}
+
+type contextManager struct {
+ gui *Gui
+ stack stack.Stack
+}
+
+func (c *contextManager) push(contextKey string) {
+ c.stack.Push(contextKey)
+}
+
+// push focus, pop focus.
+
+type Context interface {
+ OnFocus() error
+}
+
+type SimpleContext struct {
+ Self Context
+}
+
+type RemotesContext struct {
+ Self Context
+ Branches Context
+}
+
+type CommitsContext struct {
+ Self Context
+ Files Context
+}
+
+type ContextTree struct {
+ Status SimpleContext
+ Files SimpleContext
+ Branches SimpleContext
+ Remotes RemotesContext
+ Tags SimpleContext
+ Commits CommitsContext
+ Stash SimpleContext
+ Staging SimpleContext
+ PatchBuilding SimpleContext
+ Merging SimpleContext
+ Menu SimpleContext
+ Credentials SimpleContext
+ Confirmation SimpleContext
+ CommitMessage SimpleContext
+}
+
+func (gui *Gui) createContextTree() {
+ gui.State.Contexts = ContextTree{
+ Files: SimpleContext{
+ Self: gui.filesListView(),
+ },
+ }
+}
+
+// func (c *contextManager) pop() (string, bool) {
+// value, ok := c.stack.Pop()
+
+// if !ok {
+// // bottom of the stack, let's go to the default context: the files context
+// c.gui.switchFocus(nil, newView)
+// }
+// }
diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go
index 16c829025..5530032d9 100644
--- a/pkg/gui/gui.go
+++ b/pkg/gui/gui.go
@@ -236,6 +236,8 @@ type guiState struct {
StartupStage int // one of INITIAL and COMPLETE. Allows us to not load everything at once
FilterPath string // the filename that gets passed to git log
Diff DiffState
+
+ Contexts ContextTree
}
func (gui *Gui) resetState() {
diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go
index ffef99873..102cc62a6 100644
--- a/pkg/gui/keybindings.go
+++ b/pkg/gui/keybindings.go
@@ -1408,37 +1408,37 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
for _, listView := range gui.getListViews() {
bindings = append(bindings, []*Binding{
- {ViewName: listView.viewName, Contexts: []string{listView.context}, Key: gui.getKey("universal.prevItem-alt"), Modifier: gocui.ModNone, Handler: listView.handlePrevLine},
- {ViewName: listView.viewName, Contexts: []string{listView.context}, Key: gui.getKey("universal.prevItem"), Modifier: gocui.ModNone, Handler: listView.handlePrevLine},
- {ViewName: listView.viewName, Contexts: []string{listView.context}, Key: gocui.MouseWheelUp, Modifier: gocui.ModNone, Handler: listView.handlePrevLine},
- {ViewName: listView.viewName, Contexts: []string{listView.context}, Key: gui.getKey("universal.nextItem-alt"), Modifier: gocui.ModNone, Handler: listView.handleNextLine},
- {ViewName: listView.viewName, Contexts: []string{listView.context}, Key: gui.getKey("universal.nextItem"), Modifier: gocui.ModNone, Handler: listView.handleNextLine},
- {ViewName: listView.viewName, Contexts: []string{listView.context}, Key: gui.getKey("universal.prevPage"), Modifier: gocui.ModNone, Handler: listView.handlePrevPage, Description: gui.Tr.SLocalize("prevPage")},
- {ViewName: listView.viewName, Contexts: []string{listView.context}, Key: gui.getKey("universal.nextPage"), Modifier: gocui.ModNone, Handler: listView.handleNextPage, Description: gui.Tr.SLocalize("nextPage")},
- {ViewName: listView.viewName, Contexts: []string{listView.context}, Key: gui.getKey("universal.gotoTop"), Modifier: gocui.ModNone, Handler: listView.handleGotoTop, Description: gui.Tr.SLocalize("gotoTop")},
- {ViewName: listView.viewName, Contexts: []string{listView.context}, Key: gocui.MouseWheelDown, Modifier: gocui.ModNone, Handler: listView.handleNextLine},
- {ViewName: listView.viewName, Contexts: []string{listView.context}, Key: gocui.MouseLeft, Modifier: gocui.ModNone, Handler: listView.handleClick},
+ {ViewName: listView.ViewName, Contexts: []string{listView.Context}, Key: gui.getKey("universal.prevItem-alt"), Modifier: gocui.ModNone, Handler: listView.handlePrevLine},
+ {ViewName: listView.ViewName, Contexts: []string{listView.Context}, Key: gui.getKey("universal.prevItem"), Modifier: gocui.ModNone, Handler: listView.handlePrevLine},
+ {ViewName: listView.ViewName, Contexts: []string{listView.Context}, Key: gocui.MouseWheelUp, Modifier: gocui.ModNone, Handler: listView.handlePrevLine},
+ {ViewName: listView.ViewName, Contexts: []string{listView.Context}, Key: gui.getKey("universal.nextItem-alt"), Modifier: gocui.ModNone, Handler: listView.handleNextLine},
+ {ViewName: listView.ViewName, Contexts: []string{listView.Context}, Key: gui.getKey("universal.nextItem"), Modifier: gocui.ModNone, Handler: listView.handleNextLine},
+ {ViewName: listView.ViewName, Contexts: []string{listView.Context}, Key: gui.getKey("universal.prevPage"), Modifier: gocui.ModNone, Handler: listView.handlePrevPage, Description: gui.Tr.SLocalize("prevPage")},
+ {ViewName: listView.ViewName, Contexts: []string{listView.Context}, Key: gui.getKey("universal.nextPage"), Modifier: gocui.ModNone, Handler: listView.handleNextPage, Description: gui.Tr.SLocalize("nextPage")},
+ {ViewName: listView.ViewName, Contexts: []string{listView.Context}, Key: gui.getKey("universal.gotoTop"), Modifier: gocui.ModNone, Handler: listView.handleGotoTop, Description: gui.Tr.SLocalize("gotoTop")},
+ {ViewName: listView.ViewName, Contexts: []string{listView.Context}, Key: gocui.MouseWheelDown, Modifier: gocui.ModNone, Handler: listView.handleNextLine},
+ {ViewName: listView.ViewName, Contexts: []string{listView.Context}, Key: gocui.MouseLeft, Modifier: gocui.ModNone, Handler: listView.handleClick},
}...)
// the commits panel needs to lazyload things so it has a couple of its own handlers
openSearchHandler := gui.handleOpenSearch
gotoBottomHandler := listView.handleGotoBottom
- if listView.viewName == "commits" {
+ if listView.ViewName == "commits" {
openSearchHandler = gui.handleOpenSearchForCommitsPanel
gotoBottomHandler = gui.handleGotoBottomForCommitsPanel
}
bindings = append(bindings, []*Binding{
{
- ViewName: listView.viewName,
- Contexts: []string{listView.context},
+ ViewName: listView.ViewName,
+ Contexts: []string{listView.Context},
Key: gui.getKey("universal.startSearch"),
Handler: openSearchHandler,
Description: gui.Tr.SLocalize("startSearch"),
},
{
- ViewName: listView.viewName,
- Contexts: []string{listView.context},
+ ViewName: listView.ViewName,
+ Contexts: []string{listView.Context},
Key: gui.getKey("universal.gotoBottom"),
Handler: gotoBottomHandler,
Description: gui.Tr.SLocalize("gotoBottom"),
diff --git a/pkg/gui/list_view.go b/pkg/gui/list_view.go
index e7ff845b9..b11c5bf02 100644
--- a/pkg/gui/list_view.go
+++ b/pkg/gui/list_view.go
@@ -3,15 +3,15 @@ package gui
import "github.com/jesseduffield/gocui"
type listView struct {
- viewName string
- context string
- getItemsLength func() int
- getSelectedLineIdxPtr func() *int
- handleFocus func() error
- handleItemSelect func() error
- handleClickSelectedItem func() error
- gui *Gui
- rendersToMainView bool
+ ViewName string
+ Context string
+ GetItemsLength func() int
+ GetSelectedLineIdxPtr func() *int
+ OnFocus func() error
+ OnItemSelect func() error
+ OnClickSelectedItem func() error
+ Gui *Gui
+ RendersToMainView bool
}
func (lv *listView) handlePrevLine(g *gocui.Gui, v *gocui.View) error {
@@ -23,35 +23,35 @@ func (lv *listView) handleNextLine(g *gocui.Gui, v *gocui.View) error {
}
func (lv *listView) handleLineChange(change int) error {
- if !lv.gui.isPopupPanel(lv.viewName) && lv.gui.popupPanelFocused() {
+ if !lv.Gui.isPopupPanel(lv.ViewName) && lv.Gui.popupPanelFocused() {
return nil
}
- view, err := lv.gui.g.View(lv.viewName)
+ view, err := lv.Gui.g.View(lv.ViewName)
if err != nil {
return err
}
- lv.gui.changeSelectedLine(lv.getSelectedLineIdxPtr(), lv.getItemsLength(), change)
- view.FocusPoint(0, *lv.getSelectedLineIdxPtr())
+ lv.Gui.changeSelectedLine(lv.GetSelectedLineIdxPtr(), lv.GetItemsLength(), change)
+ view.FocusPoint(0, *lv.GetSelectedLineIdxPtr())
- if lv.rendersToMainView {
- if err := lv.gui.resetOrigin(lv.gui.getMainView()); err != nil {
+ if lv.RendersToMainView {
+ if err := lv.Gui.resetOrigin(lv.Gui.getMainView()); err != nil {
return err
}
- if err := lv.gui.resetOrigin(lv.gui.getSecondaryView()); err != nil {
+ if err := lv.Gui.resetOrigin(lv.Gui.getSecondaryView()); err != nil {
return err
}
}
- if lv.handleItemSelect != nil {
- return lv.handleItemSelect()
+ if lv.OnItemSelect != nil {
+ return lv.OnItemSelect()
}
return nil
}
func (lv *listView) handleNextPage(g *gocui.Gui, v *gocui.View) error {
- view, err := lv.gui.g.View(lv.viewName)
+ view, err := lv.Gui.g.View(lv.ViewName)
if err != nil {
return nil
}
@@ -64,15 +64,15 @@ func (lv *listView) handleNextPage(g *gocui.Gui, v *gocui.View) error {
}
func (lv *listView) handleGotoTop(g *gocui.Gui, v *gocui.View) error {
- return lv.handleLineChange(-lv.getItemsLength())
+ return lv.handleLineChange(-lv.GetItemsLength())
}
func (lv *listView) handleGotoBottom(g *gocui.Gui, v *gocui.View) error {
- return lv.handleLineChange(lv.getItemsLength())
+ return lv.handleLineChange(lv.GetItemsLength())
}
func (lv *listView) handlePrevPage(g *gocui.Gui, v *gocui.View) error {
- view, err := lv.gui.g.View(lv.viewName)
+ view, err := lv.Gui.g.View(lv.ViewName)
if err != nil {
return nil
}
@@ -85,171 +85,171 @@ func (lv *listView) handlePrevPage(g *gocui.Gui, v *gocui.View) error {
}
func (lv *listView) handleClick(g *gocui.Gui, v *gocui.View) error {
- if !lv.gui.isPopupPanel(lv.viewName) && lv.gui.popupPanelFocused() {
+ if !lv.Gui.isPopupPanel(lv.ViewName) && lv.Gui.popupPanelFocused() {
return nil
}
- selectedLineIdxPtr := lv.getSelectedLineIdxPtr()
+ selectedLineIdxPtr := lv.GetSelectedLineIdxPtr()
prevSelectedLineIdx := *selectedLineIdxPtr
newSelectedLineIdx := v.SelectedLineIdx()
// we need to focus the view
- if err := lv.gui.switchFocus(nil, v); err != nil {
+ if err := lv.Gui.switchFocus(nil, v); err != nil {
return err
}
- if newSelectedLineIdx > lv.getItemsLength()-1 {
- return lv.handleFocus()
+ if newSelectedLineIdx > lv.GetItemsLength()-1 {
+ return lv.OnFocus()
}
*selectedLineIdxPtr = newSelectedLineIdx
- prevViewName := lv.gui.currentViewName()
- if prevSelectedLineIdx == newSelectedLineIdx && prevViewName == lv.viewName && lv.handleClickSelectedItem != nil {
- return lv.handleClickSelectedItem()
+ prevViewName := lv.Gui.currentViewName()
+ if prevSelectedLineIdx == newSelectedLineIdx && prevViewName == lv.ViewName && lv.OnClickSelectedItem != nil {
+ return lv.OnClickSelectedItem()
}
- if lv.handleItemSelect != nil {
- return lv.handleItemSelect()
+ if lv.OnItemSelect != nil {
+ return lv.OnItemSelect()
}
return nil
}
func (lv *listView) onSearchSelect(selectedLineIdx int) error {
- *lv.getSelectedLineIdxPtr() = selectedLineIdx
- if lv.handleItemSelect != nil {
- return lv.handleItemSelect()
+ *lv.GetSelectedLineIdxPtr() = selectedLineIdx
+ if lv.OnItemSelect != nil {
+ return lv.OnItemSelect()
}
return nil
}
func (gui *Gui) menuListView() *listView {
return &listView{
- viewName: "menu",
- getItemsLength: func() int { return gui.getMenuView().LinesHeight() },
- getSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Menu.SelectedLine },
- handleFocus: gui.handleMenuSelect,
- handleItemSelect: gui.handleMenuSelect,
+ ViewName: "menu",
+ GetItemsLength: func() int { return gui.getMenuView().LinesHeight() },
+ GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Menu.SelectedLine },
+ OnFocus: gui.handleMenuSelect,
+ OnItemSelect: gui.handleMenuSelect,
// need to add a layer of indirection here because the callback changes during runtime
- handleClickSelectedItem: func() error { return gui.State.Panels.Menu.OnPress(gui.g, nil) },
- gui: gui,
- rendersToMainView: false,
+ OnClickSelectedItem: func() error { return gui.State.Panels.Menu.OnPress(gui.g, nil) },
+ Gui: gui,
+ RendersToMainView: false,
}
}
func (gui *Gui) filesListView() *listView {
return &listView{
- viewName: "files",
- getItemsLength: func() int { return len(gui.State.Files) },
- getSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Files.SelectedLine },
- handleFocus: gui.focusAndSelectFile,
- handleItemSelect: gui.focusAndSelectFile,
- handleClickSelectedItem: gui.handleFilePress,
- gui: gui,
- rendersToMainView: false,
+ ViewName: "files",
+ GetItemsLength: func() int { return len(gui.State.Files) },
+ GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Files.SelectedLine },
+ OnFocus: gui.focusAndSelectFile,
+ OnItemSelect: gui.focusAndSelectFile,
+ OnClickSelectedItem: gui.handleFilePress,
+ Gui: gui,
+ RendersToMainView: false,
}
}
func (gui *Gui) branchesListView() *listView {
return &listView{
- viewName: "branches",
- context: "local-branches",
- getItemsLength: func() int { return len(gui.State.Branches) },
- getSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Branches.SelectedLine },
- handleFocus: gui.handleBranchSelect,
- handleItemSelect: gui.handleBranchSelect,
- gui: gui,
- rendersToMainView: true,
+ ViewName: "branches",
+ Context: "local-branches",
+ GetItemsLength: func() int { return len(gui.State.Branches) },
+ GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Branches.SelectedLine },
+ OnFocus: gui.handleBranchSelect,
+ OnItemSelect: gui.handleBranchSelect,
+ Gui: gui,
+ RendersToMainView: true,
}
}
func (gui *Gui) remotesListView() *listView {
return &listView{
- viewName: "branches",
- context: "remotes",
- getItemsLength: func() int { return len(gui.State.Remotes) },
- getSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Remotes.SelectedLine },
- handleFocus: gui.renderRemotesWithSelection,
- handleItemSelect: gui.handleRemoteSelect,
- handleClickSelectedItem: gui.handleRemoteEnter,
- gui: gui,
- rendersToMainView: true,
+ ViewName: "branches",
+ Context: "remotes",
+ GetItemsLength: func() int { return len(gui.State.Remotes) },
+ GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Remotes.SelectedLine },
+ OnFocus: gui.renderRemotesWithSelection,
+ OnItemSelect: gui.handleRemoteSelect,
+ OnClickSelectedItem: gui.handleRemoteEnter,
+ Gui: gui,
+ RendersToMainView: true,
}
}
func (gui *Gui) remoteBranchesListView() *listView {
return &listView{
- viewName: "branches",
- context: "remote-branches",
- getItemsLength: func() int { return len(gui.State.RemoteBranches) },
- getSelectedLineIdxPtr: func() *int { return &gui.State.Panels.RemoteBranches.SelectedLine },
- handleFocus: gui.handleRemoteBranchSelect,
- handleItemSelect: gui.handleRemoteBranchSelect,
- gui: gui,
- rendersToMainView: true,
+ ViewName: "branches",
+ Context: "remote-branches",
+ GetItemsLength: func() int { return len(gui.State.RemoteBranches) },
+ GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.RemoteBranches.SelectedLine },
+ OnFocus: gui.handleRemoteBranchSelect,
+ OnItemSelect: gui.handleRemoteBranchSelect,
+ Gui: gui,
+ RendersToMainView: true,
}
}
func (gui *Gui) tagsListView() *listView {
return &listView{
- viewName: "branches",
- context: "tags",
- getItemsLength: func() int { return len(gui.State.Tags) },
- getSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Tags.SelectedLine },
- handleFocus: gui.handleTagSelect,
- handleItemSelect: gui.handleTagSelect,
- gui: gui,
- rendersToMainView: true,
+ ViewName: "branches",
+ Context: "tags",
+ GetItemsLength: func() int { return len(gui.State.Tags) },
+ GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Tags.SelectedLine },
+ OnFocus: gui.handleTagSelect,
+ OnItemSelect: gui.handleTagSelect,
+ Gui: gui,
+ RendersToMainView: true,
}
}
func (gui *Gui) branchCommitsListView() *listView {
return &listView{
- viewName: "commits",
- context: "branch-commits",
- getItemsLength: func() int { return len(gui.State.Commits) },
- getSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Commits.SelectedLine },
- handleFocus: gui.handleCommitSelect,
- handleItemSelect: gui.handleCommitSelect,
- handleClickSelectedItem: gui.handleSwitchToCommitFilesPanel,
- gui: gui,
- rendersToMainView: true,
+ ViewName: "commits",
+ Context: "branch-commits",
+ GetItemsLength: func() int { return len(gui.State.Commits) },
+ GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Commits.SelectedLine },
+ OnFocus: gui.handleCommitSelect,
+ OnItemSelect: gui.handleCommitSelect,
+ OnClickSelectedItem: gui.handleSwitchToCommitFilesPanel,
+ Gui: gui,
+ RendersToMainView: true,
}
}
func (gui *Gui) reflogCommitsListView() *listView {
return &listView{
- viewName: "commits",
- context: "reflog-commits",
- getItemsLength: func() int { return len(gui.State.FilteredReflogCommits) },
- getSelectedLineIdxPtr: func() *int { return &gui.State.Panels.ReflogCommits.SelectedLine },
- handleFocus: gui.handleReflogCommitSelect,
- handleItemSelect: gui.handleReflogCommitSelect,
- gui: gui,
- rendersToMainView: true,
+ ViewName: "commits",
+ Context: "reflog-commits",
+ GetItemsLength: func() int { return len(gui.State.FilteredReflogCommits) },
+ GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.ReflogCommits.SelectedLine },
+ OnFocus: gui.handleReflogCommitSelect,
+ OnItemSelect: gui.handleReflogCommitSelect,
+ Gui: gui,
+ RendersToMainView: true,
}
}
func (gui *Gui) stashListView() *listView {
return &listView{
- viewName: "stash",
- getItemsLength: func() int { return len(gui.State.StashEntries) },
- getSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Stash.SelectedLine },
- handleFocus: gui.handleStashEntrySelect,
- handleItemSelect: gui.handleStashEntrySelect,
- gui: gui,
- rendersToMainView: true,
+ ViewName: "stash",
+ GetItemsLength: func() int { return len(gui.State.StashEntries) },
+ GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Stash.SelectedLine },
+ OnFocus: gui.handleStashEntrySelect,
+ OnItemSelect: gui.handleStashEntrySelect,
+ Gui: gui,
+ RendersToMainView: true,
}
}
func (gui *Gui) commitFilesListView() *listView {
return &listView{
- viewName: "commitFiles",
- getItemsLength: func() int { return len(gui.State.CommitFiles) },
- getSelectedLineIdxPtr: func() *int { return &gui.State.Panels.CommitFiles.SelectedLine },
- handleFocus: gui.handleCommitFileSelect,
- handleItemSelect: gui.handleCommitFileSelect,
- gui: gui,
- rendersToMainView: true,
+ ViewName: "commitFiles",
+ GetItemsLength: func() int { return len(gui.State.CommitFiles) },
+ GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.CommitFiles.SelectedLine },
+ OnFocus: gui.handleCommitFileSelect,
+ OnItemSelect: gui.handleCommitFileSelect,
+ Gui: gui,
+ RendersToMainView: true,
}
}
diff --git a/pkg/gui/stack/stack.go b/pkg/gui/stack/stack.go
new file mode 100644
index 000000000..f4805b968
--- /dev/null
+++ b/pkg/gui/stack/stack.go
@@ -0,0 +1,21 @@
+package stack
+
+type Stack struct {
+ stack []string
+}
+
+func (s *Stack) Push(contextKey string) {
+ s.stack = append(s.stack, contextKey)
+}
+
+func (s *Stack) Pop() (string, bool) {
+ if len(s.stack) == 0 {
+ return "", false
+ }
+
+ n := len(s.stack) - 1
+ value := s.stack[n]
+ s.stack = s.stack[:n]
+
+ return value, true
+}