Commit 4c75f0eea5
Verified · cmc
Layout: unified · split
Sources/OrgEditorAppKit/Modal.swift +14 −8
| @@ -34,7 +34,7 @@ extension OrgEditor { | ||
| 34 | 34 | if keys.count > 1 { onKeysPending?(nil, []) } |
| 35 | 35 | let context = editContext() |
| 36 | 36 | if let binding = candidates.first(where: { modalRuns($0, in: context) }) { |
| 37 | if binding.command == "org.fill-paragraph", vim.mode == .visual || vim.mode == .visualLine { | |
| 37 | if binding.command == "org.fill-paragraph", vim.mode == .visual || vim.mode == .visualLine || vim.mode == .visualBlock { | |
| 38 | 38 | fillVisual() |
| 39 | 39 | } else { |
| 40 | 40 | run(binding.command) |
| @@ -57,15 +57,21 @@ extension OrgEditor { | ||
| 57 | 57 | } |
| 58 | 58 | } |
| 59 | 59 | |
| 60 | /// M-q in visual states: `org-fill-paragraph` on the region, the cursor kept. A change | |
| 61 | /// deactivates the region, which leaves visual state. | |
| 60 | /// M-q in visual states: `org-fill-paragraph` on the region, the cursor kept. Evil turns | |
| 61 | /// `transient-mark-mode` off in block state, so there it fills the paragraph at the cursor. | |
| 62 | /// A change deactivates the region, which leaves visual state. | |
| 62 | 63 | private func fillVisual() { |
| 63 | let selected = textView.selectedRange() | |
| 64 | let cursor = visualCaret ?? selected.location | |
| 64 | let selected = textView.selectedRanges | |
| 65 | let cursor = visualCaret ?? textView.selectedRange().location | |
| 65 | 66 | let text = document.text |
| 66 | perform(FillParagraph(point: cursor)) | |
| 67 | if vim.mode == .visualBlock { | |
| 68 | textView.setSelectedRange(NSRange(location: cursor, length: 0)) | |
| 69 | perform(FillParagraph()) | |
| 70 | } else { | |
| 71 | perform(FillParagraph(point: cursor)) | |
| 72 | } | |
| 67 | 73 | if document.text == text { |
| 68 | textView.setSelectedRange(selected) | |
| 74 | textView.selectedRanges = selected | |
| 69 | 75 | visualCaret = cursor |
| 70 | 76 | } else { |
| 71 | 77 | vim.reset() |
| @@ -91,7 +97,7 @@ extension OrgEditor { | ||
| 91 | 97 | } |
| 92 | 98 | } |
| 93 | 99 | let wasInsert = vim.mode == .insert |
| 94 | let visual = vim.mode == .visual || vim.mode == .visualLine | |
| 100 | let visual = vim.mode == .visual || vim.mode == .visualLine || vim.mode == .visualBlock | |
| 95 | 101 | let caret = visual ? (visualCaret ?? textView.selectedRange().location) : textView.selectedRange().location |
| 96 | 102 | if jumpToFileMark(key) { continue } |
| 97 | 103 | let clipboard = NSPasteboard.general.string(forType: .string) |
Tests/OrgEditorAppKitTests/ModalTests.swift +29
| @@ -161,6 +161,35 @@ struct ModalTests { | ||
| 161 | 161 | #expect(lines.editor.vim.mode == .normal) |
| 162 | 162 | } |
| 163 | 163 | |
| 164 | /// Evil turns `transient-mark-mode` off in visual block state, so M-q there fills the | |
| 165 | /// paragraph at the cursor (checked against evil with `org-fill-paragraph`). | |
| 166 | @Test func fillParagraphInVisualBlockFillsAtTheCursor() { | |
| 167 | let h = doom("aaa\nbbb\n\nccc\nddd\n\neee\nfff\n") | |
| 168 | h.caret(at: 0) | |
| 169 | type(h, "C-v j j j") | |
| 170 | #expect(h.textView.selectedRanges.first?.rangeValue == NSRange(location: 0, length: 1) && h.textView.selectedRanges.last?.rangeValue == NSRange(location: 9, length: 1)) | |
| 171 | KeyTests().press(h, "q", option: KeyEvents.leftOption, typed: "œ") | |
| 172 | #expect(h.editor.document.text == "aaa\nbbb\n\nccc ddd\n\neee\nfff\n") | |
| 173 | #expect(h.textView.selectedRange() == NSRange(location: 9, length: 0)) | |
| 174 | #expect(h.editor.vim.mode == .normal) | |
| 175 | let up = doom("aaa\nbbb\nccc\n\nx\ny\n") | |
| 176 | up.caret(at: 8) | |
| 177 | type(up, "C-v k") | |
| 178 | KeyTests().press(up, "q", option: KeyEvents.leftOption, typed: "œ") | |
| 179 | #expect(up.editor.document.text == "aaa bbb ccc\n\nx\ny\n") | |
| 180 | #expect(up.textView.selectedRange() == NSRange(location: 4, length: 0)) | |
| 181 | #expect(up.editor.vim.mode == .normal) | |
| 182 | // Nothing to fill: the block stays. | |
| 183 | let filled = doom("aaa bbb\n\nccc\n") | |
| 184 | filled.caret(at: 0) | |
| 185 | type(filled, "C-v l") | |
| 186 | let block = filled.textView.selectedRanges | |
| 187 | KeyTests().press(filled, "q", option: KeyEvents.leftOption, typed: "œ") | |
| 188 | #expect(filled.editor.document.text == "aaa bbb\n\nccc\n") | |
| 189 | #expect(filled.textView.selectedRanges == block) | |
| 190 | #expect(filled.editor.vim.mode == .visualBlock) | |
| 191 | } | |
| 192 | ||
| 164 | 193 | @Test func zzSavesAndClosesAsWq() { |
| 165 | 194 | let h = doom("text\n") |
| 166 | 195 | var app: [String] = [] |