public static bool FormatRange(ITextView textView, ITextBuffer textBuffer, ITextRange formatRange, AstRoot ast, RFormatOptions options, bool respectUserIndent = true) { ITextSnapshot snapshot = textBuffer.CurrentSnapshot; int start = formatRange.Start; int end = formatRange.End; // When user clicks editor margin to select a line, selection actually // ends in the beginning of the next line. In order to prevent formatting // of the next line that user did not select, we need to shrink span to // format and exclude the trailing line break. ITextSnapshotLine line = snapshot.GetLineFromPosition(formatRange.End); if (line.Start.Position == formatRange.End && formatRange.Length > 0) { if (line.LineNumber > 0) { line = snapshot.GetLineFromLineNumber(line.LineNumber - 1); end = line.End.Position; start = Math.Min(start, end); } } // Expand span to include the entire line ITextSnapshotLine startLine = snapshot.GetLineFromPosition(start); ITextSnapshotLine endLine = snapshot.GetLineFromPosition(end); formatRange = TextRange.FromBounds(startLine.Start, endLine.End); return FormatRangeExact(textView, textBuffer, formatRange, ast, options, -1, respectUserIndent); }
public string WriteTree(AstRoot ast) { _sb = new StringBuilder(); _indent = 0; _ast = ast; foreach (IAstNode node in ast.Children) { WriteNode(node); } if (_ast.Errors.Count > 0) { _sb.AppendLine(); foreach (var error in _ast.Errors) { _sb.AppendFormat(CultureInfo.InvariantCulture, "{0} {1} [{2}...{3})\r\n", error.ErrorType.ToString(), error.Location.ToString(), error.Start, error.End); } } string text = _sb.ToString(); _sb = null; _ast = null; return text; }
private static bool GetFunction(AstRoot astRoot, ref int position, out FunctionCall functionCall, out Variable functionVariable) { functionVariable = null; functionCall = astRoot.GetNodeOfTypeFromPosition<FunctionCall>(position); 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; return functionVariable != null; } return false; }
public RCompletionContext(ICompletionSession session, ITextBuffer textBuffer, AstRoot ast, int position) { Session = session; TextBuffer = textBuffer; Position = position; AstRoot = ast; }
internal int ComputeCurrentParameter(ITextSnapshot snapshot, AstRoot ast, int position) { ParameterInfo parameterInfo = SignatureHelp.GetParametersInfoFromBuffer(ast, snapshot, position); int index = 0; if (parameterInfo != null) { index = parameterInfo.ParameterIndex; if (parameterInfo.NamedParameter) { // A function f <- function(foo, bar) is said to have formal parameters "foo" and "bar", // and the call f(foo=3, ba=13) is said to have (actual) arguments "foo" and "ba". // R first matches all arguments that have exactly the same name as a formal parameter. // Two identical argument names cause an error. Then, R matches any argument names that // partially matches a(yet unmatched) formal parameter. But if two argument names partially // match the same formal parameter, that also causes an error. Also, it only matches // formal parameters before ... So formal parameters after ... must be specified using // their full names. Then the unnamed arguments are matched in positional order to // the remaining formal arguments. int argumentIndexInSignature = _signatureInfo.GetArgumentIndex(parameterInfo.ParameterName, REditorSettings.PartialArgumentNameMatch); if (argumentIndexInSignature >= 0) { index = argumentIndexInSignature; } } } return index; }
public ParseContext(ITextProvider textProvider, ITextRange range, TokenStream<RToken> tokens, IReadOnlyList<RToken> comments, IExpressionTermFilter filter = null) { AstRoot = new AstRoot(textProvider); TextProvider = textProvider; Tokens = tokens; TextRange = range; Scopes = new Stack<IScope>(); Expressions = new Stack<Expression>(); Comments = comments; ExpressionTermFilter = filter ?? new DefaultExpressionTermFilter(); }
public static void CompareTrees(string expected, AstRoot actualTree) { AstWriter astWriter = new AstWriter(); string actual = astWriter.WriteTree(actualTree); string expectedLine, actualLine; int index; int result = BaselineCompare.CompareLines(expected, actual, out expectedLine, out actualLine, out index); result.Should().Be(0, "Line at {0} should be {1}, but found {2}, different at position {3}", result, expectedLine, actualLine, index); }
internal static Task AugmentSignatureHelpSessionAsync(this SignatureHelpSource signatureHelpSource, ISignatureHelpSession session, IList<ISignature> signatures, AstRoot ast) { var tcs = new TaskCompletionSource<object>(); var ready = signatureHelpSource.AugmentSignatureHelpSession(session, signatures, ast, (o, p) => { signatureHelpSource.AugmentSignatureHelpSession(session, signatures, ast, null); tcs.TrySetResult(null); }); if (ready) { tcs.TrySetResult(null); } return tcs.Task; }
/// <summary> /// Given position in a text buffer finds method name. /// </summary> public static string GetFunctionNameFromBuffer(AstRoot astRoot, ref int position, out int signatureEnd) { FunctionCall functionCall; Variable functionVariable; signatureEnd = -1; if (GetFunction(astRoot, ref position, out functionCall, out functionVariable)) { signatureEnd = functionCall.End; return functionVariable.Name; } return null; }
public static void CompareTrees(EditorTree editorTree, AstRoot tree2) { #if ___DEBUG var cc1 = editorTree.ParseTree.CommentCollection; var cc2 = tree2.CommentCollection; Debug.Assert(cc1.Count == cc2.Count); for (int i = 0; i < cc1.Count; i++) { Debug.Assert(cc1[i].Start == cc2[i].Start); Debug.Assert(cc1[i].Length == cc2[i].Length); } CompareNodes(editorTree, editorTree.ParseTree.RootNode, tree2.RootNode); #endif }
internal static Task<ITrackingSpan> AugmentQuickInfoSessionAsync(this QuickInfoSource quickInfoSource, AstRoot ast, int caretPosition, IQuickInfoSession quickInfoSession, IList<object> quickInfoContent) { var tcs = new TaskCompletionSource<ITrackingSpan>(); ITrackingSpan applicableSpan; var ready = quickInfoSource.AugmentQuickInfoSession(ast, caretPosition, quickInfoSession, quickInfoContent, out applicableSpan, (o, p) => { ITrackingSpan result; quickInfoSource.AugmentQuickInfoSession(ast, caretPosition, quickInfoSession, quickInfoContent, out result, null, p); tcs.TrySetResult(result); }, null); if (ready) { tcs.TrySetResult(applicableSpan); } return tcs.Task; }
internal bool AugmentQuickInfoSession(AstRoot ast, int position, IQuickInfoSession session, IList<object> quickInfoContent, out ITrackingSpan applicableToSpan, Action<object, string> retriggerAction, string packageName) { int signatureEnd = position; applicableToSpan = null; string functionName = SignatureHelp.GetFunctionNameFromBuffer(ast, ref position, out signatureEnd); if (!string.IsNullOrEmpty(functionName)) { ITextSnapshot snapshot = session.TextView.TextBuffer.CurrentSnapshot; position = Math.Min(signatureEnd, position); int start = Math.Min(position, snapshot.Length); int end = Math.Min(signatureEnd, snapshot.Length); applicableToSpan = snapshot.CreateTrackingSpan(Span.FromBounds(start, end), SpanTrackingMode.EdgeInclusive); packageName = packageName ?? _packageName; _packageName = null; var functionInfo = FunctionIndex.GetFunctionInfo(functionName, packageName, retriggerAction, session); if (functionInfo != null && functionInfo.Signatures != null) { foreach (ISignatureInfo sig in functionInfo.Signatures) { string signatureString = sig.GetSignatureString(functionName); int wrapLength = Math.Min(SignatureInfo.MaxSignatureLength, signatureString.Length); string text; if (string.IsNullOrWhiteSpace(functionInfo.Description)) { text = string.Empty; } else { /// VS may end showing very long tooltip so we need to keep /// description reasonably short: typically about /// same length as the function signature. text = signatureString + "\r\n" + functionInfo.Description.Wrap(wrapLength); } if (text.Length > 0) { quickInfoContent.Add(text); return true; } } } } return false; }
/// <summary> /// Attempts to insert Roxygen documentation block based /// on the user function signature. /// </summary> public static bool TryInsertBlock(ITextBuffer textBuffer, AstRoot ast, int position) { // First determine if position is right before the function declaration var snapshot = textBuffer.CurrentSnapshot; ITextSnapshotLine line = null; var lineNumber = snapshot.GetLineNumberFromPosition(position); for (int i = lineNumber; i < snapshot.LineCount; i++) { line = snapshot.GetLineFromLineNumber(i); if (line.Length > 0) { break; } } if (line == null || line.Length == 0) { return false; } Variable v; int offset = line.Length - line.GetText().TrimStart().Length + 1; if (line.Start + offset >= snapshot.Length) { return false; } IFunctionDefinition fd = FunctionDefinitionExtensions.FindFunctionDefinition(textBuffer, ast, line.Start + offset, out v); if (fd != null && v != null && !string.IsNullOrEmpty(v.Name)) { int definitionStart = Math.Min(v.Start, fd.Start); Span? insertionSpan = GetRoxygenBlockPosition(snapshot, definitionStart); if (insertionSpan.HasValue) { string lineBreak = snapshot.GetLineFromPosition(position).GetLineBreakText(); if (string.IsNullOrEmpty(lineBreak)) { lineBreak = "\n"; } string block = GenerateRoxygenBlock(v.Name, fd, lineBreak); if (block.Length > 0) { if (insertionSpan.Value.Length == 0) { textBuffer.Insert(insertionSpan.Value.Start, block + lineBreak); } else { textBuffer.Replace(insertionSpan.Value, block); } return true; } } } return false; }
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; }
public bool AugmentSignatureHelpSession(ISignatureHelpSession session, IList<ISignature> signatures, AstRoot ast, Action<object, string> triggerSession) { ITextSnapshot snapshot = _textBuffer.CurrentSnapshot; int position = session.GetTriggerPoint(_textBuffer).GetPosition(snapshot); // Retrieve parameter positions from the current text buffer snapshot ParameterInfo parametersInfo = SignatureHelp.GetParametersInfoFromBuffer(ast, snapshot, position); if (parametersInfo != null) { position = Math.Min(parametersInfo.SignatureEnd, position); int start = Math.Min(position, snapshot.Length); int end = Math.Min(parametersInfo.SignatureEnd, snapshot.Length); ITrackingSpan applicableToSpan = snapshot.CreateTrackingSpan(Span.FromBounds(start, end), SpanTrackingMode.EdgeInclusive); IFunctionInfo functionInfo = null; // First try user-defined function functionInfo = ast.GetUserFunctionInfo(parametersInfo.FunctionName, position); if (functionInfo == null) { var functionIndex = _shell.ExportProvider.GetExportedValue<IFunctionIndex>(); // Then try package functions var packageName = _packageName; _packageName = null; // Get collection of function signatures from documentation (parsed RD file) functionInfo = functionIndex.GetFunctionInfo(parametersInfo.FunctionName, packageName, triggerSession, session); } if (functionInfo != null && functionInfo.Signatures != null) { foreach (ISignatureInfo signatureInfo in functionInfo.Signatures) { ISignature signature = CreateSignature(session, parametersInfo.FunctionName, functionInfo, signatureInfo, applicableToSpan, ast, position); signatures.Add(signature); } session.Properties["functionInfo"] = functionInfo; return true; } } return false; }
/// <summary> /// Given position in a text buffer finds method name, /// parameter index as well as where method signature ends. /// </summary> public static ParameterInfo GetParametersInfoFromBuffer(AstRoot astRoot, ITextSnapshot snapshot, int position) { FunctionCall functionCall; Variable functionVariable; int parameterIndex = -1; string parameterName; bool namedParameter = false; if (!GetFunction(astRoot, ref position, out functionCall, out functionVariable)) { return null; } parameterIndex = functionCall.GetParameterIndex(position); parameterName = functionCall.GetParameterName(parameterIndex, out namedParameter); if (!string.IsNullOrEmpty(functionVariable.Name) && functionCall != null && parameterIndex >= 0) { return new ParameterInfo(functionVariable.Name, functionCall, parameterIndex, parameterName, namedParameter); } return null; }
/// <summary> /// Populates R completion list for a given position /// </summary> /// <param name="position">Position in R text buffer</param> /// <param name="session">Completion session</param> /// <param name="completionSets">Completion sets to add to</param> /// <param name="ast">Document abstract syntax tree</param> internal void PopulateCompletionList(int position, ICompletionSession session, IList<CompletionSet> completionSets, AstRoot ast) { RCompletionContext context = new RCompletionContext(session, _textBuffer, ast, position); bool autoShownCompletion = true; if (session.TextView.Properties.ContainsProperty(CompletionController.AutoShownCompletion)) autoShownCompletion = session.TextView.Properties.GetProperty<bool>(CompletionController.AutoShownCompletion); IReadOnlyCollection<IRCompletionListProvider> providers = RCompletionEngine.GetCompletionForLocation(context, autoShownCompletion, _shell); // Position is in R as is the applicable spa, so no need to map down Span? applicableSpan = GetApplicableSpan(position, session); if (applicableSpan.HasValue) { ITrackingSpan trackingSpan = context.TextBuffer.CurrentSnapshot.CreateTrackingSpan(applicableSpan.Value, SpanTrackingMode.EdgeInclusive); List<RCompletion> completions = new List<RCompletion>(); bool sort = true; foreach (IRCompletionListProvider provider in providers) { IReadOnlyCollection<RCompletion> entries = provider.GetEntries(context); Debug.Assert(entries != null); if (entries.Count > 0) { completions.AddRange(entries); } sort &= provider.AllowSorting; } if (sort) { completions.Sort(RCompletion.CompareOrdinal); completions.RemoveDuplicates(RCompletion.CompareOrdinal); } CompletionSet completionSet = new RCompletionSet(session.TextView.TextBuffer, trackingSpan, completions); completionSets.Add(completionSet); } }
private ISignature CreateSignature(ISignatureHelpSession session, string functionName, IFunctionInfo functionInfo, ISignatureInfo signatureInfo, ITrackingSpan span, AstRoot ast, int position) { SignatureHelp sig = new SignatureHelp(session, _textBuffer, functionName, string.Empty, signatureInfo, _shell); List<IParameter> paramList = new List<IParameter>(); // Locus points in the pretty printed signature (the one displayed in the tooltip) var locusPoints = new List<int>(); string signatureString = signatureInfo.GetSignatureString(functionName, locusPoints); sig.Content = signatureString; sig.ApplicableToSpan = span; sig.Documentation = functionInfo.Description?.Wrap(Math.Min(SignatureInfo.MaxSignatureLength, sig.Content.Length)); Debug.Assert(locusPoints.Count == signatureInfo.Arguments.Count + 1); for (int i = 0; i < signatureInfo.Arguments.Count; i++) { IArgumentInfo p = signatureInfo.Arguments[i]; if (p != null) { int locusStart = locusPoints[i]; int locusLength = locusPoints[i + 1] - locusStart; Debug.Assert(locusLength >= 0); Span locus = new Span(locusStart, locusLength); /// VS may end showing very long tooltip so we need to keep /// description reasonably short: typically about /// same length as the function signature. paramList.Add( new SignatureParameter( p.Description.Wrap( Math.Min(SignatureInfo.MaxSignatureLength, sig.Content.Length)), locus, locus, p.Name, sig)); } } sig.Parameters = new ReadOnlyCollection<IParameter>(paramList); sig.ComputeCurrentParameter(ast, position); return sig; }
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; }
public TextChangeProcessor(EditorTree editorTree, AstRoot astRoot, Func<bool> cancelCallback = null) { _editorTree = editorTree; _astRoot = astRoot; _cancelCallback = cancelCallback; }
/// <summary> /// Outlines comments that define sections such as /// # NAME --- /// </summary> /// <returns>True if names changed and outline regions need to be rebuilt</returns> private void OutlineSections(AstRoot ast, OutliningContext context) { // Collect comments that define sections var snapshot = EditorTree.TextSnapshot; var sections = ast.Comments.Where(c => { var text = snapshot.GetText(new Span(c.Start, c.Length)); // Section looks like # [NAME] -------- return text.TrimEnd().EndsWithOrdinal("---") && text.IndexOfAny(CharExtensions.LineBreakChars) < 0; }).ToArray(); // Construct collapsible regions var ranges = new List<ITextRange>(); for (int i = 0; i < sections.Length; i++) { var startLine = snapshot.GetLineFromPosition(sections[i].Start); int end = -1; var text = snapshot.GetText(new Span(sections[i].Start, sections[i].Length)); var displayText = text.Substring(0, text.IndexOfOrdinal("---")).Trim(); if (i < sections.Length - 1) { var endLineNumber = snapshot.GetLineNumberFromPosition(sections[i + 1].Start); if (endLineNumber > startLine.LineNumber) { end = snapshot.GetLineFromLineNumber(endLineNumber - 1).End; } } else { end = snapshot.Length; } if (end > startLine.Start) { ranges.Add(sections[i]); // Trim trailing whitespace in user-defined regions var range = TextRange.FromBounds(startLine.Start, end); text = snapshot.GetText(new Span(range.Start, range.Length)); var trimBy = text.Length - text.TrimEnd().Length; range = TextRange.FromBounds(range.Start, range.End - trimBy); context.Regions.Add(new ROutlineRegion(EditorDocument.TextBuffer, range, displayText)); } } // Track changes in section names _forceRegionsChange = _sections != null && _sections.Changed; _sections?.Dispose(); _sections = new RSectionsCollection(EditorTree, ranges); }
public static ITextView MakeTextView(string content, int caretPosition, out AstRoot ast) { ast = RParser.Parse(content); ITextBuffer textBuffer = new TextBufferMock(content, RContentTypeDefinition.ContentType); return new TextViewMock(textBuffer, caretPosition); }
public virtual void ComputeCurrentParameter(AstRoot ast, int position) { if (Parameters == null || Parameters.Count == 0 || SubjectBuffer == null) { this.CurrentParameter = null; return; } var parameterIndex = ComputeCurrentParameter(SubjectBuffer.CurrentSnapshot, ast, position); if (parameterIndex < Parameters.Count) { this.CurrentParameter = Parameters[parameterIndex]; } else { //too many commas, so use the last parameter as the current one. this.CurrentParameter = Parameters[Parameters.Count - 1]; } }
private bool IsClosingToken(RToken token, AstRoot ast = null) { switch (token.TokenType) { case RTokenType.Comma: case RTokenType.CloseBrace: case RTokenType.CloseSquareBracket: case RTokenType.CloseDoubleSquareBracket: case RTokenType.Semicolon: return true; } return false; }
/// <summary> /// Appends indentation to each line so formatted text appears properly /// indented inside the host document (script block in HTML page). /// </summary> private static void IndentLines(ITextView textView, ITextBuffer textBuffer, ITextRange range, AstRoot ast, RFormatOptions options, int originalIndentSizeInSpaces) { ITextSnapshot snapshot = textBuffer.CurrentSnapshot; ITextSnapshotLine firstLine = snapshot.GetLineFromPosition(range.Start); ITextSnapshotLine lastLine = snapshot.GetLineFromPosition(range.End); IREditorDocument document = REditorDocument.TryFromTextBuffer(textBuffer); for (int i = firstLine.LineNumber; i <= lastLine.LineNumber; i++) { // Snapshot is updated after each insertion so do not cache ITextSnapshotLine line = textBuffer.CurrentSnapshot.GetLineFromLineNumber(i); int indent = SmartIndenter.GetSmartIndent(line, ast, originalIndentSizeInSpaces, formatting: true); if (indent > 0 && line.Length > 0 && line.Start >= range.Start) { // Check current indentation and correct for the difference int currentIndentSize = IndentBuilder.TextIndentInSpaces(line.GetText(), options.TabSize); indent = Math.Max(0, indent - currentIndentSize); if (indent > 0) { string indentString = IndentBuilder.GetIndentString(indent, options.IndentType, options.TabSize); textBuffer.Insert(line.Start, indentString); if (document == null) { // Typically this is a test scenario only. In the real editor // instance document is available and automatically updates AST // when whitespace inserted, not no manual update is necessary. ast.ReflectTextChange(line.Start, 0, indentString.Length, new TextProvider(textBuffer.CurrentSnapshot)); } } } } }
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 IFunctionDefinition FindFunctionDefinition(ITextBuffer textBuffer, AstRoot ast, int position, out Variable v) { v = null; var exp = ast.GetNodeOfTypeFromPosition<IExpressionStatement>(position); return exp?.GetVariableOrFunctionDefinition(out v); }
public RSignatureHelpContext(ISignatureHelpSession session, ITextBuffer textBuffer, AstRoot ast, int position) { Session = session; TextBuffer = textBuffer; Position = position; AstRoot = ast; }
public static ITextView MakeTextView(string content, out AstRoot ast) { return MakeTextView(content, 0, out ast); }
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); }
/// <summary> /// Locates deepest node of a particular type /// </summary> public static T GetNodeOfTypeFromPosition <T>(this AstRoot ast, int position, bool includeEnd = false) where T : class { return(GetSpecificNodeFromPosition <T>(ast, position, (IAstNode n) => { return n is T; }, includeEnd) as T); }
public static bool IsPositionInsideString(this AstRoot ast, int position) { // We don't want to auto-format inside strings return(ast.NodeFromPosition(position) is TokenNode node && node.Token.TokenType == RTokenType.String); }