From 6af339c660e1b2bf9026a5bac1f83a4f0d644a8b Mon Sep 17 00:00:00 2001 From: vksvicky Date: Sun, 1 Feb 2026 21:20:57 +0000 Subject: [PATCH] Indent wrapped lines (CodeEditTextView #18): settings, UI, tests; git status skip unknown types; lint fixes - Add wrappedLineIndent setting and UI in Text Editing preferences - Add TextEditingSettingsTests for wrapped line indent - GitClient+Status: skip unknown porcelain v2 entry types instead of throwing - Lint: shorten doc comment, strip trailing whitespace in WorkspaceView - CodeFileView: pass wrappedLineIndent when using local CodeEditSourceEditor --- .../Features/Editor/Views/CodeFileView.swift | 3 + .../Models/TextEditingSettings.swift | 10 +++ .../TextEditingSettingsView.swift | 20 ++++++ .../Client/GitClient+Status.swift | 3 +- CodeEdit/WorkspaceView.swift | 1 - .../Settings/TextEditingSettingsTests.swift | 61 +++++++++++++++++++ 6 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 CodeEditTests/Features/Settings/TextEditingSettingsTests.swift diff --git a/CodeEdit/Features/Editor/Views/CodeFileView.swift b/CodeEdit/Features/Editor/Views/CodeFileView.swift index f22f6cce3d..55645e20f8 100644 --- a/CodeEdit/Features/Editor/Views/CodeFileView.swift +++ b/CodeEdit/Features/Editor/Views/CodeFileView.swift @@ -31,6 +31,8 @@ struct CodeFileView: View { var lineHeightMultiple @AppSettings(\.textEditing.wrapLinesToEditorWidth) var wrapLinesToEditorWidth + @AppSettings(\.textEditing.wrappedLineIndent) + var wrappedLineIndent @AppSettings(\.textEditing.overscroll) var overscroll @AppSettings(\.textEditing.font) @@ -125,6 +127,7 @@ struct CodeFileView: View { lineHeightMultiple: lineHeightMultiple, letterSpacing: letterSpacing, wrapLines: wrapLinesToEditorWidth, + wrappedLineIndent: wrappedLineIndent, useSystemCursor: useSystemCursor, tabWidth: defaultTabWidth, bracketPairEmphasis: getBracketPairEmphasis() diff --git a/CodeEdit/Features/Settings/Pages/TextEditingSettings/Models/TextEditingSettings.swift b/CodeEdit/Features/Settings/Pages/TextEditingSettings/Models/TextEditingSettings.swift index b5b5abb5a4..0a175d5a37 100644 --- a/CodeEdit/Features/Settings/Pages/TextEditingSettings/Models/TextEditingSettings.swift +++ b/CodeEdit/Features/Settings/Pages/TextEditingSettings/Models/TextEditingSettings.swift @@ -18,6 +18,7 @@ extension SettingsData { "Prefer Indent Using", "Tab Width", "Wrap lines to editor width", + "Indent wrapped lines", "Editor Overscroll", "Font", "Font Size", @@ -60,6 +61,11 @@ extension SettingsData { /// A flag indicating whether to wrap lines to editor width var wrapLinesToEditorWidth: Bool = true + /// Spaces to indent continuation lines when line wrapping + /// is on (e.g. 4 or 12). See: + /// https://github.com/CodeEditApp/CodeEditTextView/issues/18 + var wrappedLineIndent: Int = 4 + /// The percentage of overscroll to apply to the text view var overscroll: OverscrollOption = .medium @@ -122,6 +128,10 @@ extension SettingsData { Bool.self, forKey: .wrapLinesToEditorWidth ) ?? true + self.wrappedLineIndent = try container.decodeIfPresent( + Int.self, + forKey: .wrappedLineIndent + ) ?? 4 self.overscroll = try container.decodeIfPresent( OverscrollOption.self, forKey: .overscroll diff --git a/CodeEdit/Features/Settings/Pages/TextEditingSettings/TextEditingSettingsView.swift b/CodeEdit/Features/Settings/Pages/TextEditingSettings/TextEditingSettingsView.swift index 73d9eca772..216ecbd680 100644 --- a/CodeEdit/Features/Settings/Pages/TextEditingSettings/TextEditingSettingsView.swift +++ b/CodeEdit/Features/Settings/Pages/TextEditingSettings/TextEditingSettingsView.swift @@ -21,6 +21,7 @@ struct TextEditingSettingsView: View { indentOption defaultTabWidth wrapLinesToEditorWidth + wrappedLineIndent useSystemCursor overscroll } @@ -86,6 +87,25 @@ private extension TextEditingSettingsView { Toggle("Wrap lines to editor width", isOn: $textEditing.wrapLinesToEditorWidth) } + @ViewBuilder private var wrappedLineIndent: some View { + HStack(alignment: .top) { + Stepper( + "Indent wrapped lines", + value: Binding( + get: { Double(textEditing.wrappedLineIndent) }, + set: { textEditing.wrappedLineIndent = Int($0) } + ), + in: 0...24, + step: 1, + format: .number + ) + Text("spaces") + .foregroundColor(.secondary) + } + .disabled(!textEditing.wrapLinesToEditorWidth) + .help("Number of spaces to indent continuation lines when line wrapping is on (e.g. like Xcode).") + } + @ViewBuilder private var useSystemCursor: some View { if #available(macOS 14, *) { Toggle("Use System Cursor", isOn: $textEditing.useSystemCursor) diff --git a/CodeEdit/Features/SourceControl/Client/GitClient+Status.swift b/CodeEdit/Features/SourceControl/Client/GitClient+Status.swift index 5883f7782c..657d0f601e 100644 --- a/CodeEdit/Features/SourceControl/Client/GitClient+Status.swift +++ b/CodeEdit/Features/SourceControl/Client/GitClient+Status.swift @@ -75,7 +75,8 @@ extension GitClient { case "!", "#": // Ignored files or Header try substringToNextNull(from: &index, output: output) // move the index to the next line. default: - throw GitClientError.statusInvalidChangeType(output[typeIndex]) + // Skip unknown entry types (e.g. future porcelain v2 types or unexpected output) + try substringToNextNull(from: &index, output: output) } } diff --git a/CodeEdit/WorkspaceView.swift b/CodeEdit/WorkspaceView.swift index 11ca04eae5..1a276bcacd 100644 --- a/CodeEdit/WorkspaceView.swift +++ b/CodeEdit/WorkspaceView.swift @@ -87,7 +87,6 @@ struct WorkspaceView: View { .task { // Only refresh git data if source control is enabled guard sourceControlIsEnabled else { return } - do { try await sourceControlManager.refreshRemotes() try await sourceControlManager.refreshStashEntries() diff --git a/CodeEditTests/Features/Settings/TextEditingSettingsTests.swift b/CodeEditTests/Features/Settings/TextEditingSettingsTests.swift new file mode 100644 index 0000000000..e2e449e697 --- /dev/null +++ b/CodeEditTests/Features/Settings/TextEditingSettingsTests.swift @@ -0,0 +1,61 @@ +// +// TextEditingSettingsTests.swift +// CodeEditTests +// +// Tests for Text Editing settings, including wrapped line indent (CodeEditTextView #18). +// + +import Foundation +import Testing +@testable import CodeEdit + +@Suite("Text Editing Settings") +struct TextEditingSettingsTests { + + // MARK: - Wrapped line indent (indent wrapped lines - CodeEditTextView #18) + + @Test("Default wrapped line indent is 4 spaces") + func defaultWrappedLineIndentIsFour() { + let settings = SettingsData.TextEditingSettings() + #expect(settings.wrappedLineIndent == 4) + } + + @Test("Wrapped line indent round-trips through Codable") + func wrappedLineIndentRoundTrips() throws { + var settings = SettingsData.TextEditingSettings() + settings.wrappedLineIndent = 12 + + let encoded = try JSONEncoder().encode(settings) + let decoded = try JSONDecoder().decode(SettingsData.TextEditingSettings.self, from: encoded) + #expect(decoded.wrappedLineIndent == 12) + } + + @Test("Decode with missing wrappedLineIndent uses default 4") + func decodeMissingWrappedLineIndentUsesDefault() throws { + // JSON without wrappedLineIndent key (e.g. existing user preferences) + let json = """ + {"wrapLinesToEditorWidth": true} + """ + let data = json.data(using: .utf8)! + // Decode a minimal object; TextEditingSettings init(from:) decodes each key with default + let decoded = try JSONDecoder().decode(SettingsData.TextEditingSettings.self, from: data) + #expect(decoded.wrappedLineIndent == 4) + } + + @Test("Wrapped line indent accepts valid range 0–24") + func wrappedLineIndentValidRange() throws { + for value in [0, 4, 8, 12, 24] { + var settings = SettingsData.TextEditingSettings() + settings.wrappedLineIndent = value + let encoded = try JSONEncoder().encode(settings) + let decoded = try JSONDecoder().decode(SettingsData.TextEditingSettings.self, from: encoded) + #expect(decoded.wrappedLineIndent == value) + } + } + + @Test("Wrap lines to editor width default is true") + func wrapLinesToEditorWidthDefault() { + let settings = SettingsData.TextEditingSettings() + #expect(settings.wrapLinesToEditorWidth == true) + } +}