Sublime Forum

Having trouble figuring out "expand selection to scope"

#1

I’m writing a custom syntax for a language that uses the C preprocessor. I ideally would like to get some behavior that I’m used to from emacs. In particular, I would like “expand selection to scope” to select the enclosing #if or #else branch and expanding again to select the entire #if #else #endif conditional.

I have what appears to me to be a correct syntax, but I find that the else branch works and the if branch doesn’t. I’m confused abut what “expand selection to scope” actually does because the syntax looks symmetrical.

In the following example:

#ifdef a

#else

#endif

I have a scope called “meta.ppc-cond” that starts with the # in ifdef and ends at the newline after endif. I have a scope called meta.if-branch that starts at the # in ifdef and ends just before #else (there’s a forward lookahead for #else that pops the if-branch context). There is another scope called meta.else-branch that starts at the # in else and extends to the newline after #endif.

Inside the #ifdef branch, if I do an expand selection to scope, it selects from #ifdef though the newline before #else, as expected. If I do the same inside the #else branch, it selects from the #else through the end of #endif. So far, so good.

If I expand-to-selection twice inside the else clause, the first time selects the else clause (as above), and the second time selects the entire conditional. This is exactly what I want.

If I expand-to-selection twice inside the if clause, however, the first time selects the if clause (as above), but the second time selects the entire file, rather than from #ifdef through #endif. This is what I’m confused about. Everything in the if clause is also in meta.ppc-cond, so why wouldn’t it expand to the meta.ppc-cond scope?

I had some trouble getting the else clause to work. In my original definition, meta.else-clause and meta.ppc-cond ended just after #endif (not including the newline). When I did a first “expand scope to selection” in the else clause, it looks like it selected the entire line containing #endif, but because the selection wasn’t entirely contained in meta.ppc-cond, the second expansion didn’t work. I’d imagine that something similar is going on here (the selection is somehow extending beyond the scope), but it really looks like everything selected by the if-branch is also in meta.ppc-cond. I assume there’s some subtlety here that I’m not aware of.

Any ideas? Thanks.

0 Likes

#2

I think I’ve figured out what’s happening, though I still don’t know if there’s anything I can do about it.

The problem seems general and doesn’t seem to be specific to my syntax, except that I have two nested scopes that start at the same position. I have experimented with other syntaxes where we have two scopes (inner and outer) that start at the same point, but inner ends first. The rules seem to be:

  1. If the first character of both scopes is selected, expand-selection-to-scope ignores both scopes and selects outer’s enclosing scope.

  2. If all of inner is selected except for its first character, expand-selection-to-scope will select outer.

  3. For any other selection that’s completely inside inner, expand-selection-to-scope selects inner.

  4. For any selection that starts in inner and extends to outer, expand-selection-to-scope selects outer.

The root if my problem is #1. Once I expand-selection-to-scope on inner, the first character is selected. At that point, expanding again ignores outer. The funny part is #2 (funny because I don’t think there should be anything special about the second character). In my opinion, if inner is selected in its entirety, expand-selection-to-scope should select outer (which is what happens if inner is selected except for its first character).

Has anyone else noticed this behavior? Is it a bug or a feature that I don’t understand? Any thoughts about a workaround?

Thanks.

0 Likes

#3

I have a workaround for my specific case. I’m still confused about what expand selection to scope does, though. Is there some documentation about its specific behavior?

As it turns out, if I have meta.ppc-cond run from the first character of #ifdef to the last character of #endif, meta.if-branch run from the line after ifdef through right before the # in else, and meta.else-branch run from the # in else to just before the # in #endif, everything works. In either branch, expanding to scope once selects the branch I’m in, and expanding twice expands to the entire conditional.

Through more experiments, it seems that it always wants to expand right side of the selection based on the scope of the character just after the selection, regardless of the position of the cursor. It wants to expand the left side of the selection based on the scope of the first character of the selection (i.e., the cursor location) if the cursor is at the beginning of the selection but the character before that if the cursor is at the end of the selection.

This issue of checking the character to the left of the beginning of the selection (if the cursor is at the end) as opposed to the beginning of the selection itself is the cause of my difficulties.

This seems like a strange rule. Is it by design?

0 Likes

#4

a few of us have also been wondering how it works, in the end we just avoided the functionality due to not understanding it I believe

1 Like