TASK-059: jump from a function in the outline to its counterpart file
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
3c1bd1adb2
commit
aa23f356d9
@ -202,6 +202,8 @@ fun CodeViewerScreen(
|
||||
// The share of the width the left pane of a header/source pair takes.
|
||||
var splitFraction by rememberSaveable { mutableFloatStateOf(DEFAULT_SPLIT) }
|
||||
val drawerState = rememberDrawerState(DrawerValue.Closed)
|
||||
// The sheet is shown from outside the layout below, where the jump is worked out.
|
||||
var jumpForSheet by remember { mutableStateOf<SymbolJump?>(null) }
|
||||
|
||||
// The other half of a header/source pair, once a wide window has asked for it and it has
|
||||
// arrived as text.
|
||||
@ -265,6 +267,27 @@ fun CodeViewerScreen(
|
||||
val sidePaneIsShown = layout == ViewerLayout.WIDE && !showsPair
|
||||
val drawerWidth = (maxWidth * DRAWER_WIDTH_FRACTION).coerceAtMost(MaxDrawerWidth)
|
||||
|
||||
// From a function in the outline to the same function in the other file of the pair: from
|
||||
// a declaration in the header to its definition in the source, and back. Beside each other
|
||||
// they already move together, so there it is not offered.
|
||||
val jump = if (counterpartPath == null || showsPair) {
|
||||
null
|
||||
} else {
|
||||
SymbolJump(
|
||||
label = "." + counterpartPath.value.substringAfterLast('.'),
|
||||
description = stringResource(
|
||||
if (cppRoleOf(counterpartPath) == CppFileRole.HEADER) R.string.codeviewer_jump_to_header else R.string.codeviewer_jump_to_source,
|
||||
counterpartPath.value.substringAfterLast('/'),
|
||||
),
|
||||
onJump = { symbol ->
|
||||
outlineOpen = false
|
||||
scope.launch { drawerState.close() }
|
||||
onOpenSibling(counterpartPath, symbol.name)
|
||||
},
|
||||
)
|
||||
}
|
||||
jumpForSheet = jump
|
||||
|
||||
val tools = ViewerTools(
|
||||
preferences = preferences,
|
||||
searching = searching,
|
||||
@ -310,6 +333,7 @@ fun CodeViewerScreen(
|
||||
onOpenEntry = onOpenEntry,
|
||||
onToggleFolder = onToggleFolder,
|
||||
outlineFirst = outlineFirst,
|
||||
jump = jump,
|
||||
onSwap = swapPanes,
|
||||
onSymbolSelected = {
|
||||
scope.launch { drawerState.close() }
|
||||
@ -384,6 +408,7 @@ fun CodeViewerScreen(
|
||||
onOpenEntry = onOpenEntry,
|
||||
onToggleFolder = onToggleFolder,
|
||||
outlineFirst = outlineFirst,
|
||||
jump = jump,
|
||||
onSwap = swapPanes,
|
||||
onSymbolSelected = { goToSymbol(it) },
|
||||
modifier = Modifier.width(sidePaneWidth).fillMaxHeight(),
|
||||
@ -421,6 +446,7 @@ fun CodeViewerScreen(
|
||||
if (outlineOpen) {
|
||||
OutlineSheet(
|
||||
document = document,
|
||||
jump = jumpForSheet,
|
||||
currentLine = firstVisibleLine,
|
||||
collapsed = collapsed,
|
||||
onToggleCollapsed = toggleCollapsed,
|
||||
@ -437,6 +463,9 @@ fun CodeViewerScreen(
|
||||
}
|
||||
}
|
||||
|
||||
/** Goes from a function to the function of the same name in the other file of a header/source pair. */
|
||||
private class SymbolJump(val label: String, val description: String, val onJump: (CodeSymbol) -> Unit)
|
||||
|
||||
private class ViewerTools(
|
||||
val preferences: CodeViewerPreferences,
|
||||
val searching: Boolean,
|
||||
@ -997,13 +1026,14 @@ private fun SidePane(
|
||||
onSymbolSelected: (CodeSymbol) -> Unit,
|
||||
outlineFirst: Boolean,
|
||||
onSwap: () -> Unit,
|
||||
jump: SymbolJump?,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val outline = remember(document.symbols, collapsed) { outlineTree(document.symbols, collapsed) }
|
||||
val rows = remember(state.tree) { state.tree.rows() }
|
||||
val folders: @Composable (Modifier) -> Unit = { FolderCard(state, rows, onOpenFile, onToggleFolder, onOpenEntry, it) }
|
||||
val symbols: @Composable (Modifier) -> Unit = {
|
||||
SymbolCard(document, outline, currentLine, collapsed, onToggleCollapsed, onSymbolSelected, it)
|
||||
SymbolCard(document, outline, currentLine, collapsed, onToggleCollapsed, onSymbolSelected, jump, it)
|
||||
}
|
||||
Column(modifier.background(MaterialTheme.colorScheme.background).padding(horizontal = 10.dp, vertical = 10.dp)) {
|
||||
val half = Modifier.weight(1f).fillMaxWidth()
|
||||
@ -1065,6 +1095,7 @@ private fun SymbolCard(
|
||||
collapsed: Set<Int>,
|
||||
onToggleCollapsed: (CodeSymbol) -> Unit,
|
||||
onSymbolSelected: (CodeSymbol) -> Unit,
|
||||
jump: SymbolJump?,
|
||||
modifier: Modifier,
|
||||
) {
|
||||
val current = currentIn(outline, document.symbols, currentLine)
|
||||
@ -1092,6 +1123,7 @@ private fun SymbolCard(
|
||||
isCollapsed = symbol.line in collapsed,
|
||||
methodCount = methodCountOf(document.symbols, symbol),
|
||||
onToggle = { onToggleCollapsed(symbol) },
|
||||
jump = jump,
|
||||
) { onSymbolSelected(symbol) }
|
||||
}
|
||||
}
|
||||
@ -1246,6 +1278,7 @@ private fun SymbolRow(
|
||||
isCollapsed: Boolean,
|
||||
methodCount: Int,
|
||||
onToggle: () -> Unit,
|
||||
jump: SymbolJump?,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
val colors = rememberCodeSyntaxColors()
|
||||
@ -1311,6 +1344,22 @@ private fun SymbolRow(
|
||||
maxLines = 1,
|
||||
modifier = Modifier.padding(end = if (compact) 8.dp else 0.dp),
|
||||
)
|
||||
} else if (jump != null && symbol.kind == SymbolKind.METHOD) {
|
||||
Surface(
|
||||
onClick = { jump.onJump(symbol) },
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
color = MaterialTheme.colorScheme.primaryContainer,
|
||||
contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
modifier = Modifier.semantics { contentDescription = jump.description },
|
||||
) {
|
||||
Row(
|
||||
Modifier.padding(start = 8.dp, end = 4.dp, top = 5.dp, bottom = 5.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(jump.label, fontFamily = FontFamily.Monospace, fontSize = 12.sp, fontWeight = FontWeight.Bold, maxLines = 1)
|
||||
UBIcon(R.drawable.ic_ub_chevron_right, null, size = 14.dp)
|
||||
}
|
||||
}
|
||||
} else if (!compact) {
|
||||
Text(
|
||||
stringResource(R.string.codeviewer_outline_line, symbol.line + 1),
|
||||
@ -1326,6 +1375,7 @@ private fun SymbolRow(
|
||||
@Composable
|
||||
private fun OutlineSheet(
|
||||
document: CodeDocument,
|
||||
jump: SymbolJump?,
|
||||
currentLine: Int,
|
||||
collapsed: Set<Int>,
|
||||
onToggleCollapsed: (CodeSymbol) -> Unit,
|
||||
@ -1400,6 +1450,7 @@ private fun OutlineSheet(
|
||||
isCollapsed = symbol.line in collapsed,
|
||||
methodCount = methodCountOf(document.symbols, symbol),
|
||||
onToggle = { onToggleCollapsed(symbol) },
|
||||
jump = jump,
|
||||
) { onSymbolSelected(symbol) }
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
@ -1392,3 +1392,12 @@ This is the append-only implementation and handoff record shared by Codex and Cl
|
||||
- Live verification: installed on the SM-F966N; reproduced and fixed in unit tests only, not on
|
||||
the owner's files.
|
||||
- Next owner: the product owner, to confirm on the files they were reading.
|
||||
|
||||
## 2026-09-28 (continued) — TASK-059: from a declaration to its definition
|
||||
|
||||
- Request: the human product owner confirmed the C++ outline now appears, and asked to go from a
|
||||
function in a `.h` to where it is written in the `.cpp`.
|
||||
- What changed: see `tasks/059-jump-from-declaration-to-definition.md`.
|
||||
- Checks and results: the required checks pass; 435 unit tests, 0 failures; lint 0 errors.
|
||||
- Live verification: installed on the SM-F966N; not seen on a screen by the implementer.
|
||||
- Next owner: the product owner, to confirm on the device.
|
||||
|
||||
55
tasks/059-jump-from-declaration-to-definition.md
Normal file
55
tasks/059-jump-from-declaration-to-definition.md
Normal file
@ -0,0 +1,55 @@
|
||||
# TASK-059: From a function in the header to its definition in the source
|
||||
|
||||
Status: implemented; required checks pass; installed on the product owner's Z Fold7, awaiting their confirmation
|
||||
Owner: Claude
|
||||
Reviewer: human product owner
|
||||
|
||||
## Outcome
|
||||
|
||||
The human product owner confirmed the outline now appears for their C++ files (TASK-058) and asked
|
||||
whether choosing a function in a `.h` could go to where it is written in the `.cpp`.
|
||||
|
||||
## Scope
|
||||
|
||||
- Included: in the outline (the side pane and the outline sheet), every function of a file that
|
||||
has a counterpart carries a button named after the counterpart's extension (`.cpp ›` in a
|
||||
header, `.h ›` in a source file). It opens the counterpart at the function of the same name.
|
||||
Choosing the row itself still goes to the function in the file being read.
|
||||
- Excluded:
|
||||
- Tapping a function's name in the code itself.
|
||||
- Side by side: the two panes already move to the same function together, so no button there.
|
||||
- Telling overloads apart: the first function of that name is the one opened.
|
||||
|
||||
## Writable paths
|
||||
|
||||
- `app/src/main/java/com/ubisam/ubgitea/feature/codebrowser/CodeViewerScreen.kt`
|
||||
- `tasks/`, `docs/`
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [x] The button appears only on functions, and only when the file has a counterpart that is not
|
||||
already beside it.
|
||||
- [x] The drawer or sheet closes and the counterpart opens at the function.
|
||||
- [x] A function with no definition in the counterpart (inline, pure virtual) opens the counterpart
|
||||
at its top rather than failing.
|
||||
|
||||
## Security and privacy
|
||||
|
||||
No additional concerns.
|
||||
|
||||
## Required checks
|
||||
|
||||
```text
|
||||
./gradlew compileDebugKotlin compileDebugUnitTestKotlin testDebugUnitTest assembleDebug lintDebug
|
||||
```
|
||||
|
||||
## Evidence
|
||||
|
||||
- Required checks pass: 435 unit tests, 0 failures; lint 0 errors. No test was added: the change
|
||||
is wiring in the screen, and the lookup it uses (`findSymbolNamed`, `openSibling` with a symbol)
|
||||
was already covered.
|
||||
- Installed on the SM-F966N over wireless `adb`. Not seen working on a screen by the implementer.
|
||||
|
||||
## Review findings
|
||||
|
||||
None yet.
|
||||
Loading…
x
Reference in New Issue
Block a user