diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..ac03f44 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,104 @@ +version: "2" + +linters: + enable: + - errcheck + - govet + - ineffassign + - staticcheck + - unused + - arangolint + - asasalint + - asciicheck + - bidichk + - bodyclose + - canonicalheader + - clickhouselint + - containedctx + - copyloopvar + - cyclop + - decorder + - dogsled + - dupword + - durationcheck + - embeddedstructfieldcheck + - errchkjson + - errname + - exhaustive + - exptostd + - fatcontext + - funcorder + - funlen + - ginkgolinter + - gocheckcompilerdirectives + - gochecknoinits + - gochecksumtype + - gocritic + - gocyclo + - godoclint + - goheader + - gomoddirectives + - gomodguard_v2 + - goprintffuncname + - gosmopolitan + - grouper + - iface + - importas + - inamedparam + - interfacebloat + - intrange + - iotamixing + - lll + - loggercheck + - makezero + - misspell + - modernize + - nakedret + - nestif + - nilerr + - nilnesserr + - nlreturn + - nonamedreturns + - nosprintfhostport + - paralleltest + - perfsprint + - prealloc + - promlinter + - protogetter + - reassign + - recvcheck + - rowserrcheck + - sloglint + - spancheck + - sqlclosecheck + - tagalign + - testableexamples + - testifylint + - testpackage + - thelper + - tparallel + - unconvert + - unparam + - unqueryvet + - usestdlibvars + - usetesting + - varnamelen + - wastedassign + - whitespace + - wsl_v5 + - zerologlint + + settings: + varnamelen: + ignore-names: + - err + - wg + - mu + - ts + - to + - es + - op + - fd + - id + - fn + diff --git a/.woodpecker/build.yaml b/.woodpecker/build.yaml index e20cc6f..9b8393b 100644 --- a/.woodpecker/build.yaml +++ b/.woodpecker/build.yaml @@ -2,7 +2,9 @@ matrix: platform: - linux/amd64 goversion: - - 1.24 + - 1.26 + lintversion: + - v2.12.2 labels: platform: ${platform} @@ -21,7 +23,7 @@ steps: event: [push] image: golang:${goversion} commands: - - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b $(go env GOPATH)/bin v2.5.0 + - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b $(go env GOPATH)/bin ${lintversion} - golangci-lint --version - golangci-lint run ./... depends_on: [build] diff --git a/Makefile b/Makefile index a3539bc..9254d74 100644 --- a/Makefile +++ b/Makefile @@ -46,10 +46,7 @@ test: clean testlint: test lint lint: - golangci-lint run - -lint-full: - golangci-lint run --enable-all --exclude-use-default --disable exhaustivestruct,exhaustruct,depguard,interfacer,deadcode,golint,structcheck,scopelint,varcheck,ifshort,maligned,nosnakecase,godot,funlen,gofumpt,cyclop,noctx,gochecknoglobals,paralleltest + golangci-lint run --show-stats=false testfuzzy: clean go test -fuzz ./... $(ARGS) diff --git a/main.go b/main.go index 54498d0..2b868a1 100644 --- a/main.go +++ b/main.go @@ -45,13 +45,6 @@ const ( LevelNotice = slog.Level(2) VERSION = "v0.3.2" - - IPC_HEADER_SIZE = 14 - IPC_MAGIC = "i3-ipc" - - // message types - IPC_GET_TREE = 4 - IPC_RUN_COMMAND = 0 ) var ( @@ -88,7 +81,7 @@ func main() { flag.BoolVarP(&Previous, "prev", "p", false, "cycle backward") flag.BoolVarP(&Debug, "debug", "d", false, "enable debugging") flag.BoolVarP(&Dumptree, "dump", "D", false, "dump the sway tree (needs -d as well)") - flag.BoolVarP(&Dumpvisibles, "dump-visibles", "", false, "dump a list of visible windows on current workspace (needs -d)") + flag.BoolVarP(&Dumpvisibles, "dump-visibles", "", false, "dump the sway tree (needs -d as well)") flag.BoolVarP(&Notswitch, "no-switch", "n", false, "do not switch windows") flag.BoolVarP(&Version, "version", "v", false, "show program version") flag.BoolVarP(&Showhelp, "help", "h", Showhelp, "show help") @@ -117,35 +110,46 @@ func main() { log.Fatalf("failed to close log file: %s", err) } }() + setupLogging(file) } else { setupLogging(os.Stdout) } + if err := cycle(); err != nil { + log.Fatal(err) // nolint:gocritic + } +} + +func cycle() error { // connect to sway unix socket ipc := swayipc.NewSwayIPC() err := ipc.Connect() if err != nil { - log.Fatal(err) + return err } - defer ipc.Close() + defer func() { + if err := ipc.Close(); err != nil { + log.Fatal(err) + } + }() sway, err := ipc.GetTree() if err != nil { - log.Fatal(err) + return err } // traverse the tree and find visible windows if err := processJSON(sway); err != nil { - log.Fatalf("%s", err) + return err } if len(Visibles) == 0 { - os.Exit(0) + return nil } - id := 0 + var id int if Previous { id = findPrevWindow() slog.Debug("findPrevWindow", "nextid", id) @@ -156,9 +160,11 @@ func main() { if id > 0 && !Notswitch { if err := switchFocus(id, ipc); err != nil { - log.Fatalf("%s", err) + return err } } + + return nil } // get into the sway tree, determine current workspace and extract all @@ -177,6 +183,7 @@ func processJSON(sway *swayipc.Node) error { // this is an output node containing the current workspace CurrentWorkspace = node.CurrentWorkspace recurseNodes(node.Nodes) + break } } @@ -200,6 +207,7 @@ func findNextWindow() int { for _, node := range Visibles { if node.Focused { seenfocused = true + continue } @@ -227,6 +235,7 @@ func findPrevWindow() int { if node.Focused { return prevnode } + prevnode = node.ID } @@ -237,7 +246,7 @@ func findPrevWindow() int { func switchFocus(id int, ipc *swayipc.SwayIPC) error { responses, err := ipc.RunContainerCommand(id, "focus") if err != nil { - log.Fatalf("failed to send focus command to container %d: %s (%s)", + return fmt.Errorf("failed to send focus command to container %d: %s (%s)", id, responses[0].Error, err) } @@ -249,18 +258,18 @@ func switchFocus(id int, ipc *swayipc.SwayIPC) error { // iterate recursively over given node list extracting visible windows func recurseNodes(nodes []*swayipc.Node) { for _, node := range nodes { - if istype(node, workspace) { if node.Name == CurrentWorkspace { - //floating_nodes need to be sorted because - //order changes each time they are focused. + // floating_nodes need to be sorted because + // order changes each time they are focused. FloatVis := node.FloatingNodes sort.Slice(FloatVis, func(i, j int) bool { return FloatVis[i].ID < FloatVis[j].ID }) - //now we can handle nodes and floating_nodes identical + // now we can handle nodes and floating_nodes identical node.Nodes = append(node.Nodes, FloatVis...) recurseNodes(node.Nodes) + return } @@ -329,25 +338,25 @@ func setupLogging(output io.Writer) { } } -// little helper to distinguish sway tree node types +// istype is a little helper to distinguish sway tree node types func istype(nd *swayipc.Node, which int) bool { switch nd.Type { case "root": - return which == root + return which == swayipc.NodeTypeRoot case "output": - return which == output + return which == swayipc.NodeTypeOutput case "workspace": - return which == workspace + return which == swayipc.NodeTypeWorkspace case "con": - return which == con + return which == swayipc.NodeTypeCon case "floating_con": - return which == floating + return which == swayipc.NodeTypeFloating } return false } -// returns TRUE if stdout is NOT a tty or windows +// IsNoTty returns TRUE if stdout is NOT a tty or windows func IsNoTty() bool { if !isatty.IsTerminal(os.Stdout.Fd()) { return true