Commit 61a2477
ADFA-2448: Add a document outline sidebar for Java, Kotlin and XML (#1804)
* ADFA-2448: Add outline symbol model and containment tree builder
* ADFA-2448: Add outline tree-sitter queries for java, kotlin, xml
* ADFA-2448: Add tree-sitter outline extraction provider
* ADFA-2448: Add outline UDF state types and view model
* ADFA-2448: Add outline Compose panel and row flattening
* style: spotless reformat EditorSidebarActions, no functional change
* ADFA-2448: Add outline sidebar panel with symbol navigation
* ADFA-2448: Read outline change snapshots from FileManager
* ADFA-2448: Color outline badges by symbol kind group
* ADFA-2448: Scroll the outline target into view after the drawer closes
* ADFA-2448: Use two-letter mnemonic badges for outline symbol kinds
* ADFA-2448: Densify outline rows with inline top-aligned details
* ADFA-2448: Draw hierarchy connector lines in the outline
* ADFA-2448: Center the tapped symbol in the viewport
* ADFA-2448: Keep the outline pipeline alive through parse failures
* ADFA-2448: Track only the active document and harden the outline pipeline
Review follow-ups from CodeRabbit on #1804:
- OutlineFragment ignores DocumentChangeEvents for files other than the
active editor's, so an edit to a background tab no longer swaps the
outline away from the file navigateTo targets. The event always carries
the new text (IDEEditor posts it), and the active editor's buffer is the
fallback, so the main-thread FileManager disk read is gone.
- Snapshots are keyed by normalized absolute path instead of basename, so
two Main.kt files in different modules no longer share collapse state.
- The snapshot collector catches non-cancellation failures around the whole
computation (supports() included), so one bad file cannot stop later
refreshes.
- kt/outline.scm lists a class_parameter as a property only when it is
declared val or var; class Repo(name: String) no longer shows "name".
The instrumentation fixture gains a plain parameter that must not appear.
- Asset readers for outline.scm are closed after reading (provider and
query test).
Not changed: the android.util.Log calls in EditorSidebarActions predate this
branch and are outside its scope.
* ADFA-2448: Fix outline extraction gaps, collapse-state race and stale-file tracking
Review follow-ups from itsaky-adfa on #1804.
Extraction:
- Java: @symbol.field now sits on each variable_declarator, so `private int
x, y;` lists both; dedup keys on the @name node instead of the symbol node.
- Kotlin: property_declaration is anchored to class_body, enum_class_body and
source_file, so locals inside functions no longer show as properties.
`interface` and `enum class` get INTERFACE/ENUM instead of CLASS; when two
patterns capture the same name the earlier pattern wins, so the specific
patterns are listed first.
- outlineOf runs on Dispatchers.Default inside the provider, so the guarantee
no longer depends on the caller.
View model: collapse state lives in a StateFlow combined into uiState, so the
main-thread toggle and the compute thread never share a plain field.
Fragment: DocumentCloseEvent is dropped before it reaches EventBus, so closing
the last file left a stale outline. The panel now follows EditorViewModel's
current-file LiveData (fires on open, tab switch and last-close) and
onDocumentOpened has the same active-file guard as onDocumentChanged.
Panel: the expand/collapse chevron is a 48.dp IconButton; name and detail sit
in a column so a long name cannot squeeze the signature to zero width.
Rows: every path segment carries its sibling index, so a symbol literally
named `bind#2` cannot collide with a disambiguated sibling.
CI: the nightly instrumentation job assembles and runs the editor module's
test APK on Firebase Test Lab.
Comments on the two androidTest build workarounds name the failure each
avoids (duplicate kotlin.reflect.full classes from kt-android.jar; Sentry
auto-init crashing the test process).
* ADFA-2448: Stop parsing for a hidden panel, scope Kotlin functions, run the editor suite on red nights
Round-3 review follow-ups from itsaky-adfa.
Blocking three:
- OutlineFragment holds the DrawerLayout it registers on and gates every
snapshot on the drawer being open, so a closed panel stops re-parsing the
file on each typing pause. A DrawerListener re-seeds on open, which is what
keeps a file switch made while the drawer was closed from going unnoticed.
- kt/outline.scm anchors function_declaration to class_body, enum_class_body
and source_file like the property patterns, so a local fun no longer shows
up in an outline that already drops local vals. The instrumented fixture
gained `fun helper()` inside add().
- instrumentation-test.yml captures each suite's exit code with
`|| SUITE_EXIT_CODE=$?` instead of letting bash -e abort the step, so the
editor suite runs even when the Kotlin or Groovy one fails, and a trailing
check re-fails the step if any suite failed. Plain `|| true` would have
turned a failing nightly green.
Also:
- centerPositionInView re-checks isValidPosition inside onDrawerClosed; the
earlier check was several hundred milliseconds stale by then.
- A named companion keeps its name: one pattern with an optional capture,
`(companion_object (type_identifier)? @name)`, covers named and anonymous
companions without two patterns fighting over the same node.
- Dedup compares patternIndex first and uses detail only within one pattern.
That required reordering xml/outline.scm so its two detail-bearing patterns
precede the plain ones; otherwise the id detail on an XML element would have
been dropped by the new precedence.
- OutlineSymbolKind.fromCaptureSuffix owns the capture-to-kind mapping, and a
new JVM test (OutlineQueryCapturesTest) asserts every @symbol.* capture in
all three .scm files resolves to a kind, so a typo fails the gating suite
rather than showing an empty outline.
- The kotlin-reflect exclude records that MockK is unusable in editor
androidTest as a consequence.
- extract() takes the coroutine context and calls ensureActive() per match, so
a superseded parse stops at the next match instead of running to completion.
- OutlineFragment.onDestroyView calls onNoEditor(), releasing the document copy
the activity-scoped view model would otherwise hold after the panel is gone.
- Content no longer carries an always-empty collapsedPaths; the panel collects
collapsedPaths from its own StateFlow, which also removes the combine.
- compute() resets the collapse set with a single getAndUpdate, so both writers
now go through one atomic operation.
- OutlineTreeBuilder pops until the stack top strictly contains the candidate,
so a partial overlap or an identical range makes a sibling, not a child.
* ADFA-2448: Keep the signature beside the name when it fits
The Column introduced for the round-3 review put every signature on its own
line, so `onDestroy` showed `()` underneath it with most of the row empty.
FlowRow keeps name and detail on one line while there is room and wraps the
detail only when there is not, which still prevents a long name from
squeezing the signature to zero width.
* ADFA-2448: Hold the deferred scroll as state and translate the row description
Round-4 review follow-ups from itsaky-adfa.
Blocking: navigateTo registered a second, anonymous DrawerLayout listener that
removed itself only from its own onDrawerClosed. Swiping the drawer back open
inside the close animation left it registered, holding an IDEEditor and a
Position past the fragment's life, so the next close for any reason scrolled
to an abandoned symbol. The deferred scroll is now state on the one long-lived
listener: onDrawerClosed consumes it, onDrawerOpened discards it, and
onDestroyView clears it alongside the listener it already removed.
Also:
- OutlineViewModel tracks the last computed path in its own field instead of
reading it off collapsed.path, which both early returns leave untouched. A
detour through an unsupported file no longer leaves that file's message on
screen while the previous file recomputes, and collapse still survives the
detour.
- EditorViewModel.currentFile gets @get:JvmName("currentFileLiveData"); its
default JVM getter collided by name with the existing getCurrentFile(): File?
and no Java caller could have resolved either.
- The row content description builds its kind word from :resources strings
(cd_outline_kind_*, 14 entries) instead of the enum constant name, so
TalkBack no longer announces English in a translated UI. The mapping is a
when in the panel, next to badgeColorFor, rather than resource ids on the
model enum.
- xml/outline.scm matches "^(id|name)$", so res/values files show the element
name as the detail. Before this, strings.xml and colors.xml listed N rows
reading only "string" or "color".
- loadQueries closes the TSQuery before throwing on a query that fails to
compile.
- The editor test APK glob is asserted to resolve to exactly one existing file,
so a missing APK fails with that message rather than a gcloud usage error.
- OutlineTreeBuilderTest gains the partial-overlap and identical-range cases.
Both fail against the pre-fix disjointness-only pop condition, and the other
seven cases pass either way, which is what the reviewer measured.
---------
Co-authored-by: Daniel Alome <astrocoder007@gmail.com>1 parent ee3e7d6 commit 61a2477
30 files changed
Lines changed: 2190 additions & 232 deletions
File tree
- .github/workflows
- app
- src
- main/java/com/itsaky/androidide
- actions/sidebar
- di
- fragments/sidebar
- ui
- models
- outline
- utils
- viewmodel
- test/java/com/itsaky/androidide
- ui/outline
- viewmodel
- editor
- src
- androidTest
- java/com/itsaky/androidide/editor/language/outline
- main
- assets/editor/treesitter
- java
- kt
- xml
- java/com/itsaky/androidide/editor/language/outline
- test/java/com/itsaky/androidide/editor/language/outline
- idetooltips/src/main/java/com/itsaky/androidide/idetooltips
- resources/src/main/res
- drawable
- values
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
78 | 78 | | |
79 | 79 | | |
80 | 80 | | |
| 81 | + | |
81 | 82 | | |
82 | 83 | | |
83 | 84 | | |
84 | | - | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
85 | 89 | | |
86 | 90 | | |
87 | 91 | | |
| |||
97 | 101 | | |
98 | 102 | | |
99 | 103 | | |
100 | | - | |
| 104 | + | |
101 | 105 | | |
102 | | - | |
103 | 106 | | |
104 | 107 | | |
105 | 108 | | |
| |||
126 | 129 | | |
127 | 130 | | |
128 | 131 | | |
129 | | - | |
| 132 | + | |
130 | 133 | | |
131 | | - | |
132 | 134 | | |
133 | 135 | | |
134 | 136 | | |
| |||
141 | 143 | | |
142 | 144 | | |
143 | 145 | | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
144 | 175 | | |
145 | 176 | | |
146 | 177 | | |
| |||
179 | 210 | | |
180 | 211 | | |
181 | 212 | | |
| 213 | + | |
182 | 214 | | |
183 | 215 | | |
184 | 216 | | |
| |||
199 | 231 | | |
200 | 232 | | |
201 | 233 | | |
| 234 | + | |
202 | 235 | | |
203 | 236 | | |
204 | 237 | | |
| |||
218 | 251 | | |
219 | 252 | | |
220 | 253 | | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
221 | 257 | | |
222 | 258 | | |
223 | 259 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
71 | 71 | | |
72 | 72 | | |
73 | 73 | | |
74 | | - | |
| 74 | + | |
75 | 75 | | |
76 | 76 | | |
77 | 77 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
310 | 310 | | |
311 | 311 | | |
312 | 312 | | |
| 313 | + | |
313 | 314 | | |
314 | 315 | | |
315 | 316 | | |
| |||
Lines changed: 28 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| 7 | + | |
| 8 | + | |
7 | 9 | | |
8 | 10 | | |
9 | 11 | | |
10 | 12 | | |
11 | 13 | | |
12 | 14 | | |
13 | 15 | | |
| 16 | + | |
14 | 17 | | |
15 | 18 | | |
16 | 19 | | |
| |||
35 | 38 | | |
36 | 39 | | |
37 | 40 | | |
| 41 | + | |
| 42 | + | |
38 | 43 | | |
39 | 44 | | |
40 | 45 | | |
| |||
Lines changed: 190 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
0 commit comments