Skip to content

Commit 3e1c5c5

Browse files
Reorder SBQUERY Ps values and replace magic numbers with named constants
Drop Ps=0 (default alias) and reorder to Ps=1 (last command), Ps=2 (last N commands), Ps=3 (in-progress) for a simpler specification. Introduce SBQueryType struct with constexpr named constants to eliminate magic number literals in the handler and tests. Replace raw escape sequences in tests with Function::operator() calls (DECSM, DECRM, DECRQM, SBQUERY) for readability. Signed-off-by: Christian Parpart <christian@parpart.family>
1 parent f4391c3 commit 3e1c5c5

File tree

4 files changed

+48
-37
lines changed

4 files changed

+48
-37
lines changed

docs/vt-extensions/semantic-block-query.md

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,21 @@ When disabled:
3333
- All tracked semantic block data is discarded.
3434
- The query sequence returns an error response.
3535

36-
## Query Syntax
36+
## Query Syntax — SBQUERY
3737

3838
```
3939
CSI > Ps ; Pn b
4040
```
4141

42+
**Mnemonic:** SBQUERY — **S**emantic **B**lock **QUERY**
43+
4244
CSI with `>` leader, final character `b` (for "blocks").
4345

44-
| Ps | Meaning | Pn |
45-
|----|--------------------------------|---------------------|
46-
| 0 | Last completed command block | ignored |
47-
| 1 | Last N completed command blocks| count (default 1) |
48-
| 2 | Current in-progress command | ignored |
46+
| Ps | Meaning | Pn |
47+
|----|-------------------------------- |---------------------|
48+
| 1 | Last completed command block | ignored |
49+
| 2 | Last N completed command blocks | count (default 1) |
50+
| 3 | Current in-progress command | ignored |
4951

5052
## Response Syntax
5153

@@ -95,15 +97,15 @@ That is: `ESC P > 0 b ESC \`
9597
| `prompt` | string | Text content of the prompt region (from Marked line to OutputStart). |
9698
| `output` | string | Text content of the command output region. |
9799
| `exitCode` | integer | From OSC 133;D parameter. `-1` if unknown. |
98-
| `finished` | boolean | `false` for in-progress commands (Ps=2 query), `true` otherwise. |
100+
| `finished` | boolean | `false` for in-progress commands (Ps=3 query), `true` otherwise. |
99101
| `outputLineCount` | integer | Number of output lines in the block. |
100102

101103
Text fields use JSON's native encoding for control characters (e.g. `\u001b` for ESC),
102104
which ensures the payload never contains a raw ST (ESC \) sequence.
103105

104106
## Example Session
105107

106-
```
108+
```sh
107109
# 1. Enable the semantic block protocol
108110
printf '\033[?2034h'
109111

@@ -116,14 +118,14 @@ ls -la
116118
echo hello
117119

118120
# 4. Query the last completed command
119-
printf '\033[>0b'
121+
printf '\033[>1b'
120122
# Response: DCS > 1 b {"version":1,"blocks":[{"command":"echo hello",...}]} ST
121123

122-
# 5. Query the last 3 commands
123-
printf '\033[>1;3b'
124+
# 5. Query the last 3 completed commands
125+
printf '\033[>2;3b'
124126

125127
# 6. Query any in-progress command
126-
printf '\033[>2b'
128+
printf '\033[>3b'
127129

128130
# 7. Disable when done
129131
printf '\033[?2034l'

src/vtbackend/Screen.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3501,14 +3501,14 @@ void Screen<Cell>::handleSemanticBlockQuery(Sequence const& seq)
35013501
return;
35023502
}
35033503

3504-
auto const queryType = seq.param_or(0, 0); // Ps: 0=last, 1=last N, 2=in-progress
3504+
auto const queryType = seq.param_or(0, SBQueryType::LastCommand);
35053505
auto const count = seq.param_or(1, 1); // Pn: count (default 1)
35063506

35073507
auto const& tracker = _terminal->semanticBlockTracker();
35083508
auto const& completedBlocks = tracker.completedBlocks();
35093509

3510-
// Handle in-progress query (Ps=2).
3511-
if (queryType == 2)
3510+
// Handle in-progress query.
3511+
if (queryType == SBQueryType::InProgress)
35123512
{
35133513
auto const& currentBlock = tracker.currentBlock();
35143514
if (!currentBlock || currentBlock->finished)
@@ -3582,8 +3582,8 @@ void Screen<Cell>::handleSemanticBlockQuery(Sequence const& seq)
35823582
return;
35833583
}
35843584

3585-
// Handle last completed command(s) (Ps=0 or Ps=1).
3586-
auto const requestedCount = (queryType == 0) ? 1 : std::max(count, 1);
3585+
// Handle last completed command(s).
3586+
auto const requestedCount = (queryType == SBQueryType::LastCommand) ? 1 : std::max(count, 1);
35873587

35883588
if (completedBlocks.empty())
35893589
{

src/vtbackend/SemanticBlockTracker.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ struct CommandBlockInfo
1717
bool finished = false; ///< True after OSC 133;D received.
1818
};
1919

20+
/// SBQUERY (CSI > Ps ; Pn b) query type parameter values.
21+
struct SBQueryType
22+
{
23+
static constexpr unsigned LastCommand = 1; ///< Last completed command block.
24+
static constexpr unsigned LastNumberOfCommands = 2; ///< Last N completed command blocks (Pn = count).
25+
static constexpr unsigned InProgress = 3; ///< Current in-progress command.
26+
};
27+
2028
/// Tracks semantic command blocks when DEC mode 2034 (Semantic Block Reader Protocol) is enabled.
2129
///
2230
/// This is a standalone tracker owned by Terminal, called from Screen::processShellIntegration()

src/vtbackend/ShellIntegration_test.cpp

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// SPDX-License-Identifier: Apache-2.0
2+
#include <vtbackend/Functions.h>
23
#include <vtbackend/MockTerm.h>
34
#include <vtbackend/SemanticBlockTracker.h>
45
#include <vtbackend/ShellIntegration.h>
@@ -155,7 +156,7 @@ TEST_CASE("SemanticBlockProtocol.LineFlags")
155156
SECTION("OutputStart and CommandEnd flags set when mode 2034 is on")
156157
{
157158
// Enable mode 2034
158-
mc.writeToScreen("\033[?2034h");
159+
mc.writeToScreen(DECSM(2034));
159160

160161
// OSC 133;C should set OutputStart on the current line
161162
mc.writeToScreen("\033]133;C\033\\");
@@ -191,7 +192,7 @@ TEST_CASE("SemanticBlockProtocol.TrackerMetadata")
191192

192193
SECTION("Tracker stores command line and exit code")
193194
{
194-
mc.writeToScreen("\033[?2034h");
195+
mc.writeToScreen(DECSM(2034));
195196

196197
mc.writeToScreen("\033]133;A\033\\");
197198
mc.writeToScreen("$ ");
@@ -209,7 +210,7 @@ TEST_CASE("SemanticBlockProtocol.TrackerMetadata")
209210

210211
SECTION("Tracker disabled clears data")
211212
{
212-
mc.writeToScreen("\033[?2034h");
213+
mc.writeToScreen(DECSM(2034));
213214
mc.writeToScreen("\033]133;A\033\\");
214215
mc.writeToScreen("\033]133;C;cmdline_url=test\033\\");
215216
mc.writeToScreen("\033]133;D;0\033\\");
@@ -218,7 +219,7 @@ TEST_CASE("SemanticBlockProtocol.TrackerMetadata")
218219
CHECK(tracker.currentBlock().has_value());
219220

220221
// Disable mode
221-
mc.writeToScreen("\033[?2034l");
222+
mc.writeToScreen(DECRM(2034));
222223
CHECK_FALSE(tracker.currentBlock().has_value());
223224
CHECK(tracker.completedBlocks().empty());
224225
}
@@ -231,18 +232,18 @@ TEST_CASE("SemanticBlockProtocol.DECRQM")
231232
SECTION("DECRQM reports mode 2034 as reset by default")
232233
{
233234
mc.resetReplyData();
234-
mc.writeToScreen("\033[?2034$p");
235+
mc.writeToScreen(DECRQM(2034));
235236
mc.terminal.flushInput();
236237
// Response should be CSI ? 2034 ; 2 $ y (2 = reset)
237238
CHECK(mc.replyData().find("2034;2$y") != std::string::npos);
238239
}
239240

240241
SECTION("DECRQM reports mode 2034 as set after enabling")
241242
{
242-
mc.writeToScreen("\033[?2034h");
243+
mc.writeToScreen(DECSM(2034));
243244
mc.terminal.flushInput();
244245
mc.resetReplyData();
245-
mc.writeToScreen("\033[?2034$p");
246+
mc.writeToScreen(DECRQM(2034));
246247
mc.terminal.flushInput();
247248
// Response should be CSI ? 2034 ; 1 $ y (1 = set)
248249
CHECK(mc.replyData().find("2034;1$y") != std::string::npos);
@@ -256,8 +257,8 @@ TEST_CASE("SemanticBlockProtocol.QueryDisabled")
256257
SECTION("Query without enabling mode returns error DCS")
257258
{
258259
mc.resetReplyData();
259-
// CSI > 0 b (query last command)
260-
mc.writeToScreen("\033[>0b");
260+
// Query last command
261+
mc.writeToScreen(SBQUERY(SBQueryType::LastCommand));
261262
mc.terminal.flushInput();
262263
// Should get error DCS: ESC P > 0 b ESC backslash
263264
CHECK(mc.replyData().find("\033P>0b\033\\") != std::string::npos);
@@ -269,7 +270,7 @@ TEST_CASE("SemanticBlockProtocol.QueryLastCommand")
269270
auto mc = MockTerm { PageSize { LineCount(25), ColumnCount(80) } };
270271

271272
// Enable mode 2034
272-
mc.writeToScreen("\033[?2034h");
273+
mc.writeToScreen(DECSM(2034));
273274

274275
// Simulate a complete command
275276
simulateCommand(mc, "$ ", "ls", "file1\nfile2", 0);
@@ -279,8 +280,8 @@ TEST_CASE("SemanticBlockProtocol.QueryLastCommand")
279280

280281
mc.terminal.flushInput();
281282
mc.resetReplyData();
282-
// Query last command: CSI > 0 b
283-
mc.writeToScreen("\033[>0b");
283+
// Query last command
284+
mc.writeToScreen(SBQUERY(SBQueryType::LastCommand));
284285
mc.terminal.flushInput();
285286

286287
auto const& reply = mc.replyData();
@@ -302,7 +303,7 @@ TEST_CASE("SemanticBlockProtocol.QueryLastNCommands")
302303
auto mc = MockTerm { PageSize { LineCount(25), ColumnCount(80) } };
303304

304305
// Enable mode 2034
305-
mc.writeToScreen("\033[?2034h");
306+
mc.writeToScreen(DECSM(2034));
306307

307308
// Simulate two commands
308309
simulateCommand(mc, "$ ", "cmd1", "out1", 0);
@@ -312,8 +313,8 @@ TEST_CASE("SemanticBlockProtocol.QueryLastNCommands")
312313
mc.writeToScreen("\033]133;A\033\\");
313314

314315
mc.resetReplyData();
315-
// Query last 2 commands: CSI > 1 ; 2 b
316-
mc.writeToScreen("\033[>1;2b");
316+
// Query last 2 commands
317+
mc.writeToScreen(SBQUERY(SBQueryType::LastNumberOfCommands, 2));
317318
mc.terminal.flushInput();
318319

319320
auto const& reply = mc.replyData();
@@ -328,7 +329,7 @@ TEST_CASE("SemanticBlockProtocol.QueryInProgress")
328329
auto mc = MockTerm { PageSize { LineCount(25), ColumnCount(80) } };
329330

330331
// Enable mode 2034
331-
mc.writeToScreen("\033[?2034h");
332+
mc.writeToScreen(DECSM(2034));
332333

333334
// Start a command but don't finish it (no OSC 133;D)
334335
mc.writeToScreen("\033]133;A\033\\");
@@ -338,8 +339,8 @@ TEST_CASE("SemanticBlockProtocol.QueryInProgress")
338339
mc.writeToScreen("partial output");
339340

340341
mc.resetReplyData();
341-
// Query in-progress command: CSI > 2 b
342-
mc.writeToScreen("\033[>2b");
342+
// Query in-progress command
343+
mc.writeToScreen(SBQUERY(SBQueryType::InProgress));
343344
mc.terminal.flushInput();
344345

345346
auto const& reply = mc.replyData();
@@ -355,11 +356,11 @@ TEST_CASE("SemanticBlockProtocol.NoCompletedCommands")
355356
auto mc = MockTerm { PageSize { LineCount(25), ColumnCount(80) } };
356357

357358
// Enable mode 2034
358-
mc.writeToScreen("\033[?2034h");
359+
mc.writeToScreen(DECSM(2034));
359360

360361
mc.resetReplyData();
361362
// Query last command when none exists
362-
mc.writeToScreen("\033[>0b");
363+
mc.writeToScreen(SBQUERY(SBQueryType::LastCommand));
363364
mc.terminal.flushInput();
364365

365366
auto const& reply = mc.replyData();

0 commit comments

Comments
 (0)