public void CheckForStructureChangesStartsFullReparseIfChangeOverlapsMultipleSpans() { // Arrange using (var parser = new RazorEditorParser(CreateHost(), TestLinePragmaFileName)) { var original = new StringTextBuffer("Foo @bar Baz"); var changed = new StringTextBuffer("Foo @bap Daz"); var change = new TextChange(7, 3, original, 3, changed); var parseComplete = new ManualResetEventSlim(); var parseCount = 0; parser.DocumentParseComplete += (sender, args) => { Interlocked.Increment(ref parseCount); parseComplete.Set(); }; Assert.Equal(PartialParseResult.Rejected, parser.CheckForStructureChanges(new TextChange(0, 0, new StringTextBuffer(string.Empty), 12, original))); MiscUtils.DoWithTimeoutIfNotDebugging(parseComplete.Wait); // Wait for the parse to finish parseComplete.Reset(); // Act var result = parser.CheckForStructureChanges(change); // Assert Assert.Equal(PartialParseResult.Rejected, result); MiscUtils.DoWithTimeoutIfNotDebugging(parseComplete.Wait); Assert.Equal(2, parseCount); } }
private bool TryGetDocumentWithFullyQualifiedTypeName(Document document, out TextSpan updatedTextSpan, out Document documentWithFullyQualifiedTypeName) { documentWithFullyQualifiedTypeName = null; updatedTextSpan = default(TextSpan); var surfaceBufferFieldSpan = new VsTextSpan[1]; if (snippetExpansionClient.ExpansionSession.GetFieldSpan(_fieldName, surfaceBufferFieldSpan) != VSConstants.S_OK) { return false; } SnapshotSpan subjectBufferFieldSpan; if (!snippetExpansionClient.TryGetSubjectBufferSpan(surfaceBufferFieldSpan[0], out subjectBufferFieldSpan)) { return false; } var originalTextSpan = new TextSpan(subjectBufferFieldSpan.Start, subjectBufferFieldSpan.Length); updatedTextSpan = new TextSpan(subjectBufferFieldSpan.Start, _fullyQualifiedName.Length); var textChange = new TextChange(originalTextSpan, _fullyQualifiedName); var newText = document.GetTextAsync(CancellationToken.None).WaitAndGetResult(CancellationToken.None).WithChanges(textChange); documentWithFullyQualifiedTypeName = document.WithText(newText); return true; }
public virtual bool OwnsChange(Span target, TextChange change) { var end = target.Start.AbsoluteIndex + target.Length; var changeOldEnd = change.OldPosition + change.OldLength; return change.OldPosition >= target.Start.AbsoluteIndex && (changeOldEnd < end || (changeOldEnd == end && AcceptedCharacters != AcceptedCharacters.None)); }
private static async Task<Document> GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken) { var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); TextChange textChange = new TextChange(new TextSpan(diagnostic.Location.SourceSpan.Start, 1), string.Empty); return document.WithText(text.WithChanges(textChange)); }
public void RaiseChangeCompleted(TextChange change) { var e = ChangeCompleted; if (e != null) { e(this, change); } }
private static async Task<Document> GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken token) { var newLine = document.Project.Solution.Workspace.Options.GetOption(FormattingOptions.NewLine, LanguageNames.CSharp); var sourceText = await document.GetTextAsync(token).ConfigureAwait(false); var textChange = new TextChange(diagnostic.Location.SourceSpan, newLine); return document.WithText(sourceText.WithChanges(textChange)); }
private CompletionChange(TextChange textChange, int? newPosition, bool includesCommitCharacter) { TextChange = textChange; #pragma warning disable CS0618 // Type or member is obsolete TextChanges = ImmutableArray.Create(textChange); #pragma warning restore CS0618 // Type or member is obsolete NewPosition = newPosition; IncludesCommitCharacter = includesCommitCharacter; }
#pragma warning disable RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads. #pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters public static CompletionChange Create( #pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters #pragma warning restore RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads. TextChange textChange, int? newPosition = null, bool includesCommitCharacter = false) { return new CompletionChange(textChange, newPosition, includesCommitCharacter); }
public void Bug18241() { var tree = SyntaxFactory.ParseSyntaxTree(" class C { void M() { await X() on "); SourceText text = tree.GetText(); TextSpan span = new TextSpan(text.Length, 0); TextChange change = new TextChange(span, "/*comment*/"); SourceText newText = text.WithChanges(change); // This line caused an assertion and then crashed in the parser. var newTree = tree.WithChangedText(newText); }
public void TestIsDelete() { // Arrange var oldBuffer = new Mock<ITextBuffer>().Object; var newBuffer = new Mock<ITextBuffer>().Object; var change = new TextChange(0, 1, oldBuffer, 0, newBuffer); // Assert Assert.True(change.IsDelete); }
public void TestDeleteCreatesTheRightSizeChange() { // Arrange var oldBuffer = new Mock<ITextBuffer>().Object; var newBuffer = new Mock<ITextBuffer>().Object; var change = new TextChange(0, 1, oldBuffer, 0, newBuffer); // Assert Assert.Equal(0, change.NewText.Length); Assert.Equal(1, change.OldText.Length); }
protected override PartialParseResult CanAcceptChange(Span target, TextChange normalizedChange) { if (((AutoCompleteAtEndOfSpan && IsAtEndOfSpan(target, normalizedChange)) || IsAtEndOfFirstLine(target, normalizedChange)) && normalizedChange.IsInsert && ParserHelpers.IsNewLine(normalizedChange.NewText) && AutoCompleteString != null) { return PartialParseResult.Rejected | PartialParseResult.AutoCompleteBlock; } return PartialParseResult.Rejected; }
protected override PartialParseResult CanAcceptChange(Span target, TextChange normalizedChange) { if (AcceptedCharacters == AcceptedCharacters.Any) { return PartialParseResult.Rejected; } // In some editors intellisense insertions are handled as "dotless commits". If an intellisense selection is confirmed // via something like '.' a dotless commit will append a '.' and then insert the remaining intellisense selection prior // to the appended '.'. This 'if' statement attempts to accept the intermediate steps of a dotless commit via // intellisense. It will accept two cases: // 1. '@foo.' -> '@foobaz.'. // 2. '@foobaz..' -> '@foobaz.bar.'. Includes Sub-cases '@foobaz()..' -> '@foobaz().bar.' etc. // The key distinction being the double '.' in the second case. if (IsDotlessCommitInsertion(target, normalizedChange)) { return HandleDotlessCommitInsertion(target); } if (IsAcceptableReplace(target, normalizedChange)) { return HandleReplacement(target, normalizedChange); } var changeRelativePosition = normalizedChange.OldPosition - target.Start.AbsoluteIndex; // Get the edit context char? lastChar = null; if (changeRelativePosition > 0 && target.Content.Length > 0) { lastChar = target.Content[changeRelativePosition - 1]; } // Don't support 0->1 length edits if (lastChar == null) { return PartialParseResult.Rejected; } // Accepts cases when insertions are made at the end of a span or '.' is inserted within a span. if (IsAcceptableInsertion(target, normalizedChange)) { // Handle the insertion return HandleInsertion(target, lastChar.Value, normalizedChange); } if (IsAcceptableDeletion(target, normalizedChange)) { return HandleDeletion(target, lastChar.Value, normalizedChange); } return PartialParseResult.Rejected; }
private void Create(params string[] lines) { _textView = CreateTextView(lines); _textBuffer = _textView.TextBuffer; _factory = new MockRepository(MockBehavior.Loose); _operations = _factory.Create<ICommonOperations>(MockBehavior.Strict); _vimTextBuffer = _factory.Create<IVimTextBuffer>(MockBehavior.Strict); _vimTextBuffer.SetupProperty(x => x.LastEditPoint); _trackerRaw = new TextChangeTracker(_vimTextBuffer.Object, _textView, _operations.Object); _trackerRaw.TrackCurrentChange = true; _tracker = _trackerRaw; _tracker.ChangeCompleted += (sender, args) => { _lastChange = args.TextChange; }; }
public void ConstructorInitializesProperties() { // Act var oldBuffer = new Mock<ITextBuffer>().Object; var newBuffer = new Mock<ITextBuffer>().Object; var change = new TextChange(42, 24, oldBuffer, 1337, newBuffer); // Assert Assert.Equal(42, change.OldPosition); Assert.Equal(24, change.OldLength); Assert.Equal(1337, change.NewLength); Assert.Same(newBuffer, change.NewBuffer); Assert.Same(oldBuffer, change.OldBuffer); }
public void CheckForStructureChangesRequiresNonNullBufferInChange() { var change = new TextChange(); var parameterName = "change"; var exception = Assert.Throws<ArgumentException>( parameterName, () => { using (var parser = new RazorEditorParser(CreateHost(), "C:\\Foo.cshtml")) { parser.CheckForStructureChanges(change); } }); ExceptionHelpers.ValidateArgumentException(parameterName, RazorResources.FormatStructure_Member_CannotBeNull(nameof(change.NewBuffer), nameof(TextChange)), exception); }
public virtual EditResult ApplyChange(Span target, TextChange change, bool force) { var result = PartialParseResult.Accepted; var normalized = change.Normalize(); if (!force) { result = CanAcceptChange(target, normalized); } // If the change is accepted then apply the change if ((result & PartialParseResult.Accepted) == PartialParseResult.Accepted) { return new EditResult(result, UpdateSpan(target, normalized)); } return new EditResult(result, new SpanBuilder(target)); }
protected virtual SpanBuilder UpdateSpan(Span target, TextChange normalizedChange) { var newContent = normalizedChange.ApplyChange(target); var newSpan = new SpanBuilder(target); newSpan.ClearSymbols(); foreach (ISymbol sym in Tokenizer(newContent)) { sym.OffsetStart(target.Start); newSpan.Accept(sym); } if (target.Next != null) { var newEnd = SourceLocationTracker.CalculateNewLocation(target.Start, newContent); target.Next.ChangeStart(newEnd); } return newSpan; }
private string AdjustForVirtualSpace(TextChange textChange) { var newText = textChange.NewText; var caretPoint = this.TextView.Caret.Position.BufferPosition; var virtualCaretPoint = this.TextView.Caret.Position.VirtualBufferPosition; if (textChange.Span.IsEmpty && textChange.Span.Start == caretPoint && virtualCaretPoint.IsInVirtualSpace) { // They're in virtual space and the text change is specified against the cursor // position that isn't in virtual space. In this case, add the virtual spaces to the // thing we're adding. var editorOperations = _editorOperationsFactoryService.GetEditorOperations(this.TextView); var whitespace = editorOperations.GetWhitespaceForVirtualSpace(virtualCaretPoint); return whitespace + newText; } return newText; }
public TextChange(TextChange change, ITextProvider newTextProvider) : this() { this.Combine(change); ITextSnapshotProvider newSnapshotProvider = newTextProvider as ITextSnapshotProvider; ITextSnapshotProvider changeNewSnapshotProvider = change.NewTextProvider as ITextSnapshotProvider; if ((newSnapshotProvider != null) && (changeNewSnapshotProvider != null)) { ITextSnapshot changeNewSnapshot = changeNewSnapshotProvider.Snapshot; ITextSnapshot newSnapshot = newSnapshotProvider.Snapshot; if (changeNewSnapshot.Version.ReiteratedVersionNumber != newSnapshot.Version.ReiteratedVersionNumber) { SnapshotSpan changeNewSpan = change.NewRange.ToSnapshotSpan(changeNewSnapshot); Span? oldChangedSpan; Span? newChangedSpan; if (changeNewSnapshot.Version.GetChangedExtent(newSnapshot.Version, out oldChangedSpan, out newChangedSpan)) { int start = Math.Min(oldChangedSpan.Value.Start, change.NewRange.Start); int end = Math.Max(oldChangedSpan.Value.End, change.NewRange.End); changeNewSpan = new SnapshotSpan(changeNewSnapshot, Span.FromBounds(start, end)); } SnapshotSpan newSpan = changeNewSpan.TranslateTo(newSnapshot, SpanTrackingMode.EdgeInclusive); NewRange = new TextRange(newSpan.Start.Position, newSpan.Length); } } NewTextProvider = newTextProvider; Version = NewTextProvider.Version; }
internal async Task <(bool, SumType <CompletionItem[], CompletionList>?)> TryGetProvisionalCompletionsAsync(CompletionParams request, LSPDocumentSnapshot documentSnapshot, ProjectionResult projection, CancellationToken cancellationToken) { SumType <CompletionItem[], CompletionList>?result = null; if (projection.LanguageKind != RazorLanguageKind.Html || request.Context.TriggerKind != CompletionTriggerKind.TriggerCharacter || request.Context.TriggerCharacter != ".") { return(false, result); } if (projection.Position.Character == 0) { // We're at the start of line. Can't have provisional completions here. return(false, result); } var previousCharacterPosition = new Position(projection.Position.Line, projection.Position.Character - 1); var previousCharacterProjection = await _projectionProvider.GetProjectionAsync(documentSnapshot, previousCharacterPosition, cancellationToken).ConfigureAwait(false); if (previousCharacterProjection == null || previousCharacterProjection.LanguageKind != RazorLanguageKind.CSharp) { return(false, result); } if (!(_documentManager is TrackingLSPDocumentManager trackingDocumentManager)) { return(false, result); } // Edit the CSharp projected document to contain a '.'. This allows C# completion to provide valid // completion items for moments when a user has typed a '.' that's typically interpreted as Html. var addProvisionalDot = new TextChange( TextSpan.FromBounds(previousCharacterProjection.PositionIndex, previousCharacterProjection.PositionIndex), "."); await _joinableTaskFactory.SwitchToMainThreadAsync(); trackingDocumentManager.UpdateVirtualDocument <CSharpVirtualDocument>(documentSnapshot.Uri, new[] { addProvisionalDot }, previousCharacterProjection.HostDocumentVersion); var provisionalCompletionParams = new CompletionParams() { Context = request.Context, Position = new Position(previousCharacterProjection.Position.Line, previousCharacterProjection.Position.Character + 1), TextDocument = new TextDocumentIdentifier() { Uri = previousCharacterProjection.Uri } }; result = await _requestInvoker.ReinvokeRequestOnServerAsync <CompletionParams, SumType <CompletionItem[], CompletionList>?>( Methods.TextDocumentCompletionName, LanguageServerKind.CSharp, provisionalCompletionParams, cancellationToken).ConfigureAwait(true); // We have now obtained the necessary completion items. We no longer need the provisional change. Revert. var removeProvisionalDot = new TextChange( TextSpan.FromBounds(previousCharacterProjection.PositionIndex, previousCharacterProjection.PositionIndex + 1), string.Empty); trackingDocumentManager.UpdateVirtualDocument <CSharpVirtualDocument>(documentSnapshot.Uri, new[] { removeProvisionalDot }, previousCharacterProjection.HostDocumentVersion); return(true, result); }
internal static void WriteDebugTree(string sourceFile, Block document, PartialParseResult result, TextChange change, RazorEditorParser parser, bool treeStructureChanged) { if (!OutputDebuggingEnabled) { return; } RunTask(() => { string outputFileName = Normalize(sourceFile) + "_tree"; string outputPath = Path.Combine(Path.GetDirectoryName(sourceFile), outputFileName); var treeBuilder = new StringBuilder(); WriteTree(document, treeBuilder); treeBuilder.AppendLine(); treeBuilder.AppendFormat(CultureInfo.CurrentCulture, "Last Change: {0}", change); treeBuilder.AppendLine(); treeBuilder.AppendFormat(CultureInfo.CurrentCulture, "Normalized To: {0}", change.Normalize()); treeBuilder.AppendLine(); treeBuilder.AppendFormat(CultureInfo.CurrentCulture, "Partial Parse Result: {0}", result); treeBuilder.AppendLine(); if (result.HasFlag(PartialParseResult.Rejected)) { treeBuilder.AppendFormat(CultureInfo.CurrentCulture, "Tree Structure Changed: {0}", treeStructureChanged); treeBuilder.AppendLine(); } if (result.HasFlag(PartialParseResult.AutoCompleteBlock)) { treeBuilder.AppendFormat(CultureInfo.CurrentCulture, "Auto Complete Insert String: \"{0}\"", parser.GetAutoCompleteString()); treeBuilder.AppendLine(); } File.WriteAllText(outputPath, treeBuilder.ToString()); }); }
private static void CleanupSourceMappingStart(FormattingContext context, Range sourceMappingRange, List <TextChange> changes, bool isOnType, out bool newLineAdded) { newLineAdded = false; // // We look through every source mapping that intersects with the affected range and // bring the first line to its own line and adjust its indentation, // // E.g, // // @{ public int x = 0; // } // // becomes, // // @{ // public int x = 0; // } // var text = context.SourceText; var sourceMappingSpan = sourceMappingRange.AsTextSpan(text); if (!ShouldFormat(context, sourceMappingSpan, allowImplicitStatements: false)) { // We don't want to run cleanup on this range. return; } if (sourceMappingRange.Start.Character == 0) { // It already starts on a fresh new line which doesn't need cleanup. // E.g, (The mapping starts at | in the below case) // @{ // @: Some html // | var x = 123; // } // return; } // @{ // if (true) // { // <div></div>| // // |} // } // We want to return the length of the range marked by |...| // var whitespaceLength = text.GetFirstNonWhitespaceOffset(sourceMappingSpan, out var newLineCount); if (whitespaceLength == null) { // There was no content after the start of this mapping. Meaning it already is clean. // E.g, // @{| // ... // } return; } var spanToReplace = new TextSpan(sourceMappingSpan.Start, whitespaceLength.Value); if (!context.TryGetIndentationLevel(spanToReplace.End, out var contentIndentLevel)) { // Can't find the correct indentation for this content. Leave it alone. return; } if (newLineCount == 0) { newLineAdded = true; newLineCount = 1; } // At this point, `contentIndentLevel` should contain the correct indentation level for `}` in the above example. // Make sure to preserve the same number of blank lines as the original string had var replacement = PrependLines(context.GetIndentationLevelString(contentIndentLevel), context.NewLineString, newLineCount); // After the below change the above example should look like, // @{ // if (true) // { // <div></div> // } // } var change = new TextChange(spanToReplace, replacement); changes.Add(change); }
public void QueueChange(TextChange change) { _main.QueueChange(change); }
public static TextChange.Insert AsInsert(this TextChange change) { return((TextChange.Insert)change); }
public static TextChange.DeleteRight AsDeleteRight(this TextChange change) { return((TextChange.DeleteRight)change); }
public static bool IsInsert(this TextChange change, string text) { return(change.IsInsert && change.AsInsert().Item == text); }
public void InheritFromNetModuleMetadata01() { var modRef = TestReferences.MetadataTests.NetModule01.ModuleCS00; var text1 = @" class Test : StaticModClass {"; var text2 = @" public static int Main() { r"; var tree = SyntaxFactory.ParseSyntaxTree(String.Empty); var comp = CreateCompilationWithMscorlib(syntaxTree: tree, references: new[] { modRef }); var currComp = comp; var oldTree = comp.SyntaxTrees.First(); var oldIText = oldTree.GetText(); var span = new TextSpan(oldIText.Length, 0); var change = new TextChange(span, text1); var newIText = oldIText.WithChanges(change); var newTree = oldTree.WithChangedText(newIText); currComp = currComp.ReplaceSyntaxTree(oldTree, newTree); var model = currComp.GetSemanticModel(newTree); var id = newTree.GetRoot().DescendantNodes().OfType<IdentifierNameSyntax>().Where(s => s.ToString() == "StaticModClass").First(); // NRE is thrown later but this one has to be called first var symInfo = model.GetSymbolInfo(id); Assert.NotNull(symInfo.Symbol); oldTree = newTree; oldIText = oldTree.GetText(); span = new TextSpan(oldIText.Length, 0); change = new TextChange(span, text2); newIText = oldIText.WithChanges(change); newTree = oldTree.WithChangedText(newIText); currComp = currComp.ReplaceSyntaxTree(oldTree, newTree); model = currComp.GetSemanticModel(newTree); id = newTree.GetRoot().DescendantNodes().OfType<IdentifierNameSyntax>().Where(s => s.ToString() == "StaticModClass").First(); symInfo = model.GetSymbolInfo(id); Assert.NotNull(symInfo.Symbol); }
private static int CompareTextChanges(TextChange x, TextChange y) { return x.Span.CompareTo(y.Span); }
protected virtual PartialParseResult CanAcceptChange(Span target, TextChange normalizedChange) { return(PartialParseResult.Rejected); }
/// <summary> /// Returns the old text referenced by the change. /// </summary> /// <remarks> /// If the content has already been updated by applying the change, this data will be _invalid_ /// </remarks> protected internal static string GetOldText(Span target, TextChange change) { return(target.Content.Substring(change.OldPosition - target.Start.AbsoluteIndex, change.OldLength)); }
internal virtual BackgroundParseTask CreateBackgroundTask(RazorEngineHost host, string fileName, TextChange change) { return(BackgroundParseTask.StartNew(new RazorTemplateEngine(Host), FileName, change)); }
private bool TryGetSubTextChange( SourceText originalText, TextSpan visibleSpanInOriginalText, string rightText, TextSpan spanInOriginalText, TextSpan spanInRightText, out TextChange textChange) { textChange = default; var visibleFirstLineInOriginalText = originalText.Lines.GetLineFromPosition(visibleSpanInOriginalText.Start); var visibleLastLineInOriginalText = originalText.Lines.GetLineFromPosition(visibleSpanInOriginalText.End); // skip easy case // 1. things are out of visible span if (!visibleSpanInOriginalText.IntersectsWith(spanInOriginalText)) { return(false); } // 2. there are no intersects var snippetInRightText = rightText.Substring(spanInRightText.Start, spanInRightText.Length); if (visibleSpanInOriginalText.Contains(spanInOriginalText) && visibleSpanInOriginalText.End != spanInOriginalText.End) { textChange = new TextChange(spanInOriginalText, snippetInRightText); return(true); } // okay, more complex case. things are intersecting boundaries. var firstLineOfRightTextSnippet = snippetInRightText.GetFirstLineText(); var lastLineOfRightTextSnippet = snippetInRightText.GetLastLineText(); // there are 4 complex cases - these are all heuristic. not sure what better way I have. and the heuristic is heavily based on // text differ's behavior. // 1. it is a single line if (visibleFirstLineInOriginalText.LineNumber == visibleLastLineInOriginalText.LineNumber) { // don't do anything return(false); } // 2. replacement contains visible spans if (spanInOriginalText.Contains(visibleSpanInOriginalText)) { // header // don't do anything // body textChange = new TextChange( TextSpan.FromBounds(visibleFirstLineInOriginalText.EndIncludingLineBreak, visibleLastLineInOriginalText.Start), snippetInRightText.Substring(firstLineOfRightTextSnippet.Length, snippetInRightText.Length - firstLineOfRightTextSnippet.Length - lastLineOfRightTextSnippet.Length)); // footer // don't do anything return(true); } // 3. replacement intersects with start if (spanInOriginalText.Start < visibleSpanInOriginalText.Start && visibleSpanInOriginalText.Start <= spanInOriginalText.End && spanInOriginalText.End < visibleSpanInOriginalText.End) { // header // don't do anything // body if (visibleFirstLineInOriginalText.EndIncludingLineBreak <= spanInOriginalText.End) { textChange = new TextChange( TextSpan.FromBounds(visibleFirstLineInOriginalText.EndIncludingLineBreak, spanInOriginalText.End), snippetInRightText.Substring(firstLineOfRightTextSnippet.Length)); return(true); } return(false); } // 4. replacement intersects with end if (visibleSpanInOriginalText.Start < spanInOriginalText.Start && spanInOriginalText.Start <= visibleSpanInOriginalText.End && visibleSpanInOriginalText.End <= spanInOriginalText.End) { // body if (spanInOriginalText.Start <= visibleLastLineInOriginalText.Start) { textChange = new TextChange( TextSpan.FromBounds(spanInOriginalText.Start, visibleLastLineInOriginalText.Start), snippetInRightText.Substring(0, snippetInRightText.Length - lastLineOfRightTextSnippet.Length)); return(true); } // footer // don't do anything return(false); } // if it got hit, then it means there is a missing case throw ExceptionUtilities.Unreachable; }
public override void OnDidChangeTextDocument(DidChangeTextDocumentParams parameters) { var fileCompiler = GetFileCompilerFromStringUri(parameters.uri, false); //Text Change do not have to trigger node phase, it's only a another event that will do it if (fileCompiler == null) { return; } Uri objUri = new Uri(parameters.uri); #region Convert text changes format from multiline range replacement to single line updates TextChangedEvent textChangedEvent = new TextChangedEvent(); foreach (var contentChange in parameters.contentChanges) { // Split the text updated into distinct lines List <string> lineUpdates = null; bool replacementTextStartsWithNewLine = false; if (!string.IsNullOrEmpty(contentChange.text)) { replacementTextStartsWithNewLine = contentChange.text[0] == '\r' || contentChange.text[0] == '\n'; //Allow to know if a new line was added //Split on \r \n to know the number of lines added lineUpdates = contentChange.text.Replace("\r", "").Split('\n').ToList(); if (string.IsNullOrEmpty(lineUpdates.FirstOrDefault()) && replacementTextStartsWithNewLine) { lineUpdates.RemoveAt(0); } } // Document cleared if (contentChange.range == null) { //JCM: I have noticed that if the entire text has changed, is better to reload the entire file //To avoid crashes. try { typeCobolWorkspace.OpenSourceFile(objUri, contentChange.text, this.Workspace.LsrTestOptions); return; } catch (Exception e) { //Don't rethow an exception on save. RemoteConsole.Error(string.Format("Error while handling notification {0} : {1}", "textDocument/didChange", e.Message)); return; } } // Document updated else { // Get original lines text before change string originalFirstLineText = fileCompiler.CompilationResultsForProgram.CobolTextLines[contentChange.range.start.line] .Text; string originalLastLineText = originalFirstLineText; // Check if the first line was inserted int firstLineIndex = contentChange.range.start.line; int firstLineChar = contentChange.range.start.character; if (replacementTextStartsWithNewLine && !(contentChange.range.start.character < originalLastLineText.Length)) { firstLineIndex++; firstLineChar = 0; } else if (replacementTextStartsWithNewLine) //Detected that the add line appeared inside an existing line { lineUpdates.Add(lineUpdates.First()); //Add the default 7 spaces + add lineUpdates in order to update the current line and add the new one. } // Check if the last line was deleted int lastLineIndex = contentChange.range.end.line; if (contentChange.range.end.line > contentChange.range.start.line && contentChange.range.end.character == 0) { //Allows to detect if the next line was suppressed } if (contentChange.text?.Length == 0) { lineUpdates = new List <string>(); } if (lastLineIndex > firstLineIndex) { originalLastLineText = fileCompiler.CompilationResultsForProgram.CobolTextLines[ Math.Min(lastLineIndex, fileCompiler.CompilationResultsForProgram.CobolTextLines.Count - 1)].Text; } // Text not modified at the beginning of the first replaced line string startOfFirstLine = null; if (firstLineChar > 0) { if (originalFirstLineText.Length >= contentChange.range.start.character) { startOfFirstLine = originalFirstLineText.Substring(0, contentChange.range.start.character); } else { startOfFirstLine = originalFirstLineText.Substring(0, originalFirstLineText.Length) + new string(' ', contentChange.range.start.character - originalFirstLineText.Length); } } // Text not modified at the end of the last replaced line string endOfLastLine = null; if (contentChange.range.end.character < originalLastLineText.Length) { endOfLastLine = originalLastLineText.Substring(contentChange.range.end.character); } // Remove all the old lines for (int i = firstLineIndex; i <= lastLineIndex; i++) { var textChange = new TextChange(TextChangeType.LineRemoved, firstLineIndex, null); textChangedEvent.TextChanges.Add(textChange); //Mark the index line to be removed. The index will remains the same for each line delete, because text change are apply one after another } // Insert the updated lines if (!(startOfFirstLine == null && lineUpdates == null && endOfLastLine == null)) { int lineUpdatesCount = (lineUpdates != null && lineUpdates.Count > 0) ? lineUpdates.Count : 1; for (int i = 0; i < lineUpdatesCount; i++) { string newLine = (lineUpdates != null && lineUpdates.Count > 0) ? lineUpdates[i] : string.Empty; if (i == 0) { newLine = startOfFirstLine + newLine; } if (i == lineUpdatesCount - 1) { newLine = newLine + endOfLastLine; } var textChange = new TextChange(TextChangeType.LineInserted, firstLineIndex + i, new TextLineSnapshot(firstLineIndex + i, newLine, null)); textChangedEvent.TextChanges.Add(textChange); } } } } #endregion // Update the source file with the computed text changes typeCobolWorkspace.UpdateSourceFile(objUri, textChangedEvent); // DEBUG information RemoteConsole.Log("Udpated source file : " + objUri.LocalPath); foreach (var textChange in textChangedEvent.TextChanges) { RemoteConsole.Log(" - " + textChange.ToString()); } }
public static bool IsDeleteRight(this TextChange change, int count) { return(change.IsDeleteRight && change.AsDeleteRight().Item == count); }
protected void Create(params string[] lines) { _textBuffer = EditorUtil.CreateBuffer(lines); _factory = new MockRepository(MockBehavior.Loose); _textCaret = _factory.Create<ITextCaret>(); _textView = _factory.Create<ITextView>(); _textView.SetupGet(x => x.Caret).Returns(_textCaret.Object); _textView.SetupGet(x => x.HasAggregateFocus).Returns(true); _mouse = _factory.Create<IMouseDevice>(); _keyboard = _factory.Create<IKeyboardDevice>(); _vimBuffer = new MockVimBuffer() { TextViewImpl = _textView.Object, TextBufferImpl = _textBuffer }; _trackerRaw = new TextChangeTracker(_vimBuffer, _keyboard.Object, _mouse.Object); _tracker = _trackerRaw; _tracker.ChangeCompleted += (sender, data) => { _lastChange = data; }; }
public static TextChange.Combination AsCombination(this TextChange change) { return((TextChange.Combination)change); }
public void IncrementalParseTopDownCommentOutLines() { var text = @"// <Title> Query Expression syntax </Title> // <Description> // from, join, on, equals, into, let, orderby, ascending, descending, group, by // @-with contextual keywords parseable as a type or identifier in a query expression // Various combinations // </Description> // <RelatedBugs></RelatedBugs> //<Expects status=success></Expects> // <Code> using System; using System.Linq; public class from { } public class join { } public class on { } public class equals { } public class into { } public class let { } public class orderby : let { } public class ascending : orderby, descending { } public interface descending { } public class group { } public class by { } public class QueryExpressionTest { public static int Main() { var array02a = new[] { new join(), new join(), new join() } as object[]; var array02b = new[] { new join(), new join(), new join() } as object[]; var query02 = from i in array02a join j in array02b on (@from)i equals (@from)j select new { i, j }; var array03a = new[] { new on(), new on(), new on() } as object[]; var array03b = new[] { new on(), new on(), new on() } as object[]; var query03 = from @on i in array03a join j in array03b on i equals (@on)j select new { i, j }; var array04a = new[] { new equals(), new equals(), new equals() } as object[]; return 0; } } "; var currTree = SyntaxFactory.ParseSyntaxTree(text); var currIText = currTree.GetText(); var items = text.Split('\n'); int currLen = 0; foreach (var item in items) { var span = new TextSpan(currLen, 0); var change = new TextChange(span, "// "); currLen += item.Length + 3; currIText = currIText.WithChanges(change); currTree = currTree.WithChangedText(currIText); var fullTree = SyntaxFactory.ParseSyntaxTree(currIText.ToString()); int incCount = currTree.GetCompilationUnitRoot().ChildNodesAndTokens().Count; int fullCount = fullTree.GetCompilationUnitRoot().ChildNodesAndTokens().Count; WalkTreeAndVerify(currTree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot()); } }
public static TextChange.DeleteLeft AsDeleteLeft(this TextChange change) { return((TextChange.DeleteLeft)change); }
public void DontReuseLambdaParameterAsMethodParameter() { var items = new string[] { "a b.c*/ d => {e(f =>", "/*", }; var oldText = SourceText.From(items[0]); var oldTree = SyntaxFactory.ParseSyntaxTree(oldText); // f is a simple lambda parameter var change = new TextChange(new TextSpan(0, 0), items[1]); // Prepend var newText = oldText.WithChanges(change); // f is a method decl parameter var incrTree = oldTree.WithChangedText(newText); var fullTree = SyntaxFactory.ParseSyntaxTree(newText); Assert.Equal( fullTree.GetDiagnostics().Select(d => d.ToString()), incrTree.GetDiagnostics().Select(d => d.ToString())); WalkTreeAndVerify(incrTree.GetRoot(), fullTree.GetRoot()); }
private static int CompareTextChanges(TextChange x, TextChange y) { return(x.Span.CompareTo(y.Span)); }
private CompletionChange(TextChange textChange, int?newPosition, bool includesCommitCharacter) { TextChange = textChange; NewPosition = newPosition; IncludesCommitCharacter = includesCommitCharacter; }
private static void CleanupSourceMappingEnd(FormattingContext context, Range sourceMappingRange, List <TextChange> changes, bool newLineWasAddedAtStart) { // // We look through every source mapping that intersects with the affected range and // bring the content after the last line to its own line and adjust its indentation, // // E.g, // // @{ // if (true) // { <div></div> // } // } // // becomes, // // @{ // if (true) // { // </div></div> // } // } // var text = context.SourceText; var sourceMappingSpan = sourceMappingRange.AsTextSpan(text); var mappingEndLineIndex = sourceMappingRange.End.Line; var startsInCSharpContext = context.Indentations[mappingEndLineIndex].StartsInCSharpContext; // If the span is on a single line, and we added a line, then end point is now on a line that does start in a C# context. if (!startsInCSharpContext && newLineWasAddedAtStart && sourceMappingRange.Start.Line == mappingEndLineIndex) { startsInCSharpContext = true; } if (!startsInCSharpContext) { // For corner cases like (Position marked with |), // It is already in a separate line. It doesn't need cleaning up. // @{ // if (true} // { // |<div></div> // } // } // return; } var endSpan = TextSpan.FromBounds(sourceMappingSpan.End, sourceMappingSpan.End); if (!ShouldFormat(context, endSpan, allowImplicitStatements: false)) { // We don't want to run cleanup on this range. return; } var contentStartOffset = text.Lines[mappingEndLineIndex].GetFirstNonWhitespaceOffset(sourceMappingRange.End.Character); if (contentStartOffset == null) { // There is no content after the end of this source mapping. No need to clean up. return; } var spanToReplace = new TextSpan(sourceMappingSpan.End, 0); if (!context.TryGetIndentationLevel(spanToReplace.End, out var contentIndentLevel)) { // Can't find the correct indentation for this content. Leave it alone. return; } // At this point, `contentIndentLevel` should contain the correct indentation level for `}` in the above example. var replacement = context.NewLineString + context.GetIndentationLevelString(contentIndentLevel); // After the below change the above example should look like, // @{ // if (true) // { // <div></div> // } // } var change = new TextChange(spanToReplace, replacement); changes.Add(change); }
/// <summary> /// Returns true if the specified change is an insertion of text at the end of this span. /// </summary> protected internal static bool IsEndDeletion(Span target, TextChange change) { return(change.IsDelete && IsAtEndOfSpan(target, change)); }
public override async Task <CompletionChange> GetChangeAsync(Document doc, CompletionItem item, char?commitKey = default(char?), CancellationToken cancellationToken = default(CancellationToken)) { (string beforeText, string afterText, string newMethod) = await GetInsertText(item.Properties); TextChange change; if (newMethod != null && RoslynCompletionData.RequestInsertText) // check for completion window manager to prevent the insertion cursor popup when the changes are queried by code diagnostics. { change = new TextChange(new TextSpan(item.Span.Start, item.Span.Length), item.Properties [MethodNameKey] + ";"); var semanticModel = await doc.GetSemanticModelAsync(cancellationToken); if (!doc.IsOpen() || await doc.IsForkedDocumentWithSyntaxChangesAsync(cancellationToken)) { return(CompletionChange.Create(change)); } var document = IdeApp.Workbench.ActiveDocument; var editor = document.Editor; void StartInsertionMode() { if (editor.EditMode != EditMode.Edit) { return; } var parsedDocument = document.DocumentContext.ParsedDocument; var declaringType = semanticModel.GetEnclosingSymbolMD <INamedTypeSymbol> (item.Span.Start, default(CancellationToken)); var insertionPoints = InsertionPointService.GetInsertionPoints( document.Editor, semanticModel, declaringType, editor.CaretOffset ); var options = new InsertionModeOptions( GettextCatalog.GetString("Create new method"), insertionPoints, point => { if (!point.Success) { return; } point.InsertionPoint.Insert(document.Editor, document.DocumentContext, newMethod); } ); editor.StartInsertionMode(options); } if (editor.TextView is Microsoft.VisualStudio.Text.Editor.IMdTextView) { await Runtime.RunInMainThread(StartInsertionMode); } else { StartInsertionMode(); } return(CompletionChange.Create(change)); } change = new TextChange(new TextSpan(item.Span.Start, item.Span.Length), beforeText + afterText); return(CompletionChange.Create(change, item.Span.Start + (beforeText != null ? beforeText.Length : 0))); }
/// <summary> /// Returns true if the specified change is a replacement of text at the end of this span. /// </summary> protected internal static bool IsEndReplace(Span target, TextChange change) { return(change.IsReplace && IsAtEndOfSpan(target, change)); }
protected internal static bool IsAtEndOfSpan(Span target, TextChange change) { return((change.OldPosition + change.OldLength) == (target.Start.AbsoluteIndex + target.Length)); }
static string GetTextChangeTextWithCaretAtLocation(SourceText sourceText, TextChange textChange, LinePosition desiredCaretLinePosition) { var desiredCaretLocation = sourceText.Lines.GetPosition(desiredCaretLinePosition); Debug.Assert(desiredCaretLocation >= textChange.Span.Start); var offsetInTextChange = desiredCaretLocation - textChange.Span.Start; var newText = textChange.NewText !.Insert(offsetInTextChange, "$0"); return(newText); }
internal static async Task VerifyExpectedTextAsync( string intentName, string activeDocument, string[] additionalDocuments, string[] expectedTexts, OptionsCollection?options = null, string?intentData = null, string?priorText = null) { var documentSet = additionalDocuments.Prepend(activeDocument).ToArray(); using var workspace = TestWorkspace.CreateCSharp(documentSet, composition: EditorTestCompositions.EditorFeatures); options?.SetGlobalOptions(workspace.GlobalOptions); var intentSource = workspace.ExportProvider.GetExportedValue <IIntentSourceProvider>(); // The first document will be the active document. var document = workspace.Documents.Single(d => d.Name == "test1.cs"); var textBuffer = document.GetTextBuffer(); // Get the text change to rewind the document to the correct pre-intent location. var rewindTextChange = new TextChange(document.AnnotatedSpans["typed"].Single(), priorText ?? string.Empty); // Get the current snapshot span to pass in. var currentSnapshot = new SnapshotSpan(textBuffer.CurrentSnapshot, new Span(0, textBuffer.CurrentSnapshot.Length)); var priorSelection = TextSpan.FromBounds(rewindTextChange.Span.Start, rewindTextChange.Span.Start); if (document.AnnotatedSpans.ContainsKey("priorSelection")) { priorSelection = document.AnnotatedSpans["priorSelection"].Single(); } var intentContext = new IntentRequestContext( intentName, currentSnapshot, ImmutableArray.Create(rewindTextChange), priorSelection, intentData: intentData); var results = await intentSource.ComputeIntentsAsync(intentContext, CancellationToken.None).ConfigureAwait(false); // For now, we're just taking the first result to match intellicode behavior. var result = results.First(); var actualDocumentTexts = new List <string>(); foreach (var documentChange in result.DocumentChanges) { // Get the document and open it. Since we're modifying the text buffer we don't care about linked documents. var documentBuffer = workspace.GetTestDocument(documentChange.Key).GetTextBuffer(); using var edit = documentBuffer.CreateEdit(); foreach (var change in documentChange.Value) { edit.Replace(change.Span.ToSpan(), change.NewText); } edit.Apply(); actualDocumentTexts.Add(documentBuffer.CurrentSnapshot.GetText()); } Assert.Equal(expectedTexts.Length, actualDocumentTexts.Count); foreach (var expectedText in expectedTexts) { Assert.True(actualDocumentTexts.Contains(expectedText)); } }
public CompletionChange WithTextChange(TextChange textChange) { return(new CompletionChange(textChange, this.NewPosition, this.IncludesCommitCharacter)); }
private static List<TextChange> MergeOverlappingRegions(List<TextChange> changes) { // Note: we assume the changes are ordered by CompareTextChanges var newChanges = new List<TextChange>(); for (int i = 0; i < changes.Count; i++) { TextChange change = changes[i]; for (int j = i + 1; j < changes.Count; j++) { TextChange nextChange = changes[j]; if (nextChange.Span.Start <= change.Span.End && nextChange.Span.End >= change.Span.End) { // This change overlaps but is not contained within the previous change. // In the case that this change ends where the previous change ends, we need to take // the replacement text of this change, because it is possible for end directives to // need non-empty replacement. change = new TextChange(new TextSpan(change.Span.Start, nextChange.Span.End - change.Span.Start), nextChange.NewText); i = j; } } newChanges.Add(change); } return newChanges; }
private void RewriteStyleAttributes(ResponseAnalysisContext context, string html) { var headIndex = html.IndexOf("</head>", StringComparison.OrdinalIgnoreCase); if (headIndex == -1) { return; } SortedList <string, StringSegment> inlineStyles = null; foreach (var index in FastHtmlParser.FindAllAttributeIndexes(html, "style")) { // get content var inner = FastHtmlParser.GetAttributeValueAtName(html, "style", index); if (inner.Length == 0) { continue; } if (inlineStyles == null) { inlineStyles = new SortedList <string, StringSegment>(); } // compute hash var hash = ComputeHash(inner.Trim()); if (!inlineStyles.ContainsKey(hash)) { inlineStyles.Add(hash, inner); } // add change context.AddChange(TextChange.Remove(html, index, inner.Offset + inner.Length + 1 - index)); var tagIndex = FastHtmlParser.FindOpenTagAtAttribute(html, index); var classAttribute = FastHtmlParser.GetAttributeValueAtTag(html, "class", tagIndex); if (classAttribute.Length > 0) { context.AddChange(TextChange.Insert(html, classAttribute.Offset + classAttribute.Length, "_" + hash)); } else { context.AddChange(FastHtmlParser.CreateInsertAttributeChange(html, tagIndex, "class", "_" + hash)); } } // assemble inline styles if (inlineStyles != null && inlineStyles.Count > 0) { var hashBuilder = new InplaceStringBuilder(inlineStyles.Count * HashBytes * 2); foreach (var entry in inlineStyles) { hashBuilder.Append(entry.Key); } var hash = ComputeHash(hashBuilder.ToString()); if (!InlineContentService.ContainsStyleByHash(hash)) { var contentBuilder = StringBuilderPool.Get(); foreach (var entry in inlineStyles) { contentBuilder.Append('.'); contentBuilder.Append('_'); contentBuilder.Append(entry.Key); contentBuilder.Append('{'); contentBuilder.Append(entry.Value.Value); contentBuilder.Append('}'); } InlineContentService.TryAddStyleByHash(hash, contentBuilder.ToString()); StringBuilderPool.Return(contentBuilder); } var tag = $"<link rel=\"stylesheet\" href=\"/.waf/styles/{hash}\" type=\"text/css\" />"; context.AddChange(TextChange.Insert(html, headIndex, tag)); } }
public void TearDown() { _tracker = null; _textBuffer = null; _lastChange = null; }
internal override async Task <CompletionChange> GetChangeAsync(Document document, CompletionItem completionItem, TextSpan completionListSpan, char?commitKey, CancellationToken cancellationToken) { LogCommit(); var containingNamespace = ImportCompletionItem.GetContainingNamespace(completionItem); if (await ShouldCompleteWithFullyQualifyTypeName().ConfigureAwait(false)) { var fullyQualifiedName = $"{containingNamespace}.{completionItem.DisplayText}"; var change = new TextChange(completionListSpan, fullyQualifiedName); return(CompletionChange.Create(change)); } // Find context node so we can use it to decide where to insert using/imports. var tree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var root = await tree.GetRootAsync(cancellationToken).ConfigureAwait(false); var addImportContextNode = root.FindToken(completionListSpan.Start, findInsideTrivia: true).Parent; // Add required using/imports directive. var addImportService = document.GetRequiredLanguageService <IAddImportsService>(); var optionSet = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); var placeSystemNamespaceFirst = optionSet.GetOption(GenerationOptions.PlaceSystemNamespaceFirst, document.Project.Language); var compilation = await document.Project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); var importNode = CreateImport(document, containingNamespace); var rootWithImport = addImportService.AddImport(compilation, root, addImportContextNode, importNode, placeSystemNamespaceFirst, cancellationToken); var documentWithImport = document.WithSyntaxRoot(rootWithImport); // This only formats the annotated import we just added, not the entire document. var formattedDocumentWithImport = await Formatter.FormatAsync(documentWithImport, Formatter.Annotation, cancellationToken : cancellationToken).ConfigureAwait(false); var builder = ArrayBuilder <TextChange> .GetInstance(); // Get text change for add import var importChanges = await formattedDocumentWithImport.GetTextChangesAsync(document, cancellationToken).ConfigureAwait(false); builder.AddRange(importChanges); // Create text change for complete type name. // // Note: Don't try to obtain TextChange for completed type name by replacing the text directly, // then use Document.GetTextChangesAsync on document created from the changed text. This is // because it will do a diff and return TextChanges with minimum span instead of actual // replacement span. // // For example: If I'm typing "asd", the completion provider could be triggered after "a" // is typed. Then if I selected type "AsnEncodedData" to commit, by using the approach described // above, we will get a TextChange of "AsnEncodedDat" with 0 length span, instead of a change of // the full display text with a span of length 1. This will later mess up span-tracking and end up // with "AsnEncodedDatasd" in the code. builder.Add(new TextChange(completionListSpan, completionItem.DisplayText)); // Then get the combined change var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var newText = text.WithChanges(builder); return(CompletionChange.Create(Utilities.Collapse(newText, builder.ToImmutableAndFree()))); async Task <bool> ShouldCompleteWithFullyQualifyTypeName() { if (!IsAddingImportsSupported(document)) { return(true); } // We might need to qualify unimported types to use them in an import directive, because they only affect members of the containing // import container (e.g. namespace/class/etc. declarations). // // For example, `List` and `StringBuilder` both need to be fully qualified below: // // using CollectionOfStringBuilders = System.Collections.Generic.List<System.Text.StringBuilder>; // // However, if we are typing in an C# using directive that is inside a nested import container (i.e. inside a namespace declaration block), // then we can add an using in the outer import container instead (this is not allowed in VB). // // For example: // // using System.Collections.Generic; // using System.Text; // // namespace Foo // { // using CollectionOfStringBuilders = List<StringBuilder>; // } // // Here we will always choose to qualify the unimported type, just to be consistent and keeps things simple. return(await IsInImportsDirectiveAsync(document, completionListSpan.Start, cancellationToken).ConfigureAwait(false)); } }
public void IncrementalParseStopAtEscapeBackSlash() { var text1 = @"using System; class Program { static void Main() { "; var text2 = @" Console.WriteLine(""\'\0\a\b\"; var comp = CSharpTestBase.CreateCompilationWithMscorlib(SyntaxFactory.ParseSyntaxTree(String.Empty)); var oldTree = comp.SyntaxTrees.First(); var oldIText = oldTree.GetText(); var span = new TextSpan(oldIText.Length, 0); var change = new TextChange(span, text1); var newIText = oldIText.WithChanges(change); var newTree = oldTree.WithChangedText(newIText); var fullTree = SyntaxFactory.ParseSyntaxTree(newIText.ToString(), options: newTree.Options); var fullText = fullTree.GetCompilationUnitRoot().ToFullString(); var incText = newTree.GetCompilationUnitRoot().ToFullString(); Assert.Equal(fullText.Length, incText.Length); Assert.Equal(fullText, incText); // oldTree = newTree; oldIText = oldTree.GetText(); span = new TextSpan(oldIText.Length, 0); change = new TextChange(span, text2); newIText = oldIText.WithChanges(change); newTree = oldTree.WithChangedText(newIText); fullTree = SyntaxFactory.ParseSyntaxTree(newIText.ToString(), options: newTree.Options); fullText = fullTree.GetCompilationUnitRoot().ToFullString(); incText = newTree.GetCompilationUnitRoot().ToFullString(); Assert.Equal(fullText.Length, incText.Length); Assert.Equal(fullText, incText); }
/// <summary> /// Starts the fast operations to be executed at every user interaction with the <see cref="RichTextBox"/>. /// </summary> /// <param name="richTextBox"> /// The <see cref="RichTextBox"/> which text was changed. /// </param> /// <param name="eventArgs"> /// The <see cref="TextChangedEventArgs"/> containing specific information about the user interaction. /// </param> /// <param name="scheduler"> /// The <see cref="TaskScheduler"/> containing GUI-context. /// </param> /// <returns> /// The <see cref="Task"/>. /// </returns> /// <exception cref="ArgumentNullException"> /// One or all of the parameters was/ were passed as null. /// </exception> public static async Task CodeAlteredAsync(RichTextBox richTextBox, TextChangedEventArgs eventArgs, TaskScheduler scheduler) { if (richTextBox == null) { throw new ArgumentNullException("richTextBox"); } if (eventArgs == null) { throw new ArgumentNullException("eventArgs"); } if (scheduler == null) { throw new ArgumentNullException("scheduler"); } if (NoTextChangedPlox) { return; } var text = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd).Text.Replace( " ", string.Empty); if (richTextBox.Document == null || text == "\r\n") { RecognitionEngine.AllWordsInCode.Clear(); MistakeEngine.Mistakes.Clear(); MainWindow.ErrorListView.Items.Clear(); return; } TextChange textChange = null; var textChanges = eventArgs.Changes; if (textChanges != null && textChanges.Count > 0) { textChange = textChanges.First(); } if (textChange == null || (textChange.AddedLength <= 0 && textChange.RemovedLength <= 0)) { return; } IEnumerable <Word> changedWords = RecognitionEngine.RecognizeWordsInCode(richTextBox.CaretPosition, CurrentProgrammingLanguage).Result; var newWord = changedWords.First(); if (newWord.Content == string.Empty) { await FacilitateCoding.PseudoSenseAsync(newWord, RecognitionEngine.AllWordsInCode, CurrentProgrammingLanguage, MainWindow.CodeListBox); return; } switch (pseudoSenseTask.Status) { case TaskStatus.Created: pseudoSenseTask.Start(scheduler); break; case TaskStatus.RanToCompletion: case TaskStatus.Faulted: pseudoSenseTask.Dispose(); pseudoSenseTask = new Task( () => FacilitateCoding.PseudoSenseAsync(newWord, RecognitionEngine.AllWordsInCode, CurrentProgrammingLanguage, MainWindow.CodeListBox), cancellationToken.Token, TaskCreationOptions.LongRunning); pseudoSenseTask.Start(scheduler); break; } }
public void IncrementalParseStatementAfterQuery() { var text = @" using System.Linq; class equals { static void Main(string[] args) { equals[] a; var q = from x in args select x; a = new[] { new equals() }; } } "; var currTree = SyntaxFactory.ParseSyntaxTree(text); var currIText = currTree.GetText(); // Insert "// " before the "x" in "select x"; the next statement becomes part of the query. var span = new TextSpan(text.LastIndexOf('x'), 0); var change = new TextChange(span, "// "); currIText = currIText.WithChanges(change); currTree = currTree.WithChangedText(currIText); var fullTree = SyntaxFactory.ParseSyntaxTree(currIText.ToString()); WalkTreeAndVerify(currTree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot()); }
public virtual EditResult ApplyChange(Span target, TextChange change) { return(ApplyChange(target, change, force: false)); }
[Fact]//(Skip = "Bug")] public void RemovePartialFromClassWithIncorrectSpan() { var test = @"partial class C{}"; var resultString = "class C{}"; var startTree = SyntaxTree.Parse(test); var finalString = startTree.GetCompilationUnitRoot().ToString(); var incrementalChange = new TextChange(startTree.Text, SourceText.From(resultString), new TextChangeRange[] { new TextChangeRange(new TextSpan(0, 7), 0) }); // NOTE: The string length here is a bit too short for the change var newTree = startTree.WithChange(incrementalChange); var output = newTree.GetCompilationUnitRoot().ToString(); Assert.Equal(output, resultString); }
protected internal static bool IsAtEndOfFirstLine(Span target, TextChange change) { int endOfFirstLine = target.Content.IndexOfAny(new char[] { (char)0x000d, (char)0x000a, (char)0x2028, (char)0x2029 }); return(endOfFirstLine == -1 || (change.OldPosition - target.Start.AbsoluteIndex) <= endOfFirstLine); }