public static IFunctionDefinition FindFunctionDefinition(this AstRoot ast, int position, out IVariable v) { v = null; var exp = ast.GetNodeOfTypeFromPosition <IExpressionStatement>(position); return(exp?.GetVariableOrFunctionDefinition(out v)); }
public IEnumerable <SuggestedActionSet> GetSuggestedActions(ISuggestedActionCategorySet requestedActionCategories, SnapshotSpan range, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return(Enumerable.Empty <SuggestedActionSet>()); } List <SuggestedActionSet> actionSets = new List <SuggestedActionSet>(); int caretPosition = _textView.Caret.Position.BufferPosition; SnapshotPoint?bufferPoint = _textView.MapDownToR(caretPosition); if (bufferPoint.HasValue) { AstRoot ast = _document?.EditorTree.AstRoot; int bufferPosition = bufferPoint.Value.Position; _lastNode = ast?.GetNodeOfTypeFromPosition <TokenNode>(bufferPosition); if (_lastNode != null) { foreach (IRSuggestedActionProvider actionProvider in _suggestedActionProviders) { if (actionProvider.HasSuggestedActions(_textView, _textBuffer, bufferPosition)) { IEnumerable <ISuggestedAction> actions = actionProvider.GetSuggestedActions(_textView, _textBuffer, bufferPosition); Span applicableSpan = new Span(_lastNode.Start, _lastNode.Length); SuggestedActionSet actionSet = new SuggestedActionSet(actions, applicableToSpan: applicableSpan); actionSets.Add(actionSet); } } } } return(actionSets); }
/// <summary> /// Called after character is typed. Gives language-specific completion /// controller has a chance to dismiss or initiate completion and paramenter /// help sessions depending on the current context. /// </summary> public override void OnPostTypeChar(char typedCharacter) { if (typedCharacter == '(' || typedCharacter == ',') { // Check if caret moved into a different functions such as when // user types a sequence of nested function calls. If so, // dismiss current signature session and start a new one. if (!SignatureHelper.IsSameSignatureContext(TextView, _textBuffer, SignatureBroker)) { DismissAllSessions(); TriggerSignatureHelp(); } } else if (HasActiveSignatureSession(TextView) && typedCharacter == ')') { // Typing closing ) closes signature and completion sessions. DismissAllSessions(); // However, when user types closing brace is an expression inside // function argument like in x = y * (z + 1) we need to re-trigger // signature session AstRoot ast = REditorDocument.FromTextBuffer(TextView.TextBuffer).EditorTree.AstRoot; FunctionCall f = ast.GetNodeOfTypeFromPosition <FunctionCall>(TextView.Caret.Position.BufferPosition); if (f != null) { TriggerSignatureHelp(); } } else if (HasActiveSignatureSession(TextView) && typedCharacter == '\n') { // Upon ENTER we need to dismiss all sessions and re-trigger // signature help. Triggering signature help outside of // a function definition or call is a no-op so it is safe. DismissAllSessions(); TriggerSignatureHelp(); } else if (this.HasActiveCompletionSession) { if (typedCharacter == '\'' || typedCharacter == '\"') { // First handle completion of a string. base.OnPostTypeChar(typedCharacter); // Then re-trigger completion. DismissAllSessions(); ShowCompletion(autoShownCompletion: true); return; } else { // Backspace does not dismiss completion. Characters that may be an identifier // name do not dismiss completion either allowing correction of typing. if (typedCharacter != '\b' && !RTokenizer.IsIdentifierCharacter(typedCharacter)) { DismissCompletionSession(); } } } base.OnPostTypeChar(typedCharacter); }
public static IFunctionInfo GetUserFunctionInfo(this AstRoot ast, string functionName, int position) { var scope = ast.GetNodeOfTypeFromPosition <IScope>(position); var v = scope?.FindFunctionDefinitionByName(functionName, position); var rf = v?.Value as RFunction; var fd = rf?.Value as IFunctionDefinition; return(fd?.MakeFunctionInfo(functionName)); }
public static IAstNode GetIndentDefiningNode(AstRoot ast, int position) { IScope scope = ast.GetNodeOfTypeFromPosition <IScope>(position); // Scope indentation is defined by its parent statement. IAstNodeWithScope parentStatement = scope.Parent as IAstNodeWithScope; if (parentStatement != null && parentStatement.Scope == scope) { return(parentStatement); } return(scope); }
private static bool GetFunction(AstRoot astRoot, ref int position, out FunctionCall functionCall, out Variable functionVariable) { // Note that we do not want just the deepest call since in abc(def()) // when position is over 'def' we actually want signature help for 'abc' // while simple depth search will find 'def'. functionVariable = null; int p = position; functionCall = astRoot.GetSpecificNodeFromPosition <FunctionCall>(p, (x) => { var fc = x as FunctionCall; // Take into account incompleted argument lists line in 'func(a|' return(fc != null && (fc.Arguments.Contains(p) || (fc.CloseBrace == null && fc.Arguments.End == p))); }); if (functionCall == null && position > 0) { // Retry in case caret is at the very end of function signature // that does not have final close brace yet. functionCall = astRoot.GetNodeOfTypeFromPosition <FunctionCall>(position - 1, includeEnd: true); if (functionCall != null) { // But if signature does have closing brace and caret // is beyond it, we are really otuside of the signature. if (functionCall.CloseBrace != null && position >= functionCall.CloseBrace.End) { return(false); } if (position > functionCall.SignatureEnd) { position = functionCall.SignatureEnd; } } } if (functionCall != null && functionCall.Children.Count > 0) { functionVariable = functionCall.Children[0] as Variable; if (functionVariable == null) { // Might be in a namespace var op = functionCall.Children[0] as IOperator; if (op != null && op.OperatorType == OperatorType.Namespace) { functionVariable = op.RightOperand as Variable; } } return(functionVariable != null); } return(false); }
public static void FormatScope(ITextView textView, ITextBuffer textBuffer, int position, bool indentCaret) { IREditorDocument document = REditorDocument.TryFromTextBuffer(textBuffer); if (document != null) { document.EditorTree.EnsureTreeReady(); int baseIndentPosition = -1; ITextSnapshot snapshot = textBuffer.CurrentSnapshot; AstRoot ast = document.EditorTree.AstRoot; IScope scope = ast.GetNodeOfTypeFromPosition <IScope>(position); // Scope indentation is defined by its parent statement. IAstNodeWithScope parentStatement = ast.GetNodeOfTypeFromPosition <IAstNodeWithScope>(position); if (parentStatement != null && parentStatement.Scope == scope) { ITextSnapshotLine baseLine = snapshot.GetLineFromPosition(parentStatement.Start); baseIndentPosition = baseLine.Start; } FormatScope(textView, textBuffer, ast, scope, baseIndentPosition, indentCaret); } }
/// <summary> /// Determines if position is in the argument name. Typically used to /// a) suppress general intellisense when typing function arguments /// in a function/ definition such as in 'x <- function(a|' /// b) determine if completion list should contain argumet names /// when user types inside function call. /// </summary> internal static bool IsInFunctionArgumentName <T>(AstRoot ast, int position) where T : class, IFunction { T funcDef = ast.GetNodeOfTypeFromPosition <T>(position); if (funcDef == null || funcDef.OpenBrace == null || funcDef.Arguments == null) { return(false); } if (position < funcDef.OpenBrace.End || position >= funcDef.SignatureEnd) { return(false); } int start = funcDef.OpenBrace.End; int end = funcDef.SignatureEnd; if (funcDef.Arguments.Count == 0 && position >= start && position <= end) { return(true); } for (int i = 0; i < funcDef.Arguments.Count; i++) { CommaSeparatedItem csi = funcDef.Arguments[i]; NamedArgument na = csi as NamedArgument; if (position < csi.Start) { break; } end = csi.End; if (position >= start && position <= end) { if (na == null) { return(true); } if (position <= na.EqualsSign.Start) { return(true); } } } return(false); }
/// <summary> /// Formats node at position /// </summary> public static void FormatNode <T>(ITextView textView, ITextBuffer textBuffer, int position) where T : class { IREditorDocument document = REditorDocument.TryFromTextBuffer(textBuffer); if (document != null) { ITextSnapshot snapshot = textBuffer.CurrentSnapshot; AstRoot ast = document.EditorTree.AstRoot; IAstNode node = ast.GetNodeOfTypeFromPosition <T>(position) as IAstNode; if (node != null) { UndoableFormatRange(textView, textBuffer, ast, node); } } }
private IKeywordScopeStatement GetFormatScope(int position, AstRoot ast) { try { var snapshot = EditorBuffer.CurrentSnapshot; var lineNumber = snapshot.GetLineNumberFromPosition(position); var line = snapshot.GetLineFromLineNumber(lineNumber); var lineText = line.GetText(); if (lineText.TrimEnd().EndsWithOrdinal("}")) { var scopeStatement = ast.GetNodeOfTypeFromPosition <IKeywordScopeStatement>(position); return(scopeStatement); } } catch (ArgumentException) { } return(null); }
/// <summary> /// Determines if a given position is in area where user /// specified indentation must be respected. For example, /// in multi-line list of function arguments, /// multi-line expressions and so on. /// </summary> private static bool RespectUserIndent(ITextBuffer textBuffer, AstRoot ast, int position) { // Look up nearest expression IAstNode node = ast.GetNodeOfTypeFromPosition <Expression>(position); if (IsMultilineNode(textBuffer, node)) { return(true); } node = ast.GetNodeOfTypeFromPosition <FunctionDefinition>(position); if (IsMultilineNode(textBuffer, node)) { return(true); } node = ast.GetNodeOfTypeFromPosition <FunctionCall>(position); if (IsMultilineNode(textBuffer, node)) { return(true); } return(false); }
private static bool DetermineFunction(AstRoot ast, int position, out IFunctionDefinition fd, out IVariable v, out FunctionCall fc) { fd = ast.FindFunctionDefinition(position, out v); fc = null; if (fd == null) { fc = ast.GetNodeOfTypeFromPosition <FunctionCall>(position); var name = fc.GetFunctionName(); if (string.IsNullOrEmpty(name) || !_s4FunctionNames.Contains(name)) { fc = null; } } return(fd != null || fc != null); }
/// <summary> /// Formats statement that the caret is at /// </summary> public static void FormatCurrentStatement(ITextView textView, ITextBuffer textBuffer, bool limitAtCaret = false, int caretOffset = 0) { SnapshotPoint?caretPoint = REditorDocument.MapCaretPositionFromView(textView); if (!caretPoint.HasValue) { return; } IREditorDocument document = REditorDocument.TryFromTextBuffer(textBuffer); if (document != null) { ITextSnapshot snapshot = textBuffer.CurrentSnapshot; AstRoot ast = document.EditorTree.AstRoot; IAstNode node = ast.GetNodeOfTypeFromPosition <IStatement>(Math.Max(0, caretPoint.Value + caretOffset)) as IAstNode; FormatNode(textView, textBuffer, node, limit: caretPoint.Value); } }
public static bool CanShowFileCompletion(AstRoot ast, int position, out string directory) { var node = ast.GetNodeOfTypeFromPosition <TokenNode>(position); directory = null; if (node != null && node.Token.TokenType == RTokenType.String) { var text = node.Root.TextProvider.GetText(node); // Bring file/folder completion when either string is empty or ends with / // assuming that / specifies directory where files are. if (text.Length == 2 || text.EndsWith("/\"", StringComparison.Ordinal) || text.EndsWith("/\'", StringComparison.Ordinal)) { directory = text; return(true); } } return(false); }
/// <summary> /// Formats statement that the caret is at /// </summary> public static void FormatCurrentStatement(ITextView textView, ITextBuffer textBuffer) { SnapshotPoint?caretPoint = MapCaretToBuffer(textView, textBuffer); if (!caretPoint.HasValue) { return; } IREditorDocument document = REditorDocument.TryFromTextBuffer(textBuffer); if (document != null) { ITextSnapshot snapshot = textBuffer.CurrentSnapshot; AstRoot ast = document.EditorTree.AstRoot; IAstNode node = ast.GetNodeOfTypeFromPosition <IStatement>(Math.Max(0, caretPoint.Value - 1)) as IAstNode; FormatNode(textView, textBuffer, node); } }
private static IKeywordScopeStatement GetFormatScope(ITextView textView, ITextBuffer textBuffer, AstRoot ast) { SnapshotPoint?caret = REditorDocument.MapCaretPositionFromView(textView); if (caret.HasValue) { try { int lineNumber = textBuffer.CurrentSnapshot.GetLineNumberFromPosition(caret.Value.Position); ITextSnapshotLine line = textBuffer.CurrentSnapshot.GetLineFromLineNumber(lineNumber); string lineText = line.GetText(); if (lineText.TrimEnd().EndsWith("}", StringComparison.Ordinal)) { IKeywordScopeStatement scopeStatement = ast.GetNodeOfTypeFromPosition <IKeywordScopeStatement>(caret.Value); return(scopeStatement); } } catch (Exception) { } } return(null); }
public static bool FormatRangeExact(ITextView textView, ITextBuffer textBuffer, ITextRange formatRange, AstRoot ast, RFormatOptions options, int scopeStatementPosition, bool respectUserIndent = true) { ITextSnapshot snapshot = textBuffer.CurrentSnapshot; Span spanToFormat = new Span(formatRange.Start, formatRange.Length); string spanText = snapshot.GetText(spanToFormat.Start, spanToFormat.Length); string trimmedSpanText = spanText.Trim(); if (trimmedSpanText == "}") { // Locate opening { and its statement var scopeNode = ast.GetNodeOfTypeFromPosition <IAstNodeWithScope>(spanToFormat.Start); if (scopeNode != null) { scopeStatementPosition = scopeNode.Start; } } RFormatter formatter = new RFormatter(options); string formattedText = formatter.Format(trimmedSpanText); formattedText = formattedText.Trim(); // there may be inserted line breaks after { formattedText = IndentLines(textBuffer, spanToFormat.Start, ast, formattedText, options, scopeStatementPosition, respectUserIndent); if (!spanText.Equals(formattedText, StringComparison.Ordinal)) { var selectionTracker = new RSelectionTracker(textView, textBuffer); RTokenizer tokenizer = new RTokenizer(); IReadOnlyTextRangeCollection <RToken> oldTokens = tokenizer.Tokenize(spanText); IReadOnlyTextRangeCollection <RToken> newTokens = tokenizer.Tokenize(formattedText); IncrementalTextChangeApplication.ApplyChangeByTokens( textBuffer, new TextStream(spanText), new TextStream(formattedText), oldTokens, newTokens, formatRange, Resources.AutoFormat, selectionTracker); return(true); } return(false); }
private static int IndentByIncompleteStatement(AstRoot ast, IEditorLine currentLine, IEditorLine prevLine, IAstNode scope, IREditorSettings settings) { // See if [ENTER] was hit in a middle of a statement. If it was hit in the first line of the statement, // indent one level deeper. Otherwise use block indent based on the previous line indent. // x <-[ENTER] // | // 1 // // x <-[ENTER] // | // // x <- // a +[ENTER] // | var snapshot = prevLine.Snapshot; var statement = ast.GetNodeOfTypeFromPosition <IStatement>(prevLine.End); if (statement != null) { return(prevLine.Contains(statement.Start) ? InnerIndentSizeFromNode(snapshot.EditorBuffer, scope, settings.FormatOptions) : GetBlockIndent(currentLine, settings)); } // The statement may be incomplete and hence expression parser // failed and hence there is no statement node in the AST. if (LineHasContinuation(prevLine)) { // We need to determine if last line was the first in the statement // or is it itself a continuation. if (prevLine.LineNumber > 0) { var prevPrevLine = snapshot.GetLineFromLineNumber(prevLine.LineNumber - 1); if (LineHasContinuation(prevPrevLine)) { return(GetBlockIndent(currentLine, settings)); } } return(InnerIndentSizeFromNode(snapshot.EditorBuffer, scope, settings.FormatOptions)); } return(0); }
private static IKeywordScopeStatement GetFormatScope(ITextView textView, ITextBuffer textBuffer, AstRoot ast) { var caret = textView.GetCaretPosition(textBuffer); if (caret.HasValue) { try { var lineNumber = textBuffer.CurrentSnapshot.GetLineNumberFromPosition(caret.Value.Position); var line = textBuffer.CurrentSnapshot.GetLineFromLineNumber(lineNumber); var lineText = line.GetText(); if (lineText.TrimEnd().EndsWith("}", StringComparison.Ordinal)) { var scopeStatement = ast.GetNodeOfTypeFromPosition <IKeywordScopeStatement>(caret.Value); return(scopeStatement); } } catch (Exception ex) when(!ex.IsCriticalException()) { } } return(null); }
/// <summary> /// Given position over function name retrieves name range and the function call. /// </summary> public static string GetFunctionName(this AstRoot ast, int position, out FunctionCall functionCall, out Variable functionVariable) { functionVariable = null; functionCall = null; ast.GetPositionNode(position, out var node); if (node == null) { return(null); } // In abc(de|f(x)) first find inner function, then outer. if (node is TokenNode && node.Parent is FunctionCall) { functionCall = (FunctionCall)node.Parent; } else { functionCall = ast.GetNodeOfTypeFromPosition <FunctionCall>(position); } functionVariable = functionCall?.RightOperand as Variable; return(functionVariable?.Name); }
/// <summary> /// Determines level of indentation in the line from AST and surrounding context. /// Called when user hits ENTER and editor needs to know level of indentation in /// the new line as well as when code is being auto-formatted and range formatter /// needs to know how to indent freshly formatted lines of code. /// </summary> /// <param name="line">Line to find the indent for</param> /// <param name="ast">Optional AST</param> /// <param name="formatting"> /// Indicates if current call is from formatter or /// from the core editor for indentation when user typed Enter. /// </param> /// <returns>Level of indent in spaces</returns> public static int GetSmartIndent(ITextSnapshotLine line, AstRoot ast = null, int originalIndentSizeInSpaces = -1, bool formatting = false) { ITextBuffer textBuffer = line.Snapshot.TextBuffer; ITextSnapshotLine prevLine = null; if (line.LineNumber == 0) { // Nothing to indent at the first line return(0); } if (ast == null) { IREditorDocument document = REditorDocument.TryFromTextBuffer(textBuffer); if (document == null) { return(0); } ast = document.EditorTree.AstRoot; } // The challenge here is to find scope to base the indent on. // The scope may or may not have braces and may or may not be closed. // Current line is normally empty so we use previous line data assuming // it is not empty. If previous line is empty, we do not look up // to the nearest non-empty. This is the same as C# behavior. // So we need to locate nearest node that implements IAstNodeWithScope // or the scope (implemeting IScope) itself is scope is just '{ }'. // First try based on the previous line. We will try start of the line // like in 'if(...)' { in order to locate 'if' and then, if nothing is found, // try end of the line as in 'x <- function(...) {' prevLine = line.Snapshot.GetLineFromLineNumber(line.LineNumber - 1); string prevLineText = prevLine.GetText(); if (prevLineText.Trim().Equals("else", StringComparison.Ordinal)) { // Quick short circuit for new 'else' since it is not in the ASt yet. return(GetBlockIndent(line) + REditorSettings.IndentSize); } // First, let's see if we are in a function argument list and then indent based on // the opening brace position. This needs to be done before looking for scopes // since function definition is a scope-defining statement. // Examples: 'call(a,\n<Enter>' or 'x <- function(a,<Enter>' if (prevLine.Length > 0) { var fc1 = ast.GetNodeOfTypeFromPosition <IFunction>(prevLine.End - 1); var fc2 = ast.GetNodeOfTypeFromPosition <IFunction>(line.Start); // Pick narrowest function. This happens when function definition appears // inside the argument list such as list(a = function(...)). var fc = fc2 ?? fc1; if (fc != null && fc.Arguments != null && fc.OpenBrace != null) { if (fc.CloseBrace == null || fc.CloseBrace.End > prevLine.End) { // We only want to indent here if position is in arguments and not in the function scope. if (line.Start >= fc.OpenBrace.End && !(fc.CloseBrace != null && line.Start >= fc.CloseBrace.End)) { if (originalIndentSizeInSpaces < 0) { // Indent one level deeper from the function definition line. var fcLine = line.Snapshot.GetLineFromPosition(fc.Start); if (fcLine.LineNumber == prevLine.LineNumber) { int fcIndentSize = IndentBuilder.TextIndentInSpaces(fcLine.GetText(), REditorSettings.TabSize); if (fc.CloseBrace == null || fc.CloseBrace.End >= (formatting ? line.Start : line.End)) { fcIndentSize += REditorSettings.IndentSize; } return(fcIndentSize); } else { return(GetBlockIndent(line)); } } else { return(originalIndentSizeInSpaces); } } } } } // Candidate position #1 is first non-whitespace character // in the the previous line int startOfNoWsOnPreviousLine = prevLine.Start + (prevLineText.Length - prevLineText.TrimStart().Length) + 1; // Try current new line so in case of 'if () { } else { | }' we find // the 'else' which defines the scope and not the parent 'if'. var scopeStatement1 = ast.GetNodeOfTypeFromPosition <IAstNodeWithScope>(line.Start); if (scopeStatement1 == null) { // If not found, try previous line that may define the indent scopeStatement1 = ast.GetNodeOfTypeFromPosition <IAstNodeWithScope>(startOfNoWsOnPreviousLine); if (scopeStatement1 == null) { // Line start position works for typical scope-defining statements like if() or while() // but it won't find function definition in 'x <- function(a) {' // Try end of the line instead var lastNonWsOnPreviousLine = Math.Max(0, prevLineText.TrimEnd().Length - 1); scopeStatement1 = ast.GetNodeOfTypeFromPosition <IAstNodeWithScope>(lastNonWsOnPreviousLine); // Verify that line we are asked to provide the smart indent for is actually inside // this scope since we could technically find end of x <- function(a) {} // when we went up one line. if (scopeStatement1?.Scope?.CloseCurlyBrace != null && !scopeStatement1.Contains(line.Start)) { scopeStatement1 = null; // line is outside of this scope. } } } IAstNodeWithScope scopeStatement; var scopeStatement2 = ast.GetNodeOfTypeFromPosition <IAstNodeWithScope>(startOfNoWsOnPreviousLine); // Locate standalone scope which is not a statement, if any var scope = ast.GetNodeOfTypeFromPosition <IScope>(prevLine.End); // Pick the narrowest item // so in case of // x <- function() { // if(...)<Enter> // } // we will use the 'if' and not the function definition var scopeCandidates = new List <IAstNode>() { scopeStatement1, scopeStatement2, scope }; var smallestScope = scopeCandidates.OrderBy(x => x != null ? x.Length : Int32.MaxValue).FirstOrDefault(); scopeStatement = smallestScope as IAstNodeWithScope; scope = smallestScope as IScope; // If IScope is a scope defined by the parent statement, use // the parent statement so in // x <- function(...) { // | // } // the indent in scope is defined by the function and not by the opening { if (scope != null) { var parentStarement = scope.Parent as IAstNodeWithScope; if (parentStarement != null && parentStarement.Scope == scope) { scopeStatement = parentStarement; scope = null; } } if (scopeStatement != null) { if (scopeStatement.Scope == null) { // There is nothing after statement that allows simple scope // such as in 'if(...)EOF' return(GetBlockIndent(line) + REditorSettings.IndentSize); } if (scopeStatement.Scope is SimpleScope) { // There is statement with a simple scope above such as 'if' without { }. // We need to check if the line that is being formatted is part of this scope. if (line.Start < scopeStatement.Scope.End) { // Indent line one level deeper that the statement return(GetBlockIndent(line) + REditorSettings.IndentSize); } // Line is not part of the scope, provide regular indent return(OuterIndentSizeFromNode(textBuffer, scopeStatement, REditorSettings.FormatOptions)); } // Check if line is the last line in a real scope (i.e. scope with { }) and only consists // of the closing }, it should be indented at the outer indent so closing scope aligns with // the beginning of the statement. if (scopeStatement.Scope.CloseCurlyBrace != null) { int endOfScopeLine = textBuffer.CurrentSnapshot.GetLineNumberFromPosition(scopeStatement.Scope.CloseCurlyBrace.Start); if (endOfScopeLine <= line.LineNumber) { return(OuterIndentSizeFromNode(textBuffer, scopeStatement, REditorSettings.FormatOptions)); } } if (scopeStatement.Scope.OpenCurlyBrace != null && REditorSettings.FormatOptions.BracesOnNewLine) { int startOfScopeLine = textBuffer.CurrentSnapshot.GetLineNumberFromPosition(scopeStatement.Scope.OpenCurlyBrace.Start); if (startOfScopeLine == line.LineNumber) { return(OuterIndentSizeFromNode(textBuffer, scopeStatement, REditorSettings.FormatOptions)); } } // We are inside a scope so provide inner indent return(InnerIndentSizeFromNode(textBuffer, scopeStatement, REditorSettings.FormatOptions)); } // Try locate the scope itself, if any if (scope != null && scope.OpenCurlyBrace != null) { if (scope.CloseCurlyBrace != null) { int endOfScopeLine = textBuffer.CurrentSnapshot.GetLineNumberFromPosition(scope.CloseCurlyBrace.Start); if (endOfScopeLine == line.LineNumber) { return(OuterIndentSizeFromNode(textBuffer, scope, REditorSettings.FormatOptions)); } } return(InnerIndentSizeFromNode(textBuffer, scope, REditorSettings.FormatOptions)); } return(0); }
public static int GetSmartIndent(ITextSnapshotLine line, AstRoot ast = null) { ITextBuffer textBuffer = line.Snapshot.TextBuffer; if (ast == null) { IREditorDocument document = REditorDocument.TryFromTextBuffer(textBuffer); if (document == null) { return(0); } ast = document.EditorTree.AstRoot; } if (line.LineNumber > 0) { ITextSnapshotLine prevLine = line.Snapshot.GetLineFromLineNumber(line.LineNumber - 1); string prevLineText = prevLine.GetText(); int nonWsPosition = prevLine.Start + (prevLineText.Length - prevLineText.TrimStart().Length) + 1; IAstNodeWithScope scopeStatement = ast.GetNodeOfTypeFromPosition <IAstNodeWithScope>(nonWsPosition); if (scopeStatement != null) { if (scopeStatement.Scope == null) { // No scope of any kind, use block indent return(GetBlockIndent(line) + REditorSettings.IndentSize); } if (scopeStatement.Scope is SimpleScope) { // There is statement with a simple scope above. We need to check // if the line that is being formatted is actually part of this scope. if (line.Start < scopeStatement.Scope.End) { // Indent line one level deeper that the statement return(GetBlockIndent(line) + REditorSettings.IndentSize); } // Line is not part of the scope, hence regular indent return(OuterIndentSizeFromNode(textBuffer, scopeStatement, REditorSettings.FormatOptions)); } // Check if line is the last line in scope and if so, // it should be indented at the outer indent if (scopeStatement.Scope.CloseCurlyBrace != null) { int endOfScopeLine = textBuffer.CurrentSnapshot.GetLineNumberFromPosition(scopeStatement.Scope.CloseCurlyBrace.Start); if (endOfScopeLine == line.LineNumber) { return(OuterIndentSizeFromNode(textBuffer, scopeStatement, REditorSettings.FormatOptions)); } } return(InnerIndentSizeFromNode(textBuffer, scopeStatement, REditorSettings.FormatOptions)); } } IAstNodeWithScope node = ast.GetNodeOfTypeFromPosition <IAstNodeWithScope>(line.Start); if (node != null && node.Scope != null && node.Scope.OpenCurlyBrace != null) { return(InnerIndentSizeFromNode(textBuffer, node, REditorSettings.FormatOptions)); } // See if we are in function arguments and indent at the function level var fc = ast.GetNodeOfTypeFromPosition <FunctionCall>(line.Start); if (fc != null && fc.Arguments != null && fc.OpenBrace != null && line.Start >= fc.OpenBrace.End) { return(InnerIndentSizeFromNode(textBuffer, fc, REditorSettings.FormatOptions)); } // If nothing is found, default to block indent return(GetBlockIndent(line)); }