in src/LanguageServer/Impl/Formatting/LineFormatter.cs [396:551]
private static void AppendTokenEnsureWhiteSpacesAround(StringBuilder builder, TokenExt token)
=> builder.EnsureEndsWithWhiteSpace()
.Append(token)
.EnsureEndsWithWhiteSpace();
private class TokenExt {
public TokenExt(Token token, string precedingWhitespace, IndexSpan span, int line, bool isMultiLine,
TokenExt prev) {
Token = token;
PrecedingWhitespace = precedingWhitespace;
Span = span;
Line = line;
Prev = prev;
IsMultilineString = IsString && isMultiLine;
}
public Token Token { get; }
public IndexSpan Span { get; }
public int Line { get; }
public TokenExt Inside { get; set; }
public TokenExt Prev { get; }
public TokenExt Next { get; set; }
public string PrecedingWhitespace { get; }
public bool IsMultilineString { get; }
public TokenKind Kind => Token.Kind;
public override string ToString() => Token.VerbatimImage;
public bool IsIgnored => Is(TokenKind.NewLine, TokenKind.NLToken, TokenKind.Indent, TokenKind.Dedent, TokenKind.ExplicitLineJoin);
public bool IsOpen => Is(TokenKind.LeftBrace, TokenKind.LeftBracket, TokenKind.LeftParenthesis);
public bool IsClose => Is(TokenKind.RightBrace, TokenKind.RightBracket, TokenKind.RightParenthesis);
public bool IsColonOrComma => Is(TokenKind.Colon, TokenKind.Comma);
public bool MatchesClose(TokenExt other) {
switch (Kind) {
case TokenKind.LeftBrace:
return other.Kind == TokenKind.RightBrace;
case TokenKind.LeftBracket:
return other.Kind == TokenKind.RightBracket;
case TokenKind.LeftParenthesis:
return other.Kind == TokenKind.RightParenthesis;
}
return false;
}
public bool IsOperator => Token is OperatorToken || Is(TokenKind.Dot, TokenKind.Assign, TokenKind.Twiddle);
public bool IsUnaryOp => Is(TokenKind.Add, TokenKind.Subtract, TokenKind.Twiddle);
public bool IsInsideFunctionArgs => (Inside?.Kind == TokenKind.LeftParenthesis && Inside.PrevNonIgnored?.Kind == TokenKind.Name) || (Inside?.Kind == TokenKind.KeywordLambda);
public bool IsNumber => Kind == TokenKind.Constant && Token != Tokens.NoneToken && !(Token.Value is string || Token.Value is AsciiString);
public bool IsKeyword => (Kind >= TokenKind.FirstKeyword && Kind <= TokenKind.LastKeyword) || Kind == TokenKind.KeywordAsync || Kind == TokenKind.KeywordAwait;
public bool IsString => Kind == TokenKind.FString // TODO: format inside fstrings
|| (Kind == TokenKind.Constant && Token != Tokens.NoneToken && (Token.Value is string || Token.Value is AsciiString));
public bool IsSimpleSliceToLeft {
get {
if (Kind != TokenKind.Colon) {
return false;
}
var a = PrevNonIgnored;
var b = a?.PrevNonIgnored;
var c = b?.PrevNonIgnored;
if (a == null) {
return false;
}
if (a.Is(TokenKind.LeftBracket, TokenKind.Colon)) {
return true;
}
if ((!a.IsNumber && a.Kind != TokenKind.Name) || b == null) {
return false;
}
if (b.Is(TokenKind.LeftBracket, TokenKind.Colon, TokenKind.Comma)) {
return true;
}
if (!b.IsUnaryOp || c == null) {
return false;
}
return c.Is(TokenKind.LeftBracket, TokenKind.Colon, TokenKind.Comma);
}
}
public bool IsSimpleSliceToRight {
get {
if (Kind != TokenKind.Colon) {
return false;
}
var a = NextNonIgnored;
var b = a?.NextNonIgnored;
var c = b?.NextNonIgnored;
if (a == null) {
return false;
}
if (a.Is(TokenKind.RightBracket, TokenKind.Colon, TokenKind.Comma)) {
return true;
}
if (b == null) {
return false;
}
if (a.IsUnaryOp) {
if (c == null) {
return false;
}
return (b.IsNumber || b.Kind == TokenKind.Name) && c.Is(TokenKind.RightBracket, TokenKind.Colon, TokenKind.Comma);
}
return (a.IsNumber || a.Kind == TokenKind.Name) && b.Is(TokenKind.RightBracket, TokenKind.Colon, TokenKind.Comma);
}
}
public TokenExt PrevNonIgnored {
get {
if (Prev != null) {
if (Prev.IsIgnored) {
return Prev.PrevNonIgnored;
}
return Prev;
}
return null;
}
}
public TokenExt NextNonIgnored {
get {
if (Next != null) {
if (Next.IsIgnored) {
return Next.NextNonIgnored;
}
return Next;
}
return null;
}
}
private bool Is(params TokenKind[] kinds) => kinds.Contains(Kind);
}