// Copyright (c) 2026 Storm Dragon // SPDX-License-Identifier: GPL-3.0-or-later package reader import ( "slices" "strings" "time" "unicode" "git.stormux.org/storm/fenrir/internal/presentation" "git.stormux.org/storm/fenrir/internal/screen" ) func (r *Reader) Configure_indentation(mode string) { r.indentationMode = mode } func (r *Reader) indentation_feedback(announce bool) *presentation.Message { level, valid := r.review.Indentation() if !valid { return nil // Blank rows preserve the last meaningful level. } changed := r.indentKnown && level != r.lastIndent r.lastIndent, r.indentKnown = level, true if !announce || !changed || r.indentationMode == "" || r.indentationMode == "none" { return nil } message := presentation.Indentation_message(level, r.indentationMode) return &message } func (r *Reader) automatic_indentation(previous, next screen.Snapshot, switched bool) *presentation.Message { if switched { r.indentKnown = false } // Passive program output must not create indentation chatter. Match Python's // cursor-input window and suppress automatic cursor feedback during review. announce := !switched && next.Active && r.automatic && r.cursorEcho && r.trackingMode == "cursor" && !r.review.Active() && r.region_state().allows(next.X, next.Y, next.Cols, true, true) && !r.applicationInputAt.IsZero() && time.Since(r.applicationInputAt) < 300*time.Millisecond if announce { // A hidden leading-whitespace edit must not produce indentation cues // at an otherwise visible cursor farther along the same row. for col, char := range next.Line(next.Y) { if !r.region_state().allows(col, next.Y, next.Cols, true, true) { announce = false break } if !unicode.IsSpace(char) { break } } } if announce && previous.Y == next.Y { // Horizontal input only reports a genuine leading-whitespace edit, not // movement within a line or ordinary typing/deletion in its content. before, after := previous.Text(previous.Y), next.Text(next.Y) announce = strings.TrimSpace(before) == strings.TrimSpace(after) && before != after } else if announce { // A cursor moving with new program output is not line navigation. announce = slices.Equal(previous.Line(next.Y), next.Line(next.Y)) } return r.indentation_feedback(announce) }