in resharper/resharper-unity/src/Unity.Shaders/ShaderLab/Feature/Services/TypingAssist/ShaderLabTypingAssist.cs [106:213]
private bool HandleEnterAction(IActionContext actionContext)
{
if (actionContext.EnsureWritable() != EnsureWritableResult.SUCCESS)
return false;
if (ShouldIgnoreCaretPosition(actionContext.TextControl, out var cachingLexer))
return false;
var textControl = actionContext.TextControl;
if (GetTypingAssistOption(textControl, TypingAssistOptions.SmartIndentOnEnterExpression))
{
using (CommandProcessor.UsingCommand("Smart Enter"))
{
if (textControl.Selection.OneDocRangeWithCaret().Length > 0)
return false;
var offset = textControl.Caret.Offset();
if (offset == 0)
return false;
var rangeStart = offset;
var rangeEnd = offset;
var isInsideNonWhitespaceToken = cachingLexer.TokenType is { IsWhitespace: false } &&
cachingLexer.TokenStart < offset &&
cachingLexer.TokenEnd > offset;
// no need to search for whitespaces if we are inside a non-whitespace token
if (!isInsideNonWhitespaceToken)
{
// Remove whitespaces after end of preceding non-whitespace token
if (MoveToClosestTokenNodeTypeSkippingWhitespaces(cachingLexer, -1) is { IsWhitespace: false })
rangeStart = cachingLexer.TokenEnd;
cachingLexer.Advance();
while (cachingLexer.TokenType is { IsWhitespace: true } && cachingLexer.TokenType != ShaderLabTokenType.NEW_LINE)
cachingLexer.Advance();
rangeEnd = cachingLexer.TokenStart;
}
var isStartOfBlock = cachingLexer.TokenType == ShaderLabTokenType.LBRACE;
var isEndOfBlock = cachingLexer.TokenType == ShaderLabTokenType.RBRACE;
if (!cachingLexer.FindTokenAt(offset - 1))
return false;
// move to either to start of block or closest command keyword, if next symbol is end of block then only start of block is a valid reference
if (!MoveLexerToIdentReference(cachingLexer, !isEndOfBlock))
return false;
// should only append block indent if '{' is a first indentation reference, if there preceding shader command then just use same indentation.
// If next token is end of block then it also shouldn't be indented inside of block
var stoppedAtStartOfBlock = cachingLexer.TokenType == ShaderLabTokenType.LBRACE;
var document = textControl.Document;
// e.g
//Shader "Custom/Test2_hlsl" {
//<caret> Properties {
//<caret>{
//<caret> {
if (!TryGetLineIndent(cachingLexer, document, out var lineIndent))
return false;
var formattingService = GetFormatSettingsService(textControl);
var braceStyle = (formattingService as ShaderLabFormatSettingsKey)?.BraceStyle ?? BraceFormatStyle.NEXT_LINE;
var shouldBeShifted = braceStyle is BraceFormatStyle.NEXT_LINE_SHIFTED or BraceFormatStyle.NEXT_LINE_SHIFTED_2;
var shouldAppendBlockIndent = isStartOfBlock && shouldBeShifted || stoppedAtStartOfBlock && braceStyle != BraceFormatStyle.NEXT_LINE_SHIFTED;
var indent = lineIndent;
if (shouldAppendBlockIndent)
indent += formattingService.GetIndentStr();
string? prologIndent = null;
// if Enter pressed before end of block then we want to insert extra empty line inside of the block
string? epilogueIndent = null;
if (isEndOfBlock && (stoppedAtStartOfBlock || MoveLexerToIdentReference(cachingLexer, false) && TryGetLineIndent(cachingLexer, document, out lineIndent)))
{
epilogueIndent = lineIndent;
// if Enter pressed inside of empty braces on same line and these are not on own line yet then insert line before opening brace for next line brace style
if (braceStyle is BraceFormatStyle.NEXT_LINE or BraceFormatStyle.NEXT_LINE_SHIFTED or BraceFormatStyle.NEXT_LINE_SHIFTED_2
&& GetClosestTokenNodeTypeSkippingWhitespaces(cachingLexer, 1) == ShaderLabTokenType.RBRACE
&& MoveToClosestTokenNodeTypeSkippingWhitespaces(cachingLexer, -1) is { IsWhitespace: false })
{
rangeStart = cachingLexer.TokenEnd;
if (shouldBeShifted)
{
var indentStr = formattingService.GetIndentStr();
indent += indentStr;
epilogueIndent += indentStr;
}
prologIndent = epilogueIndent;
}
}
CommitPsiOnlyAndProceedWithDirtyCaches(textControl, _ =>
{
var newLine = GetNewLineText(textControl.Document.GetPsiSourceFile(Solution));
var sb = new StringBuilder();
if (prologIndent != null)
sb.Append(newLine).Append(prologIndent).Append(ShaderLabTokenType.LBRACE.TokenRepresentation);
sb.Append(newLine).Append(indent);
var caretOffset = sb.Length;
if (epilogueIndent != null)
sb.Append(newLine).Append(epilogueIndent);
var range = new TextRange(rangeStart, rangeEnd);
document.ReplaceText(range, sb.ToString());
textControl.Caret.MoveTo(range.StartDocOffset() + caretOffset, CaretVisualPlacement.DontScrollIfVisible);
});
return true;
}
}
return false;
}