/// <summary> /// Determines if current caret position is in the same function /// argument list as before or is it a different one and signature /// help session should be dismissed and re-triggered. This is helpful /// when user types nested function calls such as 'a(b(c(...), d(...)))' /// </summary> public static bool IsSameSignatureContext(this IEditorView editorView, IEditorBuffer editorBuffer, IServiceContainer services) { var broker = services.GetService <IViewSignatureBroker>(); var sessions = broker.GetSessions(editorView); Debug.Assert(sessions.Count < 2); if (sessions.Count == 1) { sessions[0].Properties.TryGetProperty("functionInfo", out IFunctionInfo sessionFunctionInfo); if (sessionFunctionInfo != null) { try { var document = editorBuffer.GetEditorDocument <IREditorDocument>(); document.EditorTree.EnsureTreeReady(); var parametersInfo = document.EditorTree.AstRoot.GetSignatureInfoFromBuffer(editorBuffer.CurrentSnapshot, editorView.Caret.Position.Position); return(parametersInfo != null && parametersInfo.FunctionName == sessionFunctionInfo.Name); } catch (ArgumentException) { } } } return(false); }
/// <summary> /// Incrementally applies whitespace change to the buffer /// having old and new tokens produced from the 'before formatting' /// and 'after formatting' versions of the same text. /// </summary> /// <param name="editorBuffer">Text buffer to apply changes to</param> /// <param name="oldTextProvider">Text provider of the text fragment before formatting</param> /// <param name="newTextProvider">Text provider of the formatted text</param> /// <param name="oldTokens">Tokens from the 'before' text fragment</param> /// <param name="newTokens">Tokens from the 'after' text fragment</param> /// <param name="formatRange">Range that is being formatted in the text buffer</param> /// <param name="transactionName">Name of the undo transaction to open</param> /// <param name="selectionTracker"> /// Selection tracker object that will save, /// track and restore selection after changes have been applied.</param> /// <param name="additionalAction"> /// Action to perform after changes are applies by undo unit is not yet closed. /// </param> public void ApplyChange( IEditorBuffer editorBuffer, ITextProvider oldTextProvider, ITextProvider newTextProvider, IReadOnlyList <ITextRange> oldTokens, IReadOnlyList <ITextRange> newTokens, ITextRange formatRange, string transactionName, ISelectionTracker selectionTracker, Action additionalAction = null) { Debug.Assert(oldTokens.Count == newTokens.Count); if (oldTokens.Count == newTokens.Count) { using (CreateSelectionUndo(selectionTracker, _services, transactionName)) { var textBuffer = editorBuffer.As <ITextBuffer>(); using (var edit = textBuffer.CreateEdit()) { var edits = CalculateChanges(oldTextProvider, newTextProvider, oldTokens, newTokens, formatRange); foreach (var e in edits) { if (string.IsNullOrEmpty(e.NewText)) { edit.Delete(e.Range.ToSpan()); } else if (e.Range.Length > 0) { edit.Replace(e.Range.ToSpan(), e.NewText); } else { edit.Insert(e.Range.Start, e.NewText); } } edit.Apply(); additionalAction?.Invoke(); } } } }
public static int InnerIndentSizeFromNode(IEditorBuffer editorBuffer, IAstNode node, RFormatOptions options) { if (node != null) { // Scope indentation is based on the scope defining node i.e. // x <- function(a) { // | // } // caret indent is based on the function definition and not // on the position of the opening { var scope = node as IScope; if (scope != null) { if (node.Parent is IAstNodeWithScope scopeDefiningNode && scopeDefiningNode.Scope == scope) { node = scopeDefiningNode; } } var startLine = editorBuffer.CurrentSnapshot.GetLineFromPosition(node.Start); return(InnerIndentSizeFromLine(startLine, options)); } return(0); }
private static Hover ToHover(IReadOnlyList <IRFunctionQuickInfo> e, IEditorBuffer buffer) { if (e == null || e.Count == 0) { return(new Hover()); } var info = e[0]; var content = info.Content?.FirstOrDefault(); if (!string.IsNullOrEmpty(content)) { var snapshot = buffer.CurrentSnapshot; var start = info.ApplicableToRange.GetStartPoint(snapshot); var end = info.ApplicableToRange.GetEndPoint(snapshot); return(new Hover { contents = new MarkupContent { value = content }, range = buffer.ToLineRange(start, end) }); } return(new Hover()); }
private bool CanFormatRange(IEditorView editorView, IEditorBuffer editorBuffer, ITextRange formatRange) { // Make sure we are not formatting damaging the projected range in R Markdown // which looks like ```{r. 'r' should not separate from {. var locator = _services.GetService <IContentTypeServiceLocator>(); var provider = locator.GetService <IContainedLanguageHostProvider>("R"); var host = provider?.GetContainedLanguageHost(editorView, editorBuffer); if (host != null) { var snapshot = editorBuffer.CurrentSnapshot; var startLine = snapshot.GetLineNumberFromPosition(formatRange.Start); var endLine = snapshot.GetLineNumberFromPosition(formatRange.End); for (var i = startLine; i <= endLine; i++) { if (!host.CanFormatLine(editorView, editorBuffer, i)) { return(false); } } } return(true); }
/// <summary> /// Incrementally applies whitespace change to the buffer /// having old and new tokens produced from the 'before formatting' /// and 'after formatting' versions of the same text. /// </summary> /// <param name="editorBuffer">Text buffer to apply changes to</param> /// <param name="oldTextProvider">Text provider of the text fragment before formatting</param> /// <param name="newTextProvider">Text provider of the formatted text</param> /// <param name="oldTokens">Tokens from the 'before' text fragment</param> /// <param name="newTokens">Tokens from the 'after' text fragment</param> /// <param name="formatRange">Range that is being formatted in the text buffer</param> /// <param name="transactionName">Not used in VS Code</param> /// <param name="selectionTracker">Not used in VS Code</param> /// <param name="additionalAction"> /// Action to perform after changes are applies by undo unit is not yet closed. /// </param> public void ApplyChange( IEditorBuffer editorBuffer, ITextProvider oldTextProvider, ITextProvider newTextProvider, IReadOnlyList <ITextRange> oldTokens, IReadOnlyList <ITextRange> newTokens, ITextRange formatRange, string transactionName, ISelectionTracker selectionTracker, Action additionalAction = null) { Debug.Assert(oldTokens.Count == newTokens.Count); if (oldTokens.Count != newTokens.Count) { return; } var edits = CalculateChanges(oldTextProvider, newTextProvider, oldTokens, newTokens, formatRange); foreach (var e in edits) { if (string.IsNullOrEmpty(e.NewText)) { editorBuffer.Delete(e.Range); } else if (e.Range.Length > 0) { editorBuffer.Replace(e.Range, e.NewText); } else { editorBuffer.Insert(e.Range.Start, e.NewText); } } additionalAction?.Invoke(); }
private bool FormatRangeExact(IEditorView editorView, IEditorBuffer editorBuffer, ITextRange formatRange) { var snapshot = editorBuffer.CurrentSnapshot; var spanText = snapshot.GetText(formatRange); var trimmedSpanText = spanText.Trim(); var formatter = new RFormatter(_settings.FormatOptions); var formattedText = formatter.Format(trimmedSpanText); formattedText = formattedText.Trim(); // There may be inserted line breaks after { // Extract existing indent before applying changes. Existing indent // may be used by the smart indenter for function argument lists. var startLine = snapshot.GetLineFromPosition(formatRange.Start); var originalIndentSizeInSpaces = IndentBuilder.TextIndentInSpaces(startLine.GetText(), _settings.IndentSize); var selectionTracker = GetSelectionTracker(editorView, editorBuffer, formatRange); var tokenizer = new RTokenizer(); var oldTokens = tokenizer.Tokenize(spanText); var newTokens = tokenizer.Tokenize(formattedText); var wsChangeHandler = _services.GetService <IIncrementalWhitespaceChangeHandler>(); wsChangeHandler.ApplyChange( editorBuffer, new TextStream(spanText), new TextStream(formattedText), oldTokens, newTokens, formatRange, Microsoft.R.Editor.Resources.AutoFormat, selectionTracker, () => { var ast = UpdateAst(editorBuffer); // Apply indentation IndentLines(editorBuffer, new TextRange(formatRange.Start, formattedText.Length), ast, originalIndentSizeInSpaces); }); return(true); }
public static ITextSnapshot TextSnapshot(this IEditorBuffer buffer) => buffer.CurrentSnapshot.As <ITextSnapshot>();
public EditorBufferSnapshot(IEditorBuffer editorBuffer, string content, int version) { EditorBuffer = editorBuffer; _content = content; Version = version; }
public static Range ToLineRange(this ITextRange textRange, IEditorBuffer editorBuffer) => new Range { Start = editorBuffer.CurrentSnapshot.ToLinePosition(textRange.Start), End = editorBuffer.CurrentSnapshot.ToLinePosition(textRange.End) };
public static int ToStreamPosition(this IEditorBuffer editorBuffer, Position position) => editorBuffer.CurrentSnapshot.ToStreamPosition(position.Line, position.Character);
/// <summary> /// Adds service to this instance of the text buffer /// </summary> public static void AddService <T>(this IEditorBuffer editorBuffer, T service) where T : class => editorBuffer.Services.AddService <T>(service);
/// <summary> /// Removes service from this instance of the text buffer /// </summary> public static void RemoveService(this IEditorBuffer editorBuffer, object service) => editorBuffer.Services.RemoveService(service);
public RIntellisenseContext(IEditorIntellisenseSession session, IEditorBuffer editorBuffer, AstRoot ast, int position, bool autoShown = true) : base(session, editorBuffer, position) { AstRoot = ast; AutoShownCompletion = autoShown; }
public void HandleTyping(char typedChar, int position, IEditorBuffer editorBuffer = null, IIncrementalWhitespaceChangeHandler changeHandler = null) { if (!Settings.AutoFormat || (!Settings.FormatScope && typedChar == '}')) { return; } EditorBuffer = editorBuffer ?? EditorBuffer; var document = EditorBuffer.GetEditorDocument <IREditorDocument>(); // AST may or may not be ready. Upto the caller to decide if it is worth waiting. var ast = document.EditorTree.AstRoot; // Make sure we are not formatting damaging the projected range in R Markdown // which looks like ```{r. 'r' should not separate from {. if (!CanFormatContainedLanguageLine(position, typedChar)) { return; } // We don't want to auto-format inside strings if (ast.IsPositionInsideString(position)) { return; } var fo = new FormatOperations(Services, EditorView, EditorBuffer, _changeHandler); if (typedChar.IsLineBreak()) { // Special case for hitting caret after } and before 'else'. We do want to format // the construct as '} else {' but if user types Enter after } and we auto-format // it will look as if the editor just eats the Enter. Instead, we will not be // autoformatting in this specific case. User can always format either the document // or select the block and reformat it. if (!IsBetweenCurlyAndElse(position)) { var scopeStatement = GetFormatScope(position, ast); // Do not format large scope blocks for performance reasons if (scopeStatement != null && scopeStatement.Length < 200) { fo.FormatNode(scopeStatement); } else if (CanFormatLine(position, -1)) { fo.FormatViewLine(-1); } } } else if (typedChar == ';') { // Verify we are at the end of the string and not in a middle // of another string or inside a statement. var line = EditorBuffer.CurrentSnapshot.GetLineFromPosition(position); var positionInLine = position - line.Start; var lineText = line.GetText(); if (positionInLine >= lineText.TrimEnd().Length) { fo.FormatViewLine(0); } } else if (typedChar == '}') { fo.FormatCurrentStatement(limitAtCaret: true, caretOffset: -1); } }
public IEnumerable <IEditorView> GetAllViews(IEditorBuffer editorBuffer) => TextViewConnectionListener.GetViewsForBuffer(editorBuffer.As <ITextBuffer>()).Select(v => v.ToEditorView()).Where(v => v != null);
private static bool IsProjectedBuffer(IEditorBuffer editorBuffer) { var document = editorBuffer.GetEditorDocument <IREditorDocument>(); return(document.IsRepl || document.PrimaryView?.EditorBuffer != editorBuffer); }
public IEditorView GetPrimaryView(IEditorBuffer editorBuffer) => GetAllViews(editorBuffer).FirstOrDefault();
public EditorTreeMock(IEditorBuffer editorBuffer, AstRoot ast) { EditorBuffer = editorBuffer; AstRoot = ast; }
/// <summary> /// Adds service to this instance of the text buffer /// </summary> public static void AddService(this IEditorBuffer editorBuffer, object service) => editorBuffer.Services.AddService(service);
public static SnapshotPoint?GetCaretPosition(this ITextView textView, IEditorBuffer editorBuffer = null) => textView.GetCaretPosition(editorBuffer?.As <ITextBuffer>());
public ISnapshotPoint GetCaretPosition(IEditorBuffer buffer = null) => new SnapshotPoint(EditorBuffer.CurrentSnapshot, Caret.Position.Position);
/// <summary> /// Attempts to insert Roxygen documentation block based /// on the user function signature. /// </summary> public static bool TryInsertBlock(IEditorBuffer editorBuffer, AstRoot ast, int position) { // First determine if position is right before the function declaration var linePosition = DeterminePosition(editorBuffer, position); if (linePosition < 0) { return(false); } if (!DetermineFunction(ast, linePosition, out IFunctionDefinition fd, out IVariable v, out FunctionCall fc)) { return(false); } int definitionStart; if (fd != null && v != null) { definitionStart = Math.Min(v.Start, fd.Start); } else if (fc != null) { definitionStart = fc.Start; } else { return(false); } var insertionSpan = GetRoxygenBlockPosition(editorBuffer.CurrentSnapshot, definitionStart); if (insertionSpan == null) { return(false); } var lineBreak = editorBuffer.CurrentSnapshot.GetLineFromPosition(position).LineBreak; if (string.IsNullOrEmpty(lineBreak)) { lineBreak = "\n"; } var block = fd != null ? GenerateRoxygenBlock(v.Name, fd, lineBreak) : GenerateRoxygenBlock(fc, lineBreak); if (block.Length == 0) { return(false); } if (insertionSpan.Length == 0) { editorBuffer.Insert(insertionSpan.Start, block + lineBreak); } else { editorBuffer.Replace(insertionSpan, block); } return(true); }
public static Position ToLinePosition(this IEditorBuffer editorBuffer, int position) => editorBuffer.CurrentSnapshot.ToLinePosition(position);
/// <summary> /// Uncomments selected lines or current line if range has zero length. /// Only removes single comment. ### -> ## -> # and so on. Matches C# behavior. /// </summary> public static void UncommentBlock(IEditorView editorView, IEditorBuffer editorBuffer, ITextRange range, IEditorSupport es) { DoActionOnLines(editorView, editorBuffer, range, es, UncommentLine, Resources.UncommentSelection); }
public static Range ToLineRange(this IEditorBuffer editorBuffer, int start, int end) => new Range { Start = editorBuffer.CurrentSnapshot.ToLinePosition(start), End = editorBuffer.CurrentSnapshot.ToLinePosition(end) };
public IntellisenseContext(IEditorIntellisenseSession session, IEditorBuffer editorBuffer, int position) { Session = session; EditorBuffer = editorBuffer; Position = position; }
/// <summary> /// Retrieves service from the service container attached to the buffer /// </summary> public static T GetService <T>(this IEditorBuffer editorBuffer) where T : class => editorBuffer.Services.GetService <T>();
public static int ToStreamPosition(this IEditorBuffer editorBuffer, int lineNumber, int charNumber) => editorBuffer.CurrentSnapshot.ToStreamPosition(lineNumber, charNumber);
public EditorView(IEditorBuffer editorBuffer, int caretPosition) { EditorBuffer = editorBuffer; Caret = new ViewCaret(this, caretPosition); }