Hi all,
So, I have an issue with traditional text editors & code browsers that presents a recurring frustration.
Background
When browsing code, we often jump into code regions with which we are not very familiar. This happens e.g. when ticking through a list of file locations from grep or compiler diagnostics; or, during a debugger session, when selecting a row in the middle of a stack trace.
When I do this (and especially if it's code that I haven't looked at in a while), I often find myself in a nested scope that is somewhere in the body of a long procedure, and so I immediately scroll up (potentially through several pages of code) to see the semantic context.
The end result can look something like this:
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 | void
procedure_I_have_not_seen_for_some_time() {
/* many lines of code... */
/* other nested scopes... */
/* many lines of code... */
for (int i = 0; i<N; ++i) {
/* many lines of code... */
for (int j = 0; j<M; ++j) {
/* many lines of code... */
switch (something) {
/* many lines of code... */
/* (many loops nested within cases
* that I don't care about right now...)
*/
case SOME_LABEL: {
/* many lines of code... */
// --- top of 4coder window here ---
/* code... */
YOU_ARE_HERE();
/* code... */
// --- bottom of 4coder window here ---
|
Now suppose that, for each instance of "/* many lines of code... */", there is enough code to fill the screen.
So when the editor jumps so that YOU_ARE_HERE() is in the center of the screen, I have to hit <page_up> several times in order to see the full context. And every time I page-up, I have to make a mental note of what scope I'm in (and mentally push and pop unrelated scopes as I enter and leave them).
Certain navigation commands, like those that jump upward to the nearest enclosing scope, can help a little. But that takes a little more presence of mind than just mashing <page_up> (and it consumes one more key-binding that I might have used for something else, and it's one more thing to learn and remember).
Problem
All of this extra navigation consumes both time and working memory.
The situation becomes worse as the length of the list of file locations increases; if grep gives me a lot of hits, I'll have to press <page_up> many, many, many... many times.
Proposed solution
Some time after 4coder is able to parse C++ well enough to do semantically-aware wrapping & indentation, I would like to see support for optional semantically-aware code folding, such that all of the introductory parts of all enclosing compound-statements are visible in the 4coder window at the same time, and any leftover screen real-estate is used for the ordinary display of code near the cursor location, like so:
| // --- top of 4coder window
procedure_I_have_not_seen_for_some_time() {
... for (int i = 0; i<N; ++i) {
... for (int j = 0; j<M; ++j) {
... switch (something) {
... case SOME_LABEL: {
... /* some lines of code... */
YOU_ARE_HERE();
/* subsequent lines of code... */
// --- bottom of 4coder window
|
(Here, ellipses indicate that code has been folded.)
This way, when I jump to a new location, I can just read from the top of the window to the cursor location without pressing any additional keys, and I'm much more quickly informed about the context.
Eventually, you'll probably want a better & more subtle visual representation of folding than an ellipsis -- e.g. maybe a curved, gray arrow? But the point is that you want to indicate folding in a way that minimizes the amount of consumed real estate. (E.g. Xcode's text editor is better at this than Vim: where Xcode displays the fold-indicator as a small curved pseudo-character in-line, Vim consumes an entire row of text just to say "some text has been folded here".)
As I scroll up, more of the nearest folded region (in this example, code below "SOME_LABEL" but above "YOU_ARE_HERE()") should become incrementally visible, until I reach the introductory part of the nearest enclosing scope, at which point that introductory part becomes part of the ordinary display of non-folded code.
With languages that support
labeled sub-statements like those in Swift (which, BTW, should probably be a standard feature in all new imperative languages), I expect the reading experience will be even better:
| // --- top of 4coder window
procedure_I_have_not_seen_for_some_time() {
... do_outer_things: for (int i = 0; i<N; ++i) {
... do_inner_things: for (int j = 0; j<M; ++j) {
... act_on_inner_thing: switch (something) {
... case SOME_LABEL: {
... /* some lines of code... */
YOU_ARE_HERE();
/* subsequent lines of code... */
// --- bottom of 4coder window
|
Extra credit, part 1
Ideally, I would also like to toggle visibility of the introductory parts of sub-compound-statements that are *adjacent* to enclosing scopes:
1
2
3
4
5
6
7
8
9
10
11
12 | // --- top of 4coder window
procedure_I_have_not_seen_for_some_time() {
... first_loop: for (/* code */) { ... }
... second_loop: for (int i = 0; i<N; ++i) {
... act_on_thing: switch (something) {
... case SOME_LABEL: {
... first_thing_for_SOME_LABEL: {...}
... second_thing_for_SOME_LABEL: {
... /* some lines of code... */
YOU_ARE_HERE();
/* subsequent lines of code... */
// --- bottom of 4coder window
|
Extra credit, part 2
Suppose I'm at the location of a grep hit, and I want to scroll up to see some of the folded content. As I do this, the line with the search hit and its enclosing introductory regions would normally disappear below the bottom of the 4coder window. I would like an option to instead keep these regions visible, such that they stack together near the bottom of the screen. It would look something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | // --- top of 4coder window
procedure_I_have_not_seen_for_some_time() {
... for (int i = 0; i<N; ++i) {
... /* some lines of code... */
USER_HAS_SCROLLED_UP_TO_HERE();
/* subsequent lines of code... */
... for (int j = 0; j<M; ++j) {
... switch (something) {
... case SOME_LABEL: {
... GREP_HIT_LOCATION_IS_HERE();
// --- bottom of 4coder window
|