Skip to content

Commit 7e7aadc

Browse files
Merge pull request #797 from jesseduffield/support-p-flag
Support -p flag again
2 parents 9134abe + f5ff116 commit 7e7aadc

9 files changed

Lines changed: 74 additions & 30 deletions

File tree

.claude/settings.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
"hooks": {
33
"PostToolUse": [
44
{
5-
"matcher": "Edit|Write",
5+
"matcher": "Edit|Write|MultiEdit",
66
"hooks": [
77
{
88
"type": "command",
9-
"command": "jq -r '.tool_input.file_path // empty' | xargs -I{} gofumpt -w {}"
9+
"command": "jq -r '.tool_input.file_path // empty' | { read -r f; case \"$f\" in *.go) gofumpt -w \"$f\" ;; esac; } 2>/dev/null || true"
1010
}
1111
]
1212
}

CLAUDE.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# CLAUDE.md
2+
3+
## Build & test
4+
- All Go commands need `GOFLAGS=-mod=vendor` (deps are vendored, including the `jesseduffield/gocui` fork and the Docker SDK).
5+
- Unit tests: `GOFLAGS=-mod=vendor go test ./...`

pkg/commands/docker.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,25 @@ func NewDockerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Translat
154154
log.Warn(err.Error())
155155
}
156156

157+
// When the user passes -p outside of a compose directory, treat it as the
158+
// local project so the project/services panels still appear and filtering
159+
// is applied. Inside a compose dir, LocalProjectName is derived from
160+
// container labels later in RefreshContainersAndServices.
161+
if !dockerCommand.InDockerComposeProject && config.ProjectName != "" {
162+
dockerCommand.LocalProjectName = config.ProjectName
163+
}
164+
157165
return dockerCommand, nil
158166
}
159167

168+
// IsProjectScoped reports whether lazydocker should be scoped to a single
169+
// compose project — either because we're inside a compose directory or
170+
// because the user passed -p. When false, the project/services panels are
171+
// hidden and all containers are shown in a flat list.
172+
func (c *DockerCommand) IsProjectScoped() bool {
173+
return c.InDockerComposeProject || c.Config.ProjectName != ""
174+
}
175+
160176
func (c *DockerCommand) setDockerComposeCommand(config *config.AppConfig) {
161177
if config.UserConfig.CommandTemplates.DockerCompose != "docker compose" {
162178
return

pkg/commands/docker_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"testing"
66

77
"github.com/docker/docker/client"
8+
"github.com/jesseduffield/lazydocker/pkg/config"
89
"github.com/stretchr/testify/assert"
910
)
1011

@@ -61,3 +62,30 @@ func TestNewDockerClientVersionNegotiation(t *testing.T) {
6162
"client version should not be locked to DOCKER_API_VERSION env var")
6263
})
6364
}
65+
66+
// TestIsProjectScoped covers the predicate that drives whether the
67+
// project/services panels appear and whether the containers panel filters by
68+
// project. The "outside compose dir + -p" case is the regression we fixed
69+
// after PR #776 silently disabled it.
70+
func TestIsProjectScoped(t *testing.T) {
71+
cases := []struct {
72+
name string
73+
inDockerComposeProject bool
74+
projectName string
75+
want bool
76+
}{
77+
{"inside compose dir, no -p", true, "", true},
78+
{"inside compose dir, with -p", true, "myproject", true},
79+
{"outside compose dir, no -p", false, "", false},
80+
{"outside compose dir, with -p", false, "myproject", true},
81+
}
82+
for _, tc := range cases {
83+
t.Run(tc.name, func(t *testing.T) {
84+
c := &DockerCommand{
85+
InDockerComposeProject: tc.inDockerComposeProject,
86+
Config: &config.AppConfig{ProjectName: tc.projectName},
87+
}
88+
assert.Equal(t, tc.want, c.IsProjectScoped())
89+
})
90+
}
91+
}

pkg/gui/containers_panel.go

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,14 @@ func (gui *Gui) getContainersPanel() *panels.SideListPanel[*commands.Container]
8787
return false
8888
}
8989

90-
// Only apply project and standalone filtering when we are inside a
91-
// docker-compose project. Outside of a compose project all
92-
// containers are shown in a flat list regardless of which compose
93-
// project they belong to.
94-
if gui.DockerCommand.InDockerComposeProject {
95-
// This check must be inside the InDockerComposeProject guard:
96-
// outside a compose project, services are still derived from
97-
// container labels, so compose-managed containers from other
98-
// projects would be incorrectly hidden.
90+
// When project-scoped, apply project and standalone filtering.
91+
// Otherwise all containers are shown in a flat list regardless
92+
// of which compose project they belong to.
93+
if gui.DockerCommand.IsProjectScoped() {
94+
// This check must be inside the IsProjectScoped guard: when
95+
// not project-scoped, services are still derived from container
96+
// labels, so compose-managed containers from other projects
97+
// would be incorrectly hidden.
9998
//
10099
// Note that this is O(N*M) time complexity where N is the number of services
101100
// and M is the number of containers. We expect N to be small but M may be large,

pkg/gui/gui.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ func (gui *Gui) ShouldRefresh(key string) bool {
459459
}
460460

461461
func (gui *Gui) initiallyFocusedViewName() string {
462-
if gui.DockerCommand.InDockerComposeProject {
462+
if gui.DockerCommand.IsProjectScoped() {
463463
return "services"
464464
}
465465
return "containers"

pkg/gui/project_panel.go

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,7 @@ func (gui *Gui) getProjectPanel() *panels.SideListPanel[*commands.Project] {
5959
return gui.renderContainersAndServices()
6060
},
6161
Hide: func() bool {
62-
// Only show the project panel when we are inside a docker-compose
63-
// project directory. When launched outside of a compose project
64-
// there is no meaningful local project to display, so we hide the
65-
// panel and let the containers panel show all containers in a flat
66-
// list (matching the behaviour from before v0.25).
67-
return !gui.DockerCommand.InDockerComposeProject
62+
return !gui.DockerCommand.IsProjectScoped()
6863
},
6964
}
7065
}
@@ -99,18 +94,19 @@ func (gui *Gui) refreshProject() error {
9994
}
10095

10196
// getDiscoveredProjects returns all docker compose projects by examining container labels.
102-
// The local project (from docker-compose.yml in the current directory) is included if
103-
// it has running containers or if InDockerComposeProject is true.
97+
// The local project (from docker-compose.yml in the current directory, or from -p) is
98+
// included even when it has no running containers, so the user always sees the project
99+
// they explicitly scoped to.
104100
func (gui *Gui) getDiscoveredProjects() []*commands.Project {
105101
containers := gui.Panels.Containers.List.GetAllItems()
106102
projectNames := gui.DockerCommand.GetProjectNames(containers)
107103

108-
// If we're in a docker compose project but it has no running containers,
109-
// still include it. We don't fall back to the directory name here to avoid
104+
// If we're scoped to a project but it has no running containers, still
105+
// include it. We don't fall back to the directory name here to avoid
110106
// briefly flashing the wrong project name on startup.
111107
localName := gui.DockerCommand.LocalProjectName
112108

113-
if gui.DockerCommand.InDockerComposeProject && localName != "" {
109+
if gui.DockerCommand.IsProjectScoped() && localName != "" {
114110
found := false
115111
for _, name := range projectNames {
116112
if name == localName {
@@ -195,6 +191,11 @@ func (gui *Gui) renderAllLogs(project *commands.Project) tasks.TaskFunc {
195191
}
196192

197193
func (gui *Gui) renderDockerComposeConfig(project *commands.Project) tasks.TaskFunc {
194+
if !gui.DockerCommand.InDockerComposeProject {
195+
return gui.NewSimpleRenderStringTask(func() string {
196+
return "Compose config is only available when launched from a docker-compose project directory"
197+
})
198+
}
198199
if project != nil && project.Name != gui.DockerCommand.LocalProjectName {
199200
return gui.NewSimpleRenderStringTask(func() string {
200201
return "Compose config is not available for non-local projects"

pkg/gui/services_panel.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,7 @@ func (gui *Gui) getServicesPanel() *panels.SideListPanel[*commands.Service] {
9090
return presentation.GetServiceDisplayStrings(&gui.Config.UserConfig.Gui, service)
9191
},
9292
Hide: func() bool {
93-
// Only show the services panel when we are inside a docker-compose
94-
// project directory. When launched outside of a compose project
95-
// there is no local project context, so the panel is hidden and
96-
// all containers are shown in a flat list (matching pre-v0.25
97-
// behaviour).
98-
return !gui.DockerCommand.InDockerComposeProject
93+
return !gui.DockerCommand.IsProjectScoped()
9994
},
10095
}
10196
}

pkg/gui/views.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ func (gui *Gui) createAllViews() error {
130130

131131
gui.Views.Containers.Highlight = true
132132
gui.Views.Containers.SelBgColor = selectedLineBgColor
133-
if gui.Config.UserConfig.Gui.ShowAllContainers || !gui.DockerCommand.InDockerComposeProject {
133+
if gui.Config.UserConfig.Gui.ShowAllContainers || !gui.DockerCommand.IsProjectScoped() {
134134
gui.Views.Containers.Title = gui.Tr.ContainersTitle
135135
} else {
136136
gui.Views.Containers.Title = gui.Tr.StandaloneContainersTitle

0 commit comments

Comments
 (0)