Commit e4003d118e
Verified · cmc
Layout: unified · split
Sources/OrgCore/Commands/CtrlCCtrlC.swift +13 −3
| @@ -3,7 +3,8 @@ import Foundation | |||
| 3 | // C-c C-c (`org-ctrl-c-ctrl-c`, Org 9.8.7) for the contexts no more specific binding takes: | 3 | // C-c C-c (`org-ctrl-c-ctrl-c`, Org 9.8.7) for the contexts no more specific binding takes: |
| 4 | // headings set tags, clock lines get their duration again, timestamps their weekday, cookies | 4 | // headings set tags, clock lines get their duration again, timestamps their weekday, cookies |
| 5 | // update, items toggle their checkbox, or without one have their list repaired, setup keywords | 5 | // update, items toggle their checkbox, or without one have their list repaired, setup keywords |
| 6 | // refresh, and elsewhere there's nothing to do. | 6 | // refresh, tables evaluate a typed formula, then align or recalculate a row marked `#`, and |
| 7 | // elsewhere there's nothing to do. | ||
| 7 | 8 | ||
| 8 | /// `org-ctrl-c-ctrl-c`. | 9 | /// `org-ctrl-c-ctrl-c`. |
| 9 | public struct CtrlCCtrlC: OrgCommand { | 10 | public struct CtrlCCtrlC: OrgCommand { |
| @@ -20,10 +21,19 @@ public struct CtrlCCtrlC: OrgCommand { | |||
| 20 | if KeyContext.holds("table", in: context) || TableRecalculate.tblfmLine(in: context) != nil { | 21 | if KeyContext.holds("table", in: context) || TableRecalculate.tblfmLine(in: context) != nil { |
| 21 | // At the table's very start the context is the table itself, which org recalculates. | 22 | // At the table's very start the context is the table itself, which org recalculates. |
| 22 | let atStart = buffer.saveExcursion { buffer.tableBegin() } == context.caret | 23 | let atStart = buffer.saveExcursion { buffer.tableBegin() } == context.caret |
| 23 | return TableRecalculate.tblfmLine(in: context) != nil || atStart ? TableRecalculate(all: true).run(in: context) : commitBuffer(context) { buffer in | 24 | if TableRecalculate.tblfmLine(in: context) != nil || atStart { return TableRecalculate(all: true).run(in: context) } |
| 25 | // `org-table-maybe-eval-formula`, then `org-table-maybe-recalculate-line` on a row | ||
| 26 | // marked `#`, which aligns, or else `org-table-align`. | ||
| 27 | let step = commitBuffer(context) { buffer in | ||
| 24 | try buffer.tableMaybeEvalFormula() | 28 | try buffer.tableMaybeEvalFormula() |
| 25 | buffer.tableAlign() | 29 | guard buffer.lookingAtLine("^[ \\t]*\\| *# *(\\||$)") else { return buffer.tableAlign() } |
| 30 | do { | ||
| 31 | try buffer.tableRecalculate(all: false) | ||
| 32 | } catch let error as Calc.Unsupported { | ||
| 33 | throw EmacsBuffer.UserError(message: TableRecalculate.emacsMessage + error.reason) | ||
| 34 | } | ||
| 26 | } | 35 | } |
| 36 | return TableRecalculate.inEmacs(step, form: "(org-ctrl-c-ctrl-c)", in: context) | ||
| 27 | } | 37 | } |
| 28 | if buffer.footnoteReferenceAtPoint() != nil | 38 | if buffer.footnoteReferenceAtPoint() != nil |
| 29 | || buffer.footnoteDefinitions().contains(where: { $0.begin <= context.caret && context.caret < ($0.contentsBegin ?? $0.end) }) { | 39 | || buffer.footnoteDefinitions().contains(where: { $0.begin <= context.caret && context.caret < ($0.contentsBegin ?? $0.end) }) { |
Sources/OrgCore/Compute/TableFormulas.swift +11 −6
| @@ -950,19 +950,24 @@ public struct TableRecalculate: OrgCommand { | |||
| 950 | throw EmacsBuffer.UserError(message: Self.emacsMessage + error.reason) | 950 | throw EmacsBuffer.UserError(message: Self.emacsMessage + error.reason) |
| 951 | } | 951 | } |
| 952 | } | 952 | } |
| 953 | guard case .failed(let message) = step, message.hasPrefix(Self.emacsMessage) else { return step } | 953 | let form = Self.tblfmLine(in: context) != nil ? "(org-table-calc-current-TBLFM)" : all ? "(org-table-recalculate t)" : "(org-table-recalculate nil)" |
| 954 | // Emacs recalculates the table; its text replaces the table here. | 954 | return Self.inEmacs(step, form: form, in: context) |
| 955 | } | ||
| 956 | |||
| 957 | /// A step that failed with `emacsMessage` as a request to run `form` on the table in | ||
| 958 | /// Emacs, whose text then replaces the table here; other steps unchanged. | ||
| 959 | static func inEmacs(_ step: CommandStep, form: String, in context: EditContext) -> CommandStep { | ||
| 960 | guard case .failed(let message) = step, message.hasPrefix(emacsMessage) else { return step } | ||
| 955 | let buffer = EmacsBuffer(context.text, point: context.caret, settings: context.tree.settings, options: context.options) | 961 | let buffer = EmacsBuffer(context.text, point: context.caret, settings: context.tree.settings, options: context.options) |
| 956 | let onTblfm = Self.tblfmLine(in: context) != nil | 962 | let onTblfm = tblfmLine(in: context) != nil |
| 957 | if onTblfm { | 963 | if onTblfm { |
| 958 | while buffer.forwardLine(-1) == 0, !buffer.lookingAtLine("^[ \\t]*\\|") {} | 964 | while buffer.forwardLine(-1) == 0, !buffer.lookingAtLine("^[ \\t]*\\|") {} |
| 959 | } | 965 | } |
| 960 | let table = buffer.tableBegin()..<buffer.tableEnd() | 966 | let table = buffer.tableBegin()..<buffer.tableEnd() |
| 961 | let tblfm = onTblfm ? Self.tblfmLine(in: context) ?? "" : buffer.firstTblfm(after: table.upperBound) ?? "" | 967 | let tblfm = onTblfm ? tblfmLine(in: context) ?? "" : buffer.firstTblfm(after: table.upperBound) ?? "" |
| 962 | let form = onTblfm ? "(org-table-calc-current-TBLFM)" : all ? "(org-table-recalculate t)" : "(org-table-recalculate nil)" | ||
| 963 | return .external(.emacsTable( | 968 | return .external(.emacsTable( |
| 964 | table: table, point: context.caret, form: form, | 969 | table: table, point: context.caret, form: form, |
| 965 | reason: String(message.dropFirst(Self.emacsMessage.count)), lisp: tblfm.contains("'(") | 970 | reason: String(message.dropFirst(emacsMessage.count)), lisp: tblfm.contains("'(") |
| 966 | )) | 971 | )) |
| 967 | } | 972 | } |
| 968 | } | 973 | } |
Sources/OrgCore/Keymap/Presets.swift +1 −1
| @@ -998,7 +998,7 @@ public enum KeymapPreset: String, Sendable, CaseIterable { | |||
| 998 | command = "org.fill-paragraph" | 998 | command = "org.fill-paragraph" |
| 999 | [[bind]] | 999 | [[bind]] |
| 1000 | keys = "C-c C-c" | 1000 | keys = "C-c C-c" |
| 1001 | command = "org.table.align" | 1001 | command = "org.ctrl-c-ctrl-c" |
| 1002 | when = "table" | 1002 | when = "table" |
| 1003 | [[bind]] | 1003 | [[bind]] |
| 1004 | keys = "C-c C-c" | 1004 | keys = "C-c C-c" |
Tests/OrgCoreTests/KeymapTests.swift +1 −1
| @@ -212,6 +212,6 @@ struct CtrlCCtrlCKeyTests { | |||
| 212 | } | 212 | } |
| 213 | #expect(command(at: 1) == "org.ctrl-c-ctrl-c") | 213 | #expect(command(at: 1) == "org.ctrl-c-ctrl-c") |
| 214 | #expect(command(at: 5) == "org.ctrl-c-ctrl-c") | 214 | #expect(command(at: 5) == "org.ctrl-c-ctrl-c") |
| 215 | #expect(command(at: 13) == "org.table.align") | 215 | #expect(command(at: 13) == "org.ctrl-c-ctrl-c") |
| 216 | } | 216 | } |
| 217 | } | 217 | } |
Tests/OrgCoreTests/TableFormulaEntryTests.swift +5
| @@ -13,6 +13,8 @@ struct TableFormulaEntryTests { | |||
| 13 | let replaced = "| a | b |\n| 1 | =$1*5 |\n#+TBLFM: $2=$1*2::@1$2=x\n" | 13 | let replaced = "| a | b |\n| 1 | =$1*5 |\n#+TBLFM: $2=$1*2::@1$2=x\n" |
| 14 | let reuse = "| a | b |\n| 2 | = |\n#+TBLFM: $2=$1*2\n\nText\n" | 14 | let reuse = "| a | b |\n| 2 | = |\n#+TBLFM: $2=$1*2\n\nText\n" |
| 15 | let last = "| 1 | =$1+1 |\n" | 15 | let last = "| 1 | =$1+1 |\n" |
| 16 | let marked = "| | x | y |\n|---+---+---|\n| # | 2 | |\n| | 3 | |\n#+TBLFM: $3=$2*10\n" | ||
| 17 | let markedTyped = "| # | 2 | =$2+1 | |\n| # | 5 | | |\n#+TBLFM: $4=$2*2\n" | ||
| 16 | let prompt = { (arg: String) in "(cl-letf (((symbol-function 'read-string) (lambda (&rest _) \"\"))) (org-table-eval-formula \(arg)))" } | 18 | let prompt = { (arg: String) in "(cl-letf (((symbol-function 'read-string) (lambda (&rest _) \"\"))) (org-table-eval-formula \(arg)))" } |
| 17 | let cases: [(String, String, any OrgCommand, [String: String], String)] = [ | 19 | let cases: [(String, String, any OrgCommand, [String: String], String)] = [ |
| 18 | (plain, "*2", TableCommand(.nextField), [:], "(org-table-next-field)"), | 20 | (plain, "*2", TableCommand(.nextField), [:], "(org-table-next-field)"), |
| @@ -26,6 +28,9 @@ struct TableFormulaEntryTests { | |||
| 26 | (stored, "| 3", TableEvalFormula(named: false), ["formula": "$1*3"], "(org-table-eval-formula nil \"$1*3\")"), | 28 | (stored, "| 3", TableEvalFormula(named: false), ["formula": "$1*3"], "(org-table-eval-formula nil \"$1*3\")"), |
| 27 | (stored, "| 3", TableEvalFormula(named: true), ["formula": "99"], "(org-table-eval-formula '(4) \"99\")"), | 29 | (stored, "| 3", TableEvalFormula(named: true), ["formula": "99"], "(org-table-eval-formula '(4) \"99\")"), |
| 28 | (stored, "| 1 | 2", CtrlCCtrlC(), [:], "(org-ctrl-c-ctrl-c)"), | 30 | (stored, "| 1 | 2", CtrlCCtrlC(), [:], "(org-ctrl-c-ctrl-c)"), |
| 31 | (marked, "| 2", CtrlCCtrlC(), [:], "(org-ctrl-c-ctrl-c)"), | ||
| 32 | (marked, "| 3", CtrlCCtrlC(), [:], "(org-ctrl-c-ctrl-c)"), | ||
| 33 | (markedTyped, "+1", CtrlCCtrlC(), [:], "(org-ctrl-c-ctrl-c)"), | ||
| 29 | (stored, "| 3 | 4", TableEvalFormula(named: false), ["formula": ""], prompt("nil")), | 34 | (stored, "| 3 | 4", TableEvalFormula(named: false), ["formula": ""], prompt("nil")), |
| 30 | (stored, "| 1", TableEvalFormula(named: true), ["formula": ""], prompt("'(4)")), | 35 | (stored, "| 1", TableEvalFormula(named: true), ["formula": ""], prompt("'(4)")), |
| 31 | ] | 36 | ] |