|
278 | 278 | ></pre> |
279 | 279 | <v-menu v-model="executeSelectionMenu" :target="[menuX, menuY]"> |
280 | 280 | <v-list> |
| 281 | + <v-list-item |
| 282 | + :title="currentLineHasCommand ? 'Edit Command' : 'Insert Command'" |
| 283 | + @click="openCommandEditor" |
| 284 | + /> |
| 285 | + <v-divider /> |
281 | 286 | <v-list-item title="Execute Selection" @click="executeSelection" /> |
282 | 287 | <v-list-item |
283 | 288 | v-if="scriptId" |
|
520 | 525 | :persistent="true" |
521 | 526 | @status="promptDialogCallback" |
522 | 527 | /> |
| 528 | + <!-- Command Editor Dialog --> |
| 529 | + <v-dialog v-model="commandEditor.show" max-width="1200" persistent scrollable> |
| 530 | + <v-card> |
| 531 | + <v-card-title class="d-flex align-center"> |
| 532 | + <span>Insert Command</span> |
| 533 | + <v-spacer /> |
| 534 | + <v-btn icon="mdi-close" variant="text" @click="closeCommandDialog" /> |
| 535 | + </v-card-title> |
| 536 | + <v-card-text class="pa-0"> |
| 537 | + <div v-if="commandEditor.dialogError" class="error-message"> |
| 538 | + <v-icon class="mr-2" color="error">mdi-alert-circle</v-icon> |
| 539 | + <span class="flex-grow-1">{{ commandEditor.dialogError }}</span> |
| 540 | + <v-btn |
| 541 | + icon="mdi-close" |
| 542 | + size="small" |
| 543 | + variant="text" |
| 544 | + color="error" |
| 545 | + @click="commandEditor.dialogError = null" |
| 546 | + class="ml-2" |
| 547 | + /> |
| 548 | + </div> |
| 549 | + <command-editor |
| 550 | + ref="commandEditor" |
| 551 | + :initial-target-name="commandEditor.targetName" |
| 552 | + :initial-packet-name="commandEditor.packetName" |
| 553 | + :cmd-string="commandEditor.cmdString" |
| 554 | + :send-disabled="false" |
| 555 | + :show-command-button="false" |
| 556 | + @build-cmd="insertCommand($event)" |
| 557 | + /> |
| 558 | + </v-card-text> |
| 559 | + <v-card-actions> |
| 560 | + <v-spacer /> |
| 561 | + <v-btn variant="outlined" @click="closeCommandDialog"> Cancel </v-btn> |
| 562 | + <v-btn color="primary" variant="flat" @click="insertCommand()"> |
| 563 | + Insert Command |
| 564 | + </v-btn> |
| 565 | + </v-card-actions> |
| 566 | + </v-card> |
| 567 | + </v-dialog> |
523 | 568 | <v-bottom-sheet v-model="showScripts"> |
524 | 569 | <v-sheet class="pb-11 pt-5 px-5"> |
525 | 570 | <running-scripts |
@@ -564,6 +609,7 @@ import OverridesDialog from '@/tools/scriptrunner/Dialogs/OverridesDialog.vue' |
564 | 609 | import PromptDialog from '@/tools/scriptrunner/Dialogs/PromptDialog.vue' |
565 | 610 | import ResultsDialog from '@/tools/scriptrunner/Dialogs/ResultsDialog.vue' |
566 | 611 | import ScriptEnvironmentDialog from '@/tools/scriptrunner/Dialogs/ScriptEnvironmentDialog.vue' |
| 612 | +import CommandEditor from '@/components/CommandEditor.vue' |
567 | 613 | import SuiteRunner from '@/tools/scriptrunner/SuiteRunner.vue' |
568 | 614 | import ScriptLogMessages from '@/tools/scriptrunner/ScriptLogMessages.vue' |
569 | 615 | import { |
@@ -603,6 +649,7 @@ export default { |
603 | 649 | RunningScripts, |
604 | 650 | ScriptLogMessages, |
605 | 651 | CriticalCmdDialog, |
| 652 | + CommandEditor, |
606 | 653 | }, |
607 | 654 | mixins: [AceEditorModes, ClassificationBanners], |
608 | 655 | beforeRouteUpdate: function (to, from, next) { |
@@ -740,6 +787,16 @@ export default { |
740 | 787 | mnemonicChecker: new MnemonicChecker(), |
741 | 788 | showScripts: false, |
742 | 789 | showOverrides: false, |
| 790 | + commandEditor: { |
| 791 | + show: false, |
| 792 | + targetName: null, |
| 793 | + commandName: null, |
| 794 | + dialogError: null, |
| 795 | + cmdString: null, |
| 796 | + isEditing: false, |
| 797 | + editLine: null, |
| 798 | + }, |
| 799 | + currentLineHasCommand: false, |
743 | 800 | activePromptId: '', |
744 | 801 | api: null, |
745 | 802 | timeZone: 'local', |
@@ -1249,6 +1306,72 @@ export default { |
1249 | 1306 | toggleVimMode() { |
1250 | 1307 | AceEditorUtils.toggleVimMode(this.editor) |
1251 | 1308 | }, |
| 1309 | + openCommandEditor() { |
| 1310 | + this.executeSelectionMenu = false |
| 1311 | + const position = this.editor.getCursorPosition() |
| 1312 | + const line = this.editor.session.getLine(position.row) |
| 1313 | +
|
| 1314 | + if (this.currentLineHasCommand) { |
| 1315 | + // Extract and parse the command from the line |
| 1316 | + const cmdString = this.parseCommandFromLine(line) |
| 1317 | + this.commandEditor.cmdString = cmdString |
| 1318 | + this.commandEditor.isEditing = true |
| 1319 | + this.commandEditor.editLine = position.row |
| 1320 | + } else { |
| 1321 | + // Inserting a new command |
| 1322 | + this.commandEditor.cmdString = null |
| 1323 | + this.commandEditor.isEditing = false |
| 1324 | + this.commandEditor.editLine = null |
| 1325 | + } |
| 1326 | + this.commandEditor.show = true |
| 1327 | + this.commandEditor.dialogError = null |
| 1328 | + }, |
| 1329 | + insertCommand(event) { |
| 1330 | + let commandString = '' |
| 1331 | + try { |
| 1332 | + commandString = this.$refs.commandEditor.getCmdString() |
| 1333 | + let parts = commandString.split(' ') |
| 1334 | + this.commandEditor.targetName = parts[0] |
| 1335 | + this.commandEditor.commandName = parts[1] |
| 1336 | + } catch (error) { |
| 1337 | + this.commandEditor.dialogError = |
| 1338 | + error.message || 'Please fix command parameters' |
| 1339 | + return |
| 1340 | + } |
| 1341 | +
|
| 1342 | + if ( |
| 1343 | + this.commandEditor.isEditing && |
| 1344 | + this.commandEditor.editLine !== null |
| 1345 | + ) { |
| 1346 | + // Replace the existing line |
| 1347 | + const line = this.editor.session.getLine(this.commandEditor.editLine) |
| 1348 | + const indent = line.match(/^\s*/)[0] // Preserve indentation |
| 1349 | + // Extract trailing comment if present |
| 1350 | + const commentMatch = line.match(/\s+#.*$/) |
| 1351 | + const trailingComment = commentMatch ? commentMatch[0] : '' |
| 1352 | + const newLine = `${indent}cmd("${commandString}")${trailingComment}` |
| 1353 | + const Range = this.Range |
| 1354 | + this.editor.session.replace( |
| 1355 | + new Range( |
| 1356 | + this.commandEditor.editLine, |
| 1357 | + 0, |
| 1358 | + this.commandEditor.editLine, |
| 1359 | + line.length, |
| 1360 | + ), |
| 1361 | + newLine, |
| 1362 | + ) |
| 1363 | + } else { |
| 1364 | + // Insert a new command at the cursor position |
| 1365 | + const position = this.editor.getCursorPosition() |
| 1366 | + this.editor.session.insert(position, `cmd("${commandString}")\n`) |
| 1367 | + } |
| 1368 | +
|
| 1369 | + this.fileModified = true |
| 1370 | + this.commandEditor.show = false |
| 1371 | + }, |
| 1372 | + closeCommandDialog: function () { |
| 1373 | + this.commandEditor.show = false |
| 1374 | + }, |
1252 | 1375 | doResize() { |
1253 | 1376 | this.editor.resize() |
1254 | 1377 | // nextTick allows the resize to work correctly |
@@ -1383,8 +1506,26 @@ export default { |
1383 | 1506 | showExecuteSelectionMenu: function ($event) { |
1384 | 1507 | this.menuX = $event.pageX |
1385 | 1508 | this.menuY = $event.pageY |
| 1509 | + // Check if the current line contains a command |
| 1510 | + const position = this.editor.getCursorPosition() |
| 1511 | + const line = this.editor.session.getLine(position.row) |
| 1512 | + this.currentLineHasCommand = this.isCommandLine(line) |
1386 | 1513 | this.executeSelectionMenu = true |
1387 | 1514 | }, |
| 1515 | + isCommandLine: function (line) { |
| 1516 | + // Check if line contains cmd() or cmd_no_hazardous_check() or similar command patterns |
| 1517 | + const trimmedLine = line.trim() |
| 1518 | + // Match patterns like: cmd("...", cmd_no_hazardous_check("...", cmd_raw("...", etc. |
| 1519 | + return /^\s*cmd(_\w+)?\s*\(/.test(trimmedLine) |
| 1520 | + }, |
| 1521 | + parseCommandFromLine: function (line) { |
| 1522 | + // Extract the command string from patterns like: cmd("TARGET COMMAND with PARAM value") |
| 1523 | + const match = line.match(/cmd(_\w+)?\s*\(\s*["'](.+?)["']\s*\)/) |
| 1524 | + if (match) { |
| 1525 | + return match[2] // Return the command string |
| 1526 | + } |
| 1527 | + return null |
| 1528 | + }, |
1388 | 1529 | runFromCursor: function () { |
1389 | 1530 | const start_row = this.editor.getCursorPosition().row + 1 |
1390 | 1531 | if (!this.scriptId) { |
@@ -2739,6 +2880,25 @@ class TestSuite(Suite): |
2739 | 2880 | </script> |
2740 | 2881 |
|
2741 | 2882 | <style scoped> |
| 2883 | +hr { |
| 2884 | + color: white; |
| 2885 | + height: 3px; |
| 2886 | +} |
| 2887 | +
|
| 2888 | +.error-message { |
| 2889 | + border: 2px solid #f44336; |
| 2890 | + border-radius: 8px; |
| 2891 | + background-color: rgba(244, 67, 54, 0.1); |
| 2892 | + color: #d32f2f; |
| 2893 | + padding-left: 8px; |
| 2894 | + padding-right: 8px; |
| 2895 | + margin: 16px; |
| 2896 | + display: flex; |
| 2897 | + align-items: center; |
| 2898 | + font-weight: 500; |
| 2899 | + box-shadow: 0 2px 4px rgba(244, 67, 54, 0.2); |
| 2900 | +} |
| 2901 | +
|
2742 | 2902 | #sr-controls { |
2743 | 2903 | padding: 0px; |
2744 | 2904 | } |
|
0 commit comments