-
Notifications
You must be signed in to change notification settings - Fork 29.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
improve indentation rules #17868
Comments
In above description I just linked to the documentation of TextMate but unluckily, TextMate's write up about indentation is somewhat ambiguous so here I'd like to share how I understand it. When we start to write a new line of code, most modern editors will insert some spaces/tabs, or the more popular concept indentation, at the beginning of the line. The reason for that is most programming languages and structured text have indentation conventions, and editors want to be smart about making indentation correct for us. You might ask how editors decide how many spaces they should insert? There are several approaches to achieve that but here we'll talk about TextMate's mechanism first. TextMate has four regexp patterns that you can set to tell it when and how to adjust the indentation:
they work perfectly in most cases and even become a de facto standard in this particular area. Successors like Sublime Text, Atom, VS Code ( 😄 ), etc adopt this concept while implementing their smart indentation system. Indentation InheritanceIf we don't set any indentation rule mentioned above, when you start a new line, TextMate will just inherit the indentation from previous line.
Take above code as example, we are editing a plain text file which has no indentation rules. The first line contains no whitespace but we add 8 spaces at the beginning of the second line by purpose. Now let's say the cursor is after
The third line just takes the indentation from the second line. One thing to note here is that the third line doesn't take the first line's indentation into consideration in this case. Personally I call it Indentation Inheritance. Indentation RulesThe four regexp indent patterns are all about adjusting the level of the inherited indentation. So the workflow of starting a new line by pressing
Note 1
increaseIndentPatternThis pattern means if a line matches it, the indentation level of the following line should be
If we set
The inherited indentation level is decreaseIndentPatternTextMate documentation says it's the counterpart of Since it's about the line we are working on at the moment, the only catch is if the pattern contains TextMate is really smart in this part, it checks the content of current line against Sublime Text doesn't roll back when the content no longer matches. However it has special handling when you delete all characters in current line, it rolls back to inherited indentation. Atom is straight forward, if it no longer matches the Note 2I'm using Take below code as example:
The inherited indentation level is indentNextLinePatternThis pattern is called Increase Only Next Line rule. As we highlighted in Note 1, the inherited indentation level is not always only decided by the line above, In some programming languages, you are Okay to write a block without brackets if there is just one single statement inside that block. if (condition)
statement();
otherStatement(); We usually increase the indentation if a line ends with an open bracket For this situation, there is the We can also think about it in another way. When we write the first line, But there is a really annoying problem: Note 3Let's recall what TextMate's documentation (section 24.2.5) says:
But the result in TextMate is not as expected. If I made anything wrong, like configuring TextMate improperly, free feel to correct me. In this case, the third line Is TextMate wrong here? Actually no. Here is an example that explains well: if (condition1)
if (condition2)
statement();
anotherStatement(); If we only indent next line when it's a single statement block, the first and second line would match Am I saying you can't write a general Since we have unIndentedLinePatternAny line that matches this rule is ignored when calculating indentation. Personally I'd like to put it this way: any line that matches For example, in JavaScript you may write some comments before code and we don't want these comments affect the indentation stuff, we can set the
Then when we calculate the indentation for each time, we can actually see it as
The indentation of the comments are not useful and all the indentation rules on the comments will not take effect either. |
It's always easy to describe what's the expected behavior but it's difficult to acknowledge that our current implementation is inconsistent with it. Here I'd like to list corner cases I ran into as many as possible decreaseIndentPattern
unIndentedLinePatternI configured TextMate to make any line contains
Support selectionsWhen we press enter, Code will check the content surrounding the selection against indentation rules. This is true when the selection is a single cursor. If we select multiple characters, our check against
|
Indentation
Intro
Indent Style is a convention to convey the structure of source code to human readers in programming languages. It's not a requirement in most programming languages but usually language authors adopt a style for their sample code and encourage users to use that. In some particular programming languages, Python for example, indentations are even meaningful to compilers/interpreters, wrong indentations may lead to compiling or runtime errors.
VS Code helps users to write correct indentations with ease. To make this happen, we followed TextMate's style and implemented three regexp patterns you can set to tell VS Code when to increase or decrease indentations.
increaseIndentPattern
decreaseIndentPattern
indentNextLinePattern
unIndentedLinePattern
However, these regexp patterns only decide whether we should modify the indentaion of a line if it matches one pattern, they don't tell us when is a good time to apply the modification. There are two ways to implement this feature, an active one and a passive one.
enter
key, run indentaion regex rules against current line.Active or Passive
The active way looks really good as the indentation can be adjusted immediately when the content of current line matches the indentation rule. Take Ruby
def/end
block as an example:Ruby programmers or anyone running into similar situation may only be 100% satisfied if we adjust the indentation correctly once they press
d
as part of the closing keywordend
. But the catch is users might keep typing after we modify the indentation, the content of current line may not match any regex rule any more, should we keep an eye on that and undo previous indentation change? To make things worse, if any indentation regex rule contains$
, which matches the ending position of a line, how can we tell whether users finish typing in current line or not?Taking above into consideration, passive way might be more reasonable. When a user press
enter
, he or she is sure that the content in current line is satisfactory for the moment and wants to work on following lines. We can calcualte the final indentation for current line and do the updates if it is different from the existing one. Besides, we can calculate the potential correct indentation level for the following line.In VS Code we chose the passive way.
Are we in good shape now?
We implemented the logic described above (there are several other components that contribute to the indentation, we'll discuss that later) but it's not precisely as expected. Currently we got somewhat wrong with the semantics of
decreaseIndentPattern
and there might be other cases that might be related or similar:TODO
decreaseIndentPattern
The text was updated successfully, but these errors were encountered: