private bool IsEscapedPipeTyped(string currentText, SnapshotPoint positionBufferPosition) { positionBufferPosition = positionBufferPosition.Subtract(1); int backslashCount = 0; while (positionBufferPosition.Position > 0) { positionBufferPosition = positionBufferPosition.Subtract(1); if (positionBufferPosition.GetChar() != '\\') { break; } backslashCount++; } return(backslashCount % 2 == 1); }
/// <summary> /// Convert the <see cref="SnapshotPoint"/> to a (0-based) line and column number. /// </summary> /// <param name="snapshotPoint"> /// The <see cref="SnapshotPoint"/>. /// </param> /// <returns> /// The line and column number. /// </returns> public static (int line, int column) ToLineAndColumn(this SnapshotPoint snapshotPoint) { var line = snapshotPoint.GetContainingLine(); int lineNumber = line.LineNumber; int columnNumber = snapshotPoint.Subtract(line.Start).Position; return(lineNumber, columnNumber); }
public void PreOverType(out bool handledCommand) { handledCommand = false; if (ClosingPoint == null) { return; } // Brace completion is not cancellable. var cancellationToken = CancellationToken.None; var snapshot = this.SubjectBuffer.CurrentSnapshot; var document = snapshot.GetOpenDocumentInCurrentContextWithChanges(); SnapshotPoint closingSnapshotPoint = ClosingPoint.GetPoint(snapshot); if (!HasForwardTyping && _session.AllowOverType(this, cancellationToken)) { SnapshotPoint?caretPos = this.GetCaretPosition(); Debug.Assert(caretPos.HasValue && caretPos.Value.Position < closingSnapshotPoint.Position); // ensure that we are within the session before clearing if (caretPos.HasValue && caretPos.Value.Position < closingSnapshotPoint.Position && closingSnapshotPoint.Position > 0) { using (ITextUndoTransaction undo = CreateUndoTransaction()) { _editorOperations.AddBeforeTextBufferChangePrimitive(); SnapshotSpan span = new SnapshotSpan(caretPos.Value, closingSnapshotPoint.Subtract(1)); using (ITextEdit edit = SubjectBuffer.CreateEdit()) { edit.Delete(span); if (edit.HasFailedChanges) { Debug.Fail("Unable to clear closing brace"); edit.Cancel(); undo.Cancel(); } else { handledCommand = true; edit.Apply(); MoveCaretToClosingPoint(); _editorOperations.AddAfterTextBufferChangePrimitive(); undo.Complete(); } } } } } }
/// <summary> /// Provide CompletionContext items for the current auto-completion session. /// </summary> /// <param name="session">The current auto-completion session.</param> /// <param name="trigger">The action that triggered the auto-completion session.</param> /// <param name="triggerLocation">The location in the text where auto-completion was triggered.</param> /// <param name="applicableToSpan">The text area that may be affected by the auto-completion.</param> /// <param name="cancellationToken">A cancellation token.</param> /// <returns>A task returning a CompletionContext, filled with CompletionItems, if applicable.</returns> public Task <CompletionContext> GetCompletionContextAsync(InitialTrigger trigger, SnapshotPoint triggerLocation, SnapshotSpan applicableToSpan, CancellationToken token) => Task.Run(() => { IAsyncCompletionSession session = null; // TODO: where to get from? bool addPrefix = false; if (trigger.Character == '\0' && !_navigator.GetExtentOfWord(triggerLocation.Subtract(1)).Span.GetText().EndsWith("<", System.StringComparison.OrdinalIgnoreCase)) { // Meaning: autocomplete was not triggered by typing '<', but by using a shortcut, e.g. Ctrl+Space. // If the previous character is not '<', then we need to add one to the completion item text. addPrefix = true; } SnapshotSpan ats = applicableToSpan; SnapshotPoint t = triggerLocation; // TODO: Find a way to determine where in the comment section the completion was triggered, to filter out CompletionItems that are not applicable in the current context List <CompletionItem> completions = new List <CompletionItem>() { CreateCompletionItem(addPrefix, "conceptualLink", "conceptualLink target=\"\"/>", "This element is used to create a link to a MAML topic within the See Also section of a topic or an inline link to a MAML topic within one of the other XML comments elements.", false, 3), CreateCompletionItem(addPrefix, "inheritdoc", "inheritdoc/>", "This element can help minimize the effort required to document complex APIs by allowing common documentation to be inherited from base types/members."), CreateCompletionItem(addPrefix, "inheritdocCref", "inheritdoc cref=\"\"/>", "Inherit documentation from a specific member.", false, 3), CreateCompletionItem(addPrefix, "inheritdocCrefSelect", "inheritdoc cref=\"\" select=\"summary|remarks\"/>", "Inherit documentation from a specific member and comments.", false, 28), CreateCompletionItem(addPrefix, "token", "token", "This element represents a replaceable tag within a topic."), // exception CreateCompletionItem(addPrefix, "AttachedEventComments", "AttachedEventComments", "This element is used to define the content that should appear on the auto-generated attached event member topic for a given WPF routed event member."), CreateCompletionItem(addPrefix, "AttachedPropertyComments", "AttachedPropertyComments", "This element is used to define the content that should appear on the auto-generated attached property member topic for a given WPF dependency property member."), CreateCompletionItem(addPrefix, "event", "event cref=\"\"", "This element is used to list events that can be raised by a type's member.", false, 1), CreateCompletionItem(addPrefix, "overloads", "overloads", "This element is used to define the content that should appear on the auto-generated overloads topic for a given set of member overloads."), CreateCompletionItem(addPrefix, "preliminary", "preliminary/>", "This element is used to indicate that a particular type or member is preliminary and is subject to change."), CreateCompletionItem(addPrefix, "threadsafety", "threadsafety static=\"true\" instance=\"false\"/>", "This element is used to indicate whether or not a class or structure's static and instance members are safe for use in multi-threaded scenarios."), // list CreateCompletionItem(addPrefix, "note", "note type=\"note\"", "This element is used to create a note-like section within a topic to draw attention to some important information."), // language CreateCompletionItem(addPrefix, "null", "see langword=\"null\"/>", "Inserts the language-specific keyword 'null'.", true), CreateCompletionItem(addPrefix, "static", "see langword=\"static\"/>", "Inserts the language-specific keyword 'static'.", true), CreateCompletionItem(addPrefix, "virtual", "see langword=\"virtual\"/>", "Inserts the language-specific keyword 'virtual'.", true), CreateCompletionItem(addPrefix, "true", "see langword=\"true\"/>", "Inserts the language-specific keyword 'true'.", true), CreateCompletionItem(addPrefix, "false", "see langword=\"false\"/>", "Inserts the language-specific keyword 'false'.", true), CreateCompletionItem(addPrefix, "abstract", "see langword=\"abstract\"/>", "Inserts the language-specific keyword 'abstract'.", true), CreateCompletionItem(addPrefix, "sealed", "see langword=\"sealed\"/>", "Inserts the language-specific keyword 'sealed'.", true), CreateCompletionItem(addPrefix, "async", "see langword=\"async\"/>", "Inserts the language-specific keyword 'async'.", true), CreateCompletionItem(addPrefix, "await", "see langword=\"await\"/>", "Inserts the language-specific keyword 'await'.", true), CreateCompletionItem(addPrefix, "asyncAwait", "see langword=\"async/await\"/>", "Inserts the language-specific keyword 'async/await'.", true), // code CreateCompletionItem(addPrefix, "codeImport", "code language=\"\" title=\" \" source=\"..\\Path\\SourceFile.cs\" region=\"Region Name\"/>", "This element is used to indicate that a multi-line section of text should be imported from the named region of the named file and formatted as a code block.", false, 65), CreateCompletionItem(addPrefix, "codeLanguage", "code language=\"\" title=\" \"></code>", "This element is used to indicate that a multi-line section of text should be formatted as a code block.", false, 19), }; // Add handler for the completion of the session, so we can move the cursor to a position in the inserted text, if necessary. if (session != null) { session.ItemCommitted += Session_ItemCommitted; } return(new CompletionContext(completions.OrderBy(ci => ci.SortText).ToImmutableArray())); // Why doesn't VS sort? });
// basic checks to avoid incorrect behavior such as char c = '\''' private static bool AllowDefaultSession(SnapshotPoint openingPoint, char openingBrace, char closingBrace) { // avoid opening a new session next to the same char if (openingBrace == closingBrace && openingPoint.Position > 0) { char prevChar = openingPoint.Subtract(1).GetChar(); if (openingBrace.Equals(prevChar)) { return(false); } } return(true); }
public void PreOverType(out bool handledCommand) { handledCommand = false; // AllowOverType may make changes to the buffer such as for completing intellisense if (!HasForwardTyping && (_context == null || _context.AllowOverType(this))) { SnapshotPoint?caretPos = CaretPosition; SnapshotPoint closingSnapshotPoint = _closingPoint.GetPoint(SubjectBuffer.CurrentSnapshot); Debug.Assert(caretPos.HasValue && caretPos.Value.Position < closingSnapshotPoint.Position); // ensure that we are within the session before clearing if (caretPos.HasValue && caretPos.Value.Position < closingSnapshotPoint.Position && closingSnapshotPoint.Position > 0) { using (ITextUndoTransaction undo = CreateUndoTransaction()) { _editorOperations.AddBeforeTextBufferChangePrimitive(); SnapshotSpan span = new SnapshotSpan(caretPos.Value, closingSnapshotPoint.Subtract(1)); using (ITextEdit edit = _subjectBuffer.CreateEdit()) { edit.Delete(span); if (edit.HasFailedChanges) { Debug.Fail("Unable to clear closing brace"); edit.Cancel(); undo.Cancel(); } else { handledCommand = true; edit.Apply(); MoveCaretToClosingPoint(); _editorOperations.AddAfterTextBufferChangePrimitive(); undo.Complete(); } } } } } }
private async Task <RazorLanguageKind?> GetTriggerCharacterLanguageKindAsync(LSPDocumentSnapshot documentSnapshot, Position positionAfterTriggerChar, string triggerCharacter, CancellationToken cancellationToken) { // request.Character will point to the position after the character that was inserted. // For onTypeFormatting, it makes more sense to look up the projection of the character that was inserted. var line = documentSnapshot.Snapshot.GetLineFromLineNumber(positionAfterTriggerChar.Line); var position = line.Start.Position + positionAfterTriggerChar.Character; var point = new SnapshotPoint(documentSnapshot.Snapshot, position); // Subtract the trigger character length to go back to the position of the trigger character var triggerCharacterPoint = point.Subtract(triggerCharacter.Length); var triggerCharacterLine = documentSnapshot.Snapshot.GetLineFromPosition(triggerCharacterPoint.Position); var triggerCharacterPosition = new Position(triggerCharacterLine.LineNumber, triggerCharacterPoint.Position - triggerCharacterLine.Start.Position); var triggerCharacterProjectionResult = await _projectionProvider.GetProjectionAsync(documentSnapshot, triggerCharacterPosition, cancellationToken).ConfigureAwait(false); return(triggerCharacterProjectionResult?.LanguageKind); }
private bool IsSessionValid(IBraceCompletionSession session) { bool isValid = false; _guardedOperations.CallExtensionPoint(() => { if (session.ClosingPoint != null && session.OpeningPoint != null && session.SubjectBuffer != null) { ITextSnapshot snapshot = session.SubjectBuffer.CurrentSnapshot; SnapshotPoint closingSnapshotPoint = session.ClosingPoint.GetPoint(snapshot); SnapshotPoint openingSnapshotPoint = session.OpeningPoint.GetPoint(snapshot); // Verify that the closing and opening points still match the expected braces isValid = closingSnapshotPoint.Position > 1 && openingSnapshotPoint.Position <= (closingSnapshotPoint.Position - 2) && openingSnapshotPoint.GetChar() == session.OpeningBrace && closingSnapshotPoint.Subtract(1).GetChar() == session.ClosingBrace; } }); return(isValid); }
private void Start(CancellationToken cancellationToken) { // this is where the caret should go after the change SnapshotPoint pos = TextView.Caret.Position.BufferPosition; ITrackingPoint beforeTrackingPoint = pos.Snapshot.CreateTrackingPoint(pos.Position, PointTrackingMode.Negative); ITextSnapshot snapshot = SubjectBuffer.CurrentSnapshot; SnapshotPoint closingSnapshotPoint = ClosingPoint.GetPoint(snapshot); if (closingSnapshotPoint.Position < 1) { Debug.Fail("The closing point was not found at the expected position."); EndSession(); return; } SnapshotPoint openingSnapshotPoint = closingSnapshotPoint.Subtract(1); if (openingSnapshotPoint.GetChar() != OpeningBrace) { // there is a bug in editor brace completion engine on projection buffer that already fixed in vs_pro. until that is FIed to use // I will make this not to assert // Debug.Fail("The opening brace was not found at the expected position."); EndSession(); return; } OpeningPoint = snapshot.CreateTrackingPoint(openingSnapshotPoint, PointTrackingMode.Positive); var document = snapshot.GetOpenDocumentInCurrentContextWithChanges(); if (!_session.CheckOpeningPoint(this, cancellationToken)) { EndSession(); return; } using (ITextUndoTransaction undo = CreateUndoTransaction()) { // insert the closing brace using (ITextEdit edit = SubjectBuffer.CreateEdit()) { edit.Insert(closingSnapshotPoint, ClosingBrace.ToString()); if (edit.HasFailedChanges) { Debug.Fail("Unable to insert closing brace"); // exit without setting the closing point which will take us off the stack edit.Cancel(); undo.Cancel(); return; } else { snapshot = edit.Apply(); } } SnapshotPoint beforePoint = beforeTrackingPoint.GetPoint(TextView.TextSnapshot); // switch from positive to negative tracking so it stays against the closing brace ClosingPoint = SubjectBuffer.CurrentSnapshot.CreateTrackingPoint(ClosingPoint.GetPoint(snapshot), PointTrackingMode.Negative); Debug.Assert(ClosingPoint.GetPoint(snapshot).Position > 0 && (new SnapshotSpan(ClosingPoint.GetPoint(snapshot).Subtract(1), 1)) .GetText().Equals(ClosingBrace.ToString()), "The closing point does not match the closing brace character"); // move the caret back between the braces TextView.Caret.MoveTo(beforePoint); _session.AfterStart(this, cancellationToken); undo.Complete(); } }
public void PostReturn() { if (this.GetCaretPosition().HasValue) { SnapshotPoint closingSnapshotPoint = ClosingPoint.GetPoint(SubjectBuffer.CurrentSnapshot); if (closingSnapshotPoint.Position > 0 && HasNoForwardTyping(this.GetCaretPosition().Value, closingSnapshotPoint.Subtract(1))) { _session.AfterReturn(this, CancellationToken.None); } } }
public void Start() { // this is where the caret should go after the change SnapshotPoint pos = _textView.Caret.Position.BufferPosition; ITrackingPoint beforeTrackingPoint = pos.Snapshot.CreateTrackingPoint(pos.Position, PointTrackingMode.Negative); ITextSnapshot snapshot = _subjectBuffer.CurrentSnapshot; SnapshotPoint closingSnapshotPoint = _closingPoint.GetPoint(snapshot); if (closingSnapshotPoint.Position < 1) { Debug.Fail("The closing point was not found at the expected position."); EndSession(); return; } SnapshotPoint openingSnapshotPoint = closingSnapshotPoint.Subtract(1); if (openingSnapshotPoint.GetChar() != OpeningBrace) { Debug.Fail("The opening brace was not found at the expected position."); EndSession(); return; } _openingPoint = SubjectBuffer.CurrentSnapshot.CreateTrackingPoint(openingSnapshotPoint, PointTrackingMode.Positive); using (ITextUndoTransaction undo = CreateUndoTransaction()) { // insert the closing brace using (ITextEdit edit = _subjectBuffer.CreateEdit()) { edit.Insert(closingSnapshotPoint, _closingBrace.ToString(CultureInfo.CurrentCulture)); if (edit.HasFailedChanges) { Debug.Fail("Unable to insert closing brace"); // exit without setting the closing point which will take us off the stack edit.Cancel(); undo.Cancel(); return; } else { snapshot = edit.Apply(); } } SnapshotPoint beforePoint = beforeTrackingPoint.GetPoint(_textView.TextSnapshot); // switch from positive to negative tracking so it stays against the closing brace _closingPoint = SubjectBuffer.CurrentSnapshot.CreateTrackingPoint(_closingPoint.GetPoint(snapshot), PointTrackingMode.Negative); Debug.Assert(_closingPoint.GetPoint(snapshot).Position > 0 && (new SnapshotSpan(_closingPoint.GetPoint(snapshot).Subtract(1), 1)) .GetText().Equals(_closingBrace.ToString(CultureInfo.CurrentCulture), System.StringComparison.Ordinal), "The closing point does not match the closing brace character"); // move the caret back between the braces _textView.Caret.MoveTo(beforePoint); if (_context != null) { // allow the context to do extra formatting _context.Start(this); } undo.Complete(); } }
public void PostReturn() { if (_context != null && CaretPosition.HasValue) { SnapshotPoint closingSnapshotPoint = _closingPoint.GetPoint(SubjectBuffer.CurrentSnapshot); if (closingSnapshotPoint.Position > 0 && HasNoForwardTyping(CaretPosition.Value, closingSnapshotPoint.Subtract(1))) { _context.OnReturn(this); } } }