[txmt-dev] TM2 Indentation: a better approach

Gerd Knops gerti-textmate at bitart.com
Tue Aug 21 22:27:53 UTC 2012


Hi All,

I will start with the conclusion because I suspect many would get lost along that winding description below:

Regular Expression-based indentation rules are doomed. They are unmaintainable for anything but the basic cases. Nobody can read them.

A far better approach would be one based on scopes alone:

- assign indents to certain scopes
- count all existing scopes at the beginning if a line
- subtract all scopes no longer existing at the end of that line

The result is the indent for that line. Period. Done. Really!

With some gentle grammar support, this would even allow to indent switch statements etc. to everybody's liking.


Here is how I arrived at that conclusion:

In the last few days I embarked on a crusade to get nested Objective-C method calls to format like I want them too, eg like so:

	[[a b]
		c:d
		e:[f g]
		h:[i
			j:k
		]
	];

Indentation in ObjC is currently governed by the rules in C, but trying to add on to those quickly leads to madness. Besides ObjC rules do not belong in the C bundle.

So it occurred to me to use try indentation preferences scoped to the bracketed scope.

So I created a preference scoped to "meta.bracketed.objc", containing:

{	decreaseIndentPattern = '(?=not)possible';
	increaseIndentPattern = '\[';
	indentNextLinePattern = '(?=not)possible';
	unIndentedLinePattern = '(?=not)possible';
}

That looked promising. Since the decreaseIndentPattern will not work (as the decrease has to happen outside the scope) I added a simple snippet:

	Scope: R:(punctuation.section.scope.end.objc)
	Key Equivalent: \n
	Content: "\n\t$0\n"

Looking better. But this still fails to do nested scopes. To fix it gets really complex in a hurry:

	increaseIndentPattern = '(?x)
		^			# Start of line
		[^\[\]]*		# non-bracket chars
	    (?<rel>			# This block will skip over nested square brackets
	      \[			#
	        (?:			#
	          (?> [^\[\]]+ )	#
	          |			#
	          \g<rel>		#
	        )*			#
	      \]			#
	    )?				# End skip block
		[^\[\]]*		# non-bracket chars
		\[			# open bracket
		[^\[\]]*		# non-bracket chars
	    (?<rer>			# This block will skip over nested square brackets
	      \[			#
	        (?:			#
	          (?> [^\[\]]+ )	#
	          |			#
	          \g<rer>		#
	        )*			#
	      \]			#
	    )?				# End skip block
		[^\[\]]*		# non-bracket chars
		$			# End of line
	';

That works. But any brackets in strings or character constants will still break this, not even mentioning comments.

I guess it could be done, though the size of the RE would probably rival the size of the entire TM2 code.

Gerd



More information about the textmate-dev mailing list