diff --git a/README.md b/README.md index 3ad8fda..8d4b643 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ Deferred manual/runtime verification (implemented after 2026-02-20 while user un - `SetNetClasses` - `SetTextVariables` - `UpdateBoardStackup` +- `InteractiveMoveItems` ## KiCad v10 RC1.1 API Completion Matrix @@ -68,9 +69,9 @@ Legend: | Common (base) | 6 | 6 | 100% | | Common editor/document | 23 | 23 | 100% | | Project manager | 5 | 5 | 100% | -| Board editor (PCB) | 22 | 21 | 95% | +| Board editor (PCB) | 22 | 22 | 100% | | Schematic editor (dedicated proto commands) | 0 | 0 | n/a | -| **Total** | **56** | **55** | **98%** | +| **Total** | **56** | **56** | **100%** | ### Common (base) @@ -146,7 +147,7 @@ Legend: | `SetActiveLayer` | Implemented | `KiCadClient::set_active_layer` | | `GetBoardEditorAppearanceSettings` | Implemented | `KiCadClient::get_board_editor_appearance_settings_raw`, `KiCadClient::get_board_editor_appearance_settings` | | `SetBoardEditorAppearanceSettings` | Implemented | `KiCadClient::set_board_editor_appearance_settings` | -| `InteractiveMoveItems` | Not yet | - | +| `InteractiveMoveItems` | Implemented | `KiCadClient::interactive_move_items_raw`, `KiCadClient::interactive_move_items` | ### Schematic editor diff --git a/docs/TEST_CLI.md b/docs/TEST_CLI.md index b9b7943..ab0fe02 100644 --- a/docs/TEST_CLI.md +++ b/docs/TEST_CLI.md @@ -364,6 +364,12 @@ Refill all zones: cargo run --bin kicad-ipc-cli -- refill-zones ``` +Start interactive move tool for one or more item IDs: + +```bash +cargo run --bin kicad-ipc-cli -- interactive-move --id --id +``` + Show typed netclass map: ```bash diff --git a/src/client.rs b/src/client.rs index a4e2480..10d7a42 100644 --- a/src/client.rs +++ b/src/client.rs @@ -65,6 +65,7 @@ const CMD_GET_BOARD_EDITOR_APPEARANCE_SETTINGS: &str = "kiapi.board.commands.GetBoardEditorAppearanceSettings"; const CMD_SET_BOARD_EDITOR_APPEARANCE_SETTINGS: &str = "kiapi.board.commands.SetBoardEditorAppearanceSettings"; +const CMD_INTERACTIVE_MOVE_ITEMS: &str = "kiapi.board.commands.InteractiveMoveItems"; const CMD_GET_ITEMS_BY_NET: &str = "kiapi.board.commands.GetItemsByNet"; const CMD_GET_ITEMS_BY_NET_CLASS: &str = "kiapi.board.commands.GetItemsByNetClass"; const CMD_GET_NETCLASS_FOR_NETS: &str = "kiapi.board.commands.GetNetClassForNets"; @@ -1612,6 +1613,35 @@ impl KiCadClient { self.get_board_editor_appearance_settings().await } + pub async fn interactive_move_items_raw( + &self, + item_ids: Vec, + ) -> Result { + if item_ids.is_empty() { + return Err(KiCadError::Config { + reason: "interactive_move_items_raw requires at least one item id".to_string(), + }); + } + + let command = board_commands::InteractiveMoveItems { + board: Some(self.current_board_document_proto().await?), + items: item_ids + .into_iter() + .map(|value| common_types::Kiid { value }) + .collect(), + }; + + let response = self + .send_command(envelope::pack_any(&command, CMD_INTERACTIVE_MOVE_ITEMS)) + .await?; + response_payload_as_any(response, RES_PROTOBUF_EMPTY) + } + + pub async fn interactive_move_items(&self, item_ids: Vec) -> Result<(), KiCadError> { + let _ = self.interactive_move_items_raw(item_ids).await?; + Ok(()) + } + pub async fn get_title_block_info(&self) -> Result { let command = common_commands::GetTitleBlockInfo { document: Some(self.current_board_document_proto().await?), diff --git a/test-scripts/kicad-ipc-cli.rs b/test-scripts/kicad-ipc-cli.rs index 2821748..942b6cd 100644 --- a/test-scripts/kicad-ipc-cli.rs +++ b/test-scripts/kicad-ipc-cli.rs @@ -178,6 +178,9 @@ enum Command { RefillZones { zone_ids: Vec, }, + InteractiveMoveItems { + item_ids: Vec, + }, NetClass, BoardReadReport { output: PathBuf, @@ -874,6 +877,10 @@ async fn run() -> Result<(), KiCadError> { client.refill_zones(zone_ids).await?; println!("refill_zones_dispatched=ok"); } + Command::InteractiveMoveItems { item_ids } => { + client.interactive_move_items(item_ids.clone()).await?; + println!("interactive_move_item_count={}", item_ids.len()); + } Command::NetClass => { let nets = client.get_nets().await?; let netclasses = client.get_netclass_for_nets(nets).await?; @@ -1938,6 +1945,28 @@ fn parse_args_from(mut args: Vec) -> Result<(CliConfig, Command), KiCadE } Command::RefillZones { zone_ids } } + "interactive-move" => { + let mut item_ids = Vec::new(); + let mut i = 1; + while i < args.len() { + if args[i] == "--id" { + let value = args.get(i + 1).ok_or_else(|| KiCadError::Config { + reason: "missing value for interactive-move --id".to_string(), + })?; + item_ids.push(value.clone()); + i += 2; + continue; + } + i += 1; + } + if item_ids.is_empty() { + return Err(KiCadError::Config { + reason: "interactive-move requires one or more `--id ` arguments" + .to_string(), + }); + } + Command::InteractiveMoveItems { item_ids } + } "netclass" => Command::NetClass, "proto-coverage-board-read" => Command::ProtoCoverageBoardRead, "board-read-report" => { @@ -2053,7 +2082,110 @@ fn default_config() -> CliConfig { fn print_help() { println!( - "kicad-ipc-cli\n\nUSAGE:\n cargo run --bin kicad-ipc-cli -- [--socket URI] [--token TOKEN] [--client-name NAME] [--timeout-ms N] [command options]\n\nCOMMANDS:\n ping Check IPC connectivity\n version Fetch KiCad version\n kicad-binary-path [--binary-name ]\n Resolve absolute path for a KiCad binary (default: kicad-cli)\n plugin-settings-path [--identifier ]\n Resolve writeable plugin settings directory (default: kicad-ipc-rust)\n open-docs [--type ] List open docs (default type: pcb)\n project-path Get current project path from open PCB docs\n board-open Exit non-zero if no PCB doc is open\n net-classes List project netclass definitions\n set-net-classes [--merge-mode ]\n Write current netclass set back with selected merge mode\n text-variables List text variables for current board document\n set-text-variables [--merge-mode ] [--var ...]\n Set text variables for current board document\n expand-text-variables Expand variables in provided text values\n Options: --text (repeatable)\n text-extents Measure text bounding box\n Options: --text \n text-as-shapes Convert text to rendered shapes\n Options: --text (repeatable)\n nets List board nets (requires one open PCB)\n netlist-pads Emit pad-level netlist data (with footprint context)\n items-by-id --id ... Show parsed details for specific item IDs\n item-bbox --id ... Show bounding boxes for item IDs\n hit-test --id --x-nm --y-nm [--tolerance-nm ]\n Hit-test one item at a point\n types-pcb List PCB KiCad object type IDs from proto enum\n items-raw --type-id ... Dump raw Any payloads for requested item type IDs\n items-raw-all-pcb [--debug] Dump all PCB item payloads across all PCB object types\n pad-shape-polygon --pad-id ... --layer-id [--debug]\n Dump pad polygons on a target layer\n padstack-presence --item-id ... --layer-id ... [--debug]\n Check padstack shape presence matrix across layers\n title-block Show title block fields\n board-as-string Dump board as KiCad s-expression text\n selection-as-string Dump current selection as KiCad s-expression text\n stackup Show typed board stackup\n update-stackup Round-trip current stackup through UpdateBoardStackup\n graphics-defaults Show typed graphics defaults\n appearance Show typed editor appearance settings\n set-appearance --inactive-layer-display \n --net-color-display \n --board-flip \n --ratsnest-display \n Set editor appearance settings\n inject-drc-error --severity --message [--x-nm --y-nm ] [--item-id ...]\n Inject a DRC marker (severity: warning|error|exclusion|ignore|info|action|debug|undefined)\n refill-zones [--zone-id ...]\n Refill all zones or a provided subset netclass Show typed netclass map for current board nets\n proto-coverage-board-read Print board-read command coverage vs proto\n board-read-report [--out P] Write markdown board reconstruction report\n enabled-layers List enabled board layers\n set-enabled-layers --copper-layer-count [--layer-id ...]\n Set enabled board layer set\n active-layer Show active board layer\n set-active-layer --layer-id \n Set active board layer\n visible-layers Show currently visible board layers\n set-visible-layers --layer-id ...\n Set visible board layers\n board-origin [--type ] Show board origin (`grid` default, or `drill`)\n set-board-origin --type --x-nm --y-nm \n Set board origin (`grid` or `drill`)\n refresh-editor [--frame ] Refresh a specific editor frame (default: pcb)\n begin-commit Start staged commit and print commit ID\n end-commit --id [--action ] [--message ]\n End staged commit with commit/drop action\n save-doc Save current board document\n save-copy --path [--overwrite] [--include-project]\n Save current board document to a new location\n revert-doc Revert current board document from disk\n run-action --action Run a raw KiCad tool action\n create-items --item = ... [--container-id ]\n Create raw Any payload items in current board document\n update-items --item = ...\n Update raw Any payload items in current board document\n delete-items --id ...\n Delete item IDs from current board document\n parse-create-items --contents \n Parse s-expression and create resulting items\n add-to-selection --id ...\n Add items to current selection\n remove-from-selection --id ...\n Remove items from current selection\n clear-selection Clear current item selection\n selection-summary Show current selection item type counts\n selection-details Show parsed details for selected items\n selection-raw Show raw Any payload bytes for selected items\n smoke ping + version + board-open summary\n help Show help\n\nTYPES:\n schematic | symbol | pcb | footprint | drawing-sheet | project\n" + r#"kicad-ipc-cli + +USAGE: + cargo run --bin kicad-ipc-cli -- [--socket URI] [--token TOKEN] [--client-name NAME] [--timeout-ms N] [command options] + +COMMANDS: + ping Check IPC connectivity + version Fetch KiCad version + kicad-binary-path [--binary-name ] + Resolve absolute path for a KiCad binary (default: kicad-cli) + plugin-settings-path [--identifier ] + Resolve writeable plugin settings directory (default: kicad-ipc-rust) + open-docs [--type ] List open docs (default type: pcb) + project-path Get current project path from open PCB docs + board-open Exit non-zero if no PCB doc is open + net-classes List project netclass definitions + set-net-classes [--merge-mode ] + Write current netclass set back with selected merge mode + text-variables List text variables for current board document + set-text-variables [--merge-mode ] [--var ...] + Set text variables for current board document + expand-text-variables Expand variables in provided text values + Options: --text (repeatable) + text-extents Measure text bounding box + Options: --text + text-as-shapes Convert text to rendered shapes + Options: --text (repeatable) + nets List board nets (requires one open PCB) + netlist-pads Emit pad-level netlist data (with footprint context) + items-by-id --id ... Show parsed details for specific item IDs + item-bbox --id ... Show bounding boxes for item IDs + hit-test --id --x-nm --y-nm [--tolerance-nm ] + Hit-test one item at a point + types-pcb List PCB KiCad object type IDs from proto enum + items-raw --type-id ... Dump raw Any payloads for requested item type IDs + items-raw-all-pcb [--debug] Dump all PCB item payloads across all PCB object types + pad-shape-polygon --pad-id ... --layer-id [--debug] + Dump pad polygons on a target layer + padstack-presence --item-id ... --layer-id ... [--debug] + Check padstack shape presence matrix across layers + title-block Show title block fields + board-as-string Dump board as KiCad s-expression text + selection-as-string Dump current selection as KiCad s-expression text + stackup Show typed board stackup + update-stackup Round-trip current stackup through UpdateBoardStackup + graphics-defaults Show typed graphics defaults + appearance Show typed editor appearance settings + set-appearance --inactive-layer-display + --net-color-display + --board-flip + --ratsnest-display + Set editor appearance settings + inject-drc-error --severity --message [--x-nm --y-nm ] [--item-id ...] + Inject a DRC marker (severity: warning|error|exclusion|ignore|info|action|debug|undefined) + refill-zones [--zone-id ...] + Refill all zones or a provided subset + interactive-move --id ... + Start interactive move tool for item IDs + netclass Show typed netclass map for current board nets + proto-coverage-board-read Print board-read command coverage vs proto + board-read-report [--out P] Write markdown board reconstruction report + enabled-layers List enabled board layers + set-enabled-layers --copper-layer-count [--layer-id ...] + Set enabled board layer set + active-layer Show active board layer + set-active-layer --layer-id + Set active board layer + visible-layers Show currently visible board layers + set-visible-layers --layer-id ... + Set visible board layers + board-origin [--type ] Show board origin (`grid` default, or `drill`) + set-board-origin --type --x-nm --y-nm + Set board origin (`grid` or `drill`) + refresh-editor [--frame ] Refresh a specific editor frame (default: pcb) + begin-commit Start staged commit and print commit ID + end-commit --id [--action ] [--message ] + End staged commit with commit/drop action + save-doc Save current board document + save-copy --path [--overwrite] [--include-project] + Save current board document to a new location + revert-doc Revert current board document from disk + run-action --action Run a raw KiCad tool action + create-items --item = ... [--container-id ] + Create raw Any payload items in current board document + update-items --item = ... + Update raw Any payload items in current board document + delete-items --id ... + Delete item IDs from current board document + parse-create-items --contents + Parse s-expression and create resulting items + add-to-selection --id ... + Add items to current selection + remove-from-selection --id ... + Remove items from current selection + clear-selection Clear current item selection + selection-summary Show current selection item type counts + selection-details Show parsed details for selected items + selection-raw Show raw Any payload bytes for selected items + smoke ping + version + board-open summary + help Show help + +TYPES: + schematic | symbol | pcb | footprint | drawing-sheet | project +"# ); } @@ -3123,4 +3255,23 @@ mod tests { .expect("update-stackup should parse"); assert!(matches!(command, Command::UpdateStackup)); } + + #[test] + fn parse_args_parses_interactive_move_items() { + let (_, command) = parse_args_from(vec![ + "interactive-move".to_string(), + "--id".to_string(), + "item-1".to_string(), + "--id".to_string(), + "item-2".to_string(), + ]) + .expect("interactive-move args should parse"); + + match command { + Command::InteractiveMoveItems { item_ids } => { + assert_eq!(item_ids, vec!["item-1".to_string(), "item-2".to_string()]); + } + other => panic!("unexpected command variant: {other:?}"), + } + } }