/// <summary> /// Inserts one or more statements at current line. With correct position, indentation, etc. /// If editor is null or readonly, prints in output. /// Async if called from non-main thread. /// </summary> /// <param name="s">Text. The function ignores "\r\n" at the end. Does nothing if null.</param> /// <param name="separate">Prepend/append empty line to separate from surrounding code if need. If null, does it if <i>s</i> contains '\n'.</param> public static void Statements(string s, ICSFlags flags = 0, bool?separate = null) { if (s == null) { return; } bool sep = separate ?? s.Contains('\n'); if (Environment.CurrentManagedThreadId == 1) { _Statements(s, flags, sep); } else { App.Dispatcher.InvokeAsync(() => _Statements(s, flags, sep)); } }
static void _Statements(string s, ICSFlags flags, bool separate) { if (!App.Hmain.IsVisible) { App.ShowWindow(); } if (!CodeInfo.GetContextAndDocument(out var k, metaToo: true)) { print.it(s); return; } var root = k.syntaxRoot; var code = k.code; var pos = k.pos; var token = root.FindToken(pos); var node = token.Parent; //get the best valid insertion place bool havePos = false; var last = root.AttributeLists.LastOrDefault() as SyntaxNode ?? root.Usings.LastOrDefault() as SyntaxNode ?? root.Externs.LastOrDefault() as SyntaxNode ?? root.GetDirectives(o => o is DefineDirectiveTriviaSyntax).LastOrDefault(); if (last != null) { int e1 = last.FullSpan.End; if (havePos = pos <= e1) { pos = e1; } } if (!havePos) { var members = root.Members; if (members.Any()) { var g = members.LastOrDefault(o => o is GlobalStatementSyntax); int posAfterTLS = g?.FullSpan.End ?? members.First().FullSpan.Start; bool done1 = false; if (node is BlockSyntax) { done1 = node.Span.ContainsInside(pos); } else if (node is MemberDeclarationSyntax) { done1 = true; //don't use posAfterTLS if before the first type bool here = node == members.FirstOrDefault(o => o is not GlobalStatementSyntax) && pos <= node.SpanStart; pos = Math.Min(pos, here ? node.SpanStart : posAfterTLS); } else if (node is CompilationUnitSyntax && g != members[^ 1]) //after types { done1 = true; pos = Math.Min(pos, posAfterTLS); } if (!done1) { for (; node is not CompilationUnitSyntax; node = node.Parent) { //CiUtil.PrintNode(node); if (node is StatementSyntax) { var pa = node.Parent; if (node is BlockSyntax && pa is not(BlockSyntax or GlobalStatementSyntax)) { continue; } var span = node.Span; if (havePos = pos >= span.End && token.IsKind(SyntaxKind.CloseBraceToken)) { pos = node.FullSpan.End; } else if (havePos = pos >= span.Start) { pos = span.Start; } break; } if (node is MemberDeclarationSyntax) { pos = posAfterTLS; break; } } } havePos |= pos == posAfterTLS; } } if (k.meta.end > 0 && pos <= k.meta.end) { havePos = true; pos = k.meta.end; if (code.Eq(pos, "\r\n")) { pos += 2; } else if (code.Eq(pos, '\n')) { pos++; } } if (!havePos) //if in comments or directive or disabled code, move to the start of trivia or line { var trivia = root.FindTrivia(pos); var tk = trivia.Kind(); if (tk is not(SyntaxKind.EndOfLineTrivia or SyntaxKind.None)) { if (tk == SyntaxKind.DisabledTextTrivia) { while (pos > 0 && code[pos - 1] != '\n') { pos--; } } else { pos = trivia.FullSpan.Start; } //rejected: move to the start of entire #if ... block. // Rare, not easy, may be far, and maybe user wants to insert into disabled code. } } //rename symbols in s if need try { _RenameNewSymbols(ref s, k.document, node, pos); } catch (Exception e1) { Debug_.Print(e1); } //indent, newlines string breakLine = null; for (; pos > 0 && code[pos - 1] != '\n'; pos--) { if (code[pos - 1] is not(' ' or '\t')) { breakLine = "\r\n"; break; } } int replTo = pos; while (replTo < code.Length && code[replTo] is ' ' or '\t') { replTo++; } var d = k.sci; var t2 = root.FindToken(pos); if (t2.SpanStart >= pos) { t2 = t2.GetPreviousToken(); } bool afterOpenBrace = t2.IsKind(SyntaxKind.OpenBraceToken); bool beforeCloseBrace = replTo < code.Length && code[replTo] == '}'; int indent = d.zLineIndentationFromPos(true, pos); if (afterOpenBrace && breakLine != null && !(t2.Parent is BlockSyntax bs1 && bs1.Parent is GlobalStatementSyntax)) { indent++; }