Skip to content

Commit e8dbd37

Browse files
committed
Update IndentLines functions
1 parent eb5d8a6 commit e8dbd37

File tree

2 files changed

+114
-17
lines changed

2 files changed

+114
-17
lines changed

Sources/CodeEditSourceEditor/Controller/TextViewController+IndentLines.swift

Lines changed: 104 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,35 +9,111 @@ import AppKit
99
import CodeEditTextView
1010

1111
extension TextViewController {
12-
/// Handles indentation and unindentation
12+
/// Handles indentation and unindentation for selected lines in the text view.
1313
///
14-
/// Handles the indentation of lines in the text view based on the current indentation option.
14+
/// This function modifies the indentation of the selected lines based on the current `indentOption`.
15+
/// It handles both indenting (moving text to the right) and unindenting (moving text to the left), with the
16+
/// behavior depending on whether `inwards` is `true` or `false`. It processes the `indentOption` to apply the
17+
/// correct number of spaces or tabs.
1518
///
16-
/// This function assumes that the document is formatted according to the current selected indentation option.
17-
/// It will not indent a tab character if spaces are selected, and vice versa. Ensure that the document is
18-
/// properly formatted before invoking this function.
19+
/// The function operates on **one-to-many selections**, where each selection can affect **one-to-many lines**.
20+
/// Each of those lines will be modified accordingly.
1921
///
20-
/// - Parameter inwards: A Boolean flag indicating whether to outdent (default is `false`).
22+
/// ```
23+
/// +----------------------------+
24+
/// | Selection 1 |
25+
/// | |
26+
/// | +------------------------+ |
27+
/// | | Line 1 (Modified) | |
28+
/// | +------------------------+ |
29+
/// | +------------------------+ |
30+
/// | | Line 2 (Modified) | |
31+
/// | +------------------------+ |
32+
/// +----------------------------+
33+
///
34+
/// +----------------------------+
35+
/// | Selection 2 |
36+
/// | |
37+
/// | +------------------------+ |
38+
/// | | Line 1 (Modified) | |
39+
/// | +------------------------+ |
40+
/// | +------------------------+ |
41+
/// | | Line 2 (Modified) | |
42+
/// | +------------------------+ |
43+
/// +----------------------------+
44+
/// ```
45+
///
46+
/// **Selection Updates**:
47+
/// The method will not update the selection (and its highlighting) until all lines for the given selection
48+
/// have been processed. This ensures that the selection updates are only applied after all indentations
49+
/// are completed, preventing issues where the selection might be updated incrementally during the processing
50+
/// of multiple lines.
51+
///
52+
/// - Parameter inwards: A `Bool` flag indicating whether to outdent (default is `false`).
53+
/// - If `inwards` is `true`, the text will be unindented.
54+
/// - If `inwards` is `false`, the text will be indented.
55+
///
56+
/// - Note: This function assumes that the document is formatted according to the selected indentation option.
57+
/// It will not indent a tab character if spaces are selected, and vice versa. Ensure that the document is
58+
/// properly formatted before invoking this function.
59+
///
60+
/// - Important: This method operates on the current selections in the `textView`. It performs a reverse iteration
61+
/// over the text selections, ensuring that edits do not affect the later selections.
62+
2163
public func handleIndent(inwards: Bool = false) {
22-
let indentationChars: String = indentOption.stringValue
2364
guard !cursorPositions.isEmpty else { return }
2465

2566
textView.undoManager?.beginUndoGrouping()
26-
for cursorPosition in self.cursorPositions.reversed() {
67+
var selectionIndex = 0
68+
textView.editSelections { textView, selection in
2769
// get lineindex, i.e line-numbers+1
28-
guard let lineIndexes = getHighlightedLines(for: cursorPosition.range) else { continue }
29-
30-
for lineIndex in lineIndexes {
31-
adjustIndentation(
32-
lineIndex: lineIndex,
33-
indentationChars: indentationChars,
34-
inwards: inwards
35-
)
36-
}
70+
guard let lineIndexes = getHighlightedLines(for: selection.range) else { return }
71+
72+
adjustIndentation(lineIndexes: lineIndexes, inwards: inwards)
73+
74+
updateSelection(
75+
selection: selection,
76+
textSelectionCount: textView.selectionManager.textSelections.count,
77+
inwards: inwards,
78+
lineCount: lineIndexes.count,
79+
selectionIndex: selectionIndex
80+
)
81+
82+
selectionIndex += 1
3783
}
3884
textView.undoManager?.endUndoGrouping()
3985
}
4086

87+
private func updateSelection(
88+
selection: TextSelectionManager.TextSelection,
89+
textSelectionCount: Int,
90+
inwards: Bool,
91+
lineCount: Int,
92+
selectionIndex: Int
93+
) {
94+
let sectionModifier = calculateSelectionIndentationAdjustment(
95+
textSelectionCount: textSelectionCount,
96+
selectionIndex: selectionIndex,
97+
lineCount: lineCount
98+
)
99+
100+
let charCount = indentOption.charCount
101+
102+
selection.range.location += inwards ? -charCount * sectionModifier : charCount * sectionModifier
103+
if lineCount > 1 {
104+
let ammount = charCount * (lineCount - 1)
105+
selection.range.length += inwards ? -ammount : ammount
106+
}
107+
}
108+
109+
private func calculateSelectionIndentationAdjustment(
110+
textSelectionCount: Int,
111+
selectionIndex: Int,
112+
lineCount: Int
113+
) -> Int {
114+
return 1 + ((textSelectionCount - selectionIndex) - 1) * lineCount
115+
}
116+
41117
/// This method is used to handle tabs appropriately when multiple lines are selected,
42118
/// allowing normal use of tabs.
43119
///
@@ -66,6 +142,17 @@ extension TextViewController {
66142
return startLineInfo.index...endLineInfo.index
67143
}
68144

145+
private func adjustIndentation(lineIndexes: ClosedRange<Int>, inwards: Bool) {
146+
let indentationChars: String = indentOption.stringValue
147+
for lineIndex in lineIndexes {
148+
adjustIndentation(
149+
lineIndex: lineIndex,
150+
indentationChars: indentationChars,
151+
inwards: inwards
152+
)
153+
}
154+
}
155+
69156
private func adjustIndentation(lineIndex: Int, indentationChars: String, inwards: Bool) {
70157
guard let lineInfo = textView.layoutManager.textLineForIndex(lineIndex) else { return }
71158

Sources/CodeEditSourceEditor/Enums/IndentOption.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@ public enum IndentOption: Equatable, Hashable {
1919
}
2020
}
2121

22+
/// Represents the number of chacters that indent represents
23+
var charCount: Int {
24+
switch self {
25+
case .spaces(let count):
26+
count
27+
case .tab:
28+
1
29+
}
30+
}
31+
2232
public static func == (lhs: IndentOption, rhs: IndentOption) -> Bool {
2333
switch (lhs, rhs) {
2434
case (.tab, .tab):

0 commit comments

Comments
 (0)