public void StartInsertionMode(InsertionModeOptions insertionModeOptions) { if (insertionModeOptions == null) { throw new ArgumentNullException(nameof(insertionModeOptions)); } textEditorImpl.StartInsertionMode(insertionModeOptions); }
protected override async void ApplyDocumentTextChanged (DocumentId id, SourceText text) { var document = GetDocument (id); if (document == null) return; bool isOpen; var filePath = document.FilePath; Projection projection = null; foreach (var entry in ProjectionList) { var p = entry.Projections.FirstOrDefault (proj => proj?.Document?.FileName != null && FilePath.PathComparer.Equals (proj.Document.FileName, filePath)); if (p != null) { filePath = entry.File.FilePath; projection = p; break; } } var data = TextFileProvider.Instance.GetTextEditorData (filePath, out isOpen); // Guard against already done changes in linked files. // This shouldn't happen but the roslyn merging seems not to be working correctly in all cases :/ if (document.GetLinkedDocumentIds ().Length > 0 && isOpen && !(text.GetType ().FullName == "Microsoft.CodeAnalysis.Text.ChangedText")) { return; } SourceText formerText; lock (changedFiles) { if (changedFiles.TryGetValue (filePath, out formerText)) { if (formerText.Length == text.Length && formerText.ToString () == text.ToString ()) return; } changedFiles [filePath] = text; } SourceText oldFile; if (!isOpen || !document.TryGetText (out oldFile)) { oldFile = await document.GetTextAsync (); } var changes = text.GetTextChanges (oldFile).OrderByDescending (c => c.Span.Start).ToList (); int delta = 0; if (!isOpen) { delta = ApplyChanges (projection, data, changes); var formatter = CodeFormatterService.GetFormatter (data.MimeType); if (formatter.SupportsPartialDocumentFormatting) { var mp = GetMonoProject (CurrentSolution.GetProject (id.ProjectId)); string currentText = data.Text; foreach (var change in changes) { delta -= change.Span.Length - change.NewText.Length; var startOffset = change.Span.Start - delta; if (projection != null) { int originalOffset; if (projection.TryConvertFromProjectionToOriginal (startOffset, out originalOffset)) startOffset = originalOffset; } string str; if (change.NewText.Length == 0) { str = formatter.FormatText (mp.Policies, currentText, TextSegment.FromBounds (Math.Max (0, startOffset - 1), Math.Min (data.Length, startOffset + 1))); } else { str = formatter.FormatText (mp.Policies, currentText, new TextSegment (startOffset, change.NewText.Length)); } data.ReplaceText (startOffset, change.NewText.Length, str); } } data.Save (); if (projection != null) { await UpdateProjectionsDocuments (document, data); } else { OnDocumentTextChanged (id, new MonoDevelopSourceText (data), PreservationMode.PreserveValue); } FileService.NotifyFileChanged (filePath); } else { var formatter = CodeFormatterService.GetFormatter (data.MimeType); var documentContext = IdeApp.Workbench.Documents.FirstOrDefault (d => FilePath.PathComparer.Compare (d.FileName, filePath) == 0); var root = await projectChanges.NewProject.GetDocument (id).GetSyntaxRootAsync (); var annotatedNode = root.DescendantNodesAndSelf ().FirstOrDefault (n => n.HasAnnotation (TypeSystemService.InsertionModeAnnotation)); SyntaxToken? renameTokenOpt = root.GetAnnotatedNodesAndTokens (Microsoft.CodeAnalysis.CodeActions.RenameAnnotation.Kind) .Where (s => s.IsToken) .Select (s => s.AsToken ()) .Cast<SyntaxToken?> () .FirstOrDefault (); if (documentContext != null) { var editor = (TextEditor)data; await Runtime.RunInMainThread (async () => { using (var undo = editor.OpenUndoGroup ()) { var oldVersion = editor.Version; delta = ApplyChanges (projection, data, changes); var versionBeforeFormat = editor.Version; if (formatter.SupportsOnTheFlyFormatting) { foreach (var change in changes) { delta -= change.Span.Length - change.NewText.Length; var startOffset = change.Span.Start - delta; if (projection != null) { int originalOffset; if (projection.TryConvertFromProjectionToOriginal (startOffset, out originalOffset)) startOffset = originalOffset; } if (change.NewText.Length == 0) { formatter.OnTheFlyFormat (editor, documentContext, TextSegment.FromBounds (Math.Max (0, startOffset - 1), Math.Min (data.Length, startOffset + 1))); } else { formatter.OnTheFlyFormat (editor, documentContext, new TextSegment (startOffset, change.NewText.Length)); } } } if (annotatedNode != null && GetInsertionPoints != null) { IdeApp.Workbench.Documents.First (d => d.FileName == editor.FileName).Select (); var formattedVersion = editor.Version; int startOffset = versionBeforeFormat.MoveOffsetTo (editor.Version, annotatedNode.Span.Start); int endOffset = versionBeforeFormat.MoveOffsetTo (editor.Version, annotatedNode.Span.End); // alway whole line start & delimiter var startLine = editor.GetLineByOffset (startOffset); startOffset = startLine.Offset; var endLine = editor.GetLineByOffset (endOffset); endOffset = endLine.EndOffsetIncludingDelimiter + 1; var insertionCursorSegment = TextSegment.FromBounds (startOffset, endOffset); string textToInsert = editor.GetTextAt (insertionCursorSegment).TrimEnd (); editor.RemoveText (insertionCursorSegment); var insertionPoints = await GetInsertionPoints (editor, editor.CaretOffset); if (insertionPoints.Count == 0) { // Just to get sure if no insertion points -> go back to the formatted version. var textChanges = editor.Version.GetChangesTo (formattedVersion).ToList (); using (var undo2 = editor.OpenUndoGroup ()) { foreach (var v in textChanges) { editor.ReplaceText (v.Offset, v.RemovalLength, v.InsertedText); } } return; } string insertionModeOperation; const int CSharpMethodKind = 8875; bool isMethod = annotatedNode.RawKind == CSharpMethodKind; if (!isMethod) { // atm only for generate field/property : remove all new lines generated & just insert the plain node. // for methods it's not so easy because of "extract code" changes. foreach (var v in editor.Version.GetChangesTo (oldVersion).ToList ()) { editor.ReplaceText (v.Offset, v.RemovalLength, v.InsertedText); } } switch (annotatedNode.RawKind) { case 8873: // C# field insertionModeOperation = GettextCatalog.GetString ("Insert Field"); break; case CSharpMethodKind: insertionModeOperation = GettextCatalog.GetString ("Insert Method"); break; case 8892: // C# property insertionModeOperation = GettextCatalog.GetString ("Insert Property"); break; default: insertionModeOperation = GettextCatalog.GetString ("Insert Code"); break; } var options = new InsertionModeOptions ( insertionModeOperation, insertionPoints, point => { if (!point.Success) return; point.InsertionPoint.Insert (editor, textToInsert); } ); options.ModeExitedAction += delegate (InsertionCursorEventArgs args) { if (!args.Success) { var textChanges = editor.Version.GetChangesTo (oldVersion).ToList (); using (var undo2 = editor.OpenUndoGroup ()) { foreach (var v in textChanges) { editor.ReplaceText (v.Offset, v.RemovalLength, v.InsertedText); } } } }; for (int i = 0; i < insertionPoints.Count; i++) { if (insertionPoints [i].Location.Line < editor.CaretLine) { options.FirstSelectedInsertionPoint = Math.Min (isMethod ? i + 1 : i, insertionPoints.Count - 1); } else { break; } } options.ModeExitedAction += delegate { if (renameTokenOpt.HasValue) StartRenameSession (editor, documentContext, versionBeforeFormat, renameTokenOpt.Value); }; editor.StartInsertionMode (options); } } }); } if (projection != null) { await UpdateProjectionsDocuments (document, data); } else { OnDocumentTextChanged (id, new MonoDevelopSourceText (data.CreateDocumentSnapshot ()), PreservationMode.PreserveValue); } await Runtime.RunInMainThread (() => { if (IdeApp.Workbench != null) foreach (var w in IdeApp.Workbench.Documents) w.StartReparseThread (); }); } }
internal async void Run () { var token = default(CancellationToken); var insertionAction = act as InsertionAction; if (insertionAction != null) { var insertion = await insertionAction.CreateInsertion (token).ConfigureAwait (false); var document = await IdeApp.Workbench.OpenDocument (insertion.Location.SourceTree.FilePath, documentContext.Project); var parsedDocument = await document.UpdateParseDocument (); if (parsedDocument != null) { var insertionPoints = InsertionPointService.GetInsertionPoints ( document.Editor, parsedDocument, insertion.Type, insertion.Location.SourceSpan.Start ); var options = new InsertionModeOptions ( insertionAction.Title, insertionPoints, point => { if (!point.Success) return; var node = Formatter.Format (insertion.Node, TypeSystemService.Workspace, document.GetOptionSet (), token); point.InsertionPoint.Insert (document.Editor, document, node.ToString ()); // document = await Simplifier.ReduceAsync(document.AnalysisDocument, Simplifier.Annotation, cancellationToken: token).ConfigureAwait(false); } ); document.Editor.StartInsertionMode (options); return; } } var oldSolution = documentContext.AnalysisDocument.Project.Solution; var updatedSolution = oldSolution; if (RefactoringService.OptionSetCreation != null) documentContext.RoslynWorkspace.Options = RefactoringService.OptionSetCreation (editor, documentContext); using (var undo = editor.OpenUndoGroup ()) { foreach (var operation in act.GetOperationsAsync (token).Result) { var applyChanges = operation as ApplyChangesOperation; if (applyChanges == null) { operation.Apply (documentContext.RoslynWorkspace, token); continue; } if (updatedSolution == oldSolution) { updatedSolution = applyChanges.ChangedSolution; } operation.Apply (documentContext.RoslynWorkspace, token); } } TryStartRenameSession (documentContext.RoslynWorkspace, oldSolution, updatedSolution, token); }
public override void InsertCompletionText (CompletionListWindow window, ref KeyActions ka, KeyDescriptor descriptor) { // insert add/remove event handler code after +=/-= var editor = factory.Ext.Editor; bool AddSemicolon = true; var position = window.CodeCompletionContext.TriggerOffset; editor.ReplaceText (position, editor.CaretOffset - position, this.DisplayText + (AddSemicolon ? ";" : "")); var document = IdeApp.Workbench.ActiveDocument; var parsedDocument = document.UpdateParseDocument ().Result; var semanticModel = parsedDocument.GetAst<SemanticModel> (); var declaringType = semanticModel.GetEnclosingSymbol<INamedTypeSymbol> (position, default(CancellationToken)); var enclosingSymbol = semanticModel.GetEnclosingSymbol<ISymbol> (position, default(CancellationToken)); var insertionPoints = InsertionPointService.GetInsertionPoints ( document.Editor, parsedDocument, declaringType, editor.CaretOffset ); var options = new InsertionModeOptions ( GettextCatalog.GetString ("Create new method"), insertionPoints, point => { if (!point.Success) return; var indent = "\t"; var sb = new StringBuilder (); if (enclosingSymbol != null && enclosingSymbol.IsStatic) sb.Append ("static "); sb.Append ("void "); int pos2 = sb.Length; sb.Append (this.DisplayText); sb.Append (' '); sb.Append("("); var delegateMethod = delegateType.GetDelegateInvokeMethod(); for (int k = 0; k < delegateMethod.Parameters.Length; k++) { if (k > 0) { sb.Append(", "); } sb.Append (RoslynCompletionData.SafeMinimalDisplayString (delegateMethod.Parameters [k], semanticModel, position, MonoDevelop.Ide.TypeSystem.Ambience.LabelFormat)); } sb.Append(")"); sb.Append (editor.EolMarker); sb.Append (indent); sb.Append ("{"); sb.Append (editor.EolMarker); sb.Append (indent); sb.Append (editor.Options.GetIndentationString ()); //int cursorPos = pos + sb.Length; sb.Append (editor.EolMarker); sb.Append (indent); sb.Append ("}"); point.InsertionPoint.Insert (document.Editor, document, sb.ToString ()); // // start text link mode after insert // var links = new List<TextLink> (); // var link = new TextLink ("name"); // // link.AddLink (new TextSegment (initialOffset, this.DisplayText.Length)); // link.AddLink (new TextSegment (initialOffset + pos + pos2, this.DisplayText.Length)); // links.Add (link); // editor.StartTextLinkMode (new TextLinkModeOptions (links)); } ); editor.StartInsertionMode (options); }
void ITextEditorImpl.StartInsertionMode (InsertionModeOptions insertionModeOptions) { var mode = new InsertionCursorEditMode (TextEditor, insertionModeOptions.InsertionPoints.Select (ip => new Mono.TextEditor.InsertionPoint ( new Mono.TextEditor.DocumentLocation (ip.Location.Line, ip.Location.Column), (Mono.TextEditor.NewLineInsertion)ip.LineBefore, (Mono.TextEditor.NewLineInsertion)ip.LineAfter )).ToList ()); if (mode.InsertionPoints.Count == 0) { return; } var helpWindow = new Mono.TextEditor.PopupWindow.InsertionCursorLayoutModeHelpWindow (); helpWindow.TitleText = insertionModeOptions.Operation; mode.HelpWindow = helpWindow; mode.CurIndex = insertionModeOptions.FirstSelectedInsertionPoint; mode.StartMode (); mode.Exited += delegate(object s, Mono.TextEditor.InsertionCursorEventArgs iCArgs) { insertionModeOptions.ModeExitedAction (new MonoDevelop.Ide.Editor.InsertionCursorEventArgs (iCArgs.Success, new MonoDevelop.Ide.Editor.InsertionPoint ( new MonoDevelop.Ide.Editor.DocumentLocation (iCArgs.InsertionPoint.Location.Line, iCArgs.InsertionPoint.Location.Column), (MonoDevelop.Ide.Editor.NewLineInsertion)iCArgs.InsertionPoint.LineBefore, (MonoDevelop.Ide.Editor.NewLineInsertion)iCArgs.InsertionPoint.LineAfter ) )); }; }
public static async Task InsertMemberWithCursor (string operation, Projects.Project project, ITypeSymbol type, Location part, SyntaxNode newMember, CancellationToken cancellationToken = default(CancellationToken)) { if (operation == null) throw new ArgumentNullException (nameof (operation)); if (project == null) throw new ArgumentNullException (nameof (project)); if (type == null) throw new ArgumentNullException (nameof (type)); if (newMember == null) throw new ArgumentNullException (nameof (newMember)); var ws = MonoDevelop.Ide.TypeSystem.TypeSystemService.GetWorkspace (project.ParentSolution); var projectId = ws.GetProjectId (project); var docId = ws.GetDocumentId (projectId, part.SourceTree.FilePath); var document = ws.GetDocument (docId, cancellationToken); var root = await document.GetSyntaxRootAsync (cancellationToken).ConfigureAwait (false); var typeDecl = (ClassDeclarationSyntax)root.FindNode (part.SourceSpan); // for some reason the reducer doesn't reduce this var systemVoid = newMember.DescendantNodesAndSelf ().OfType<QualifiedNameSyntax> ().FirstOrDefault (ma => ma.ToString () == "System.Void"); if (systemVoid != null) newMember = newMember.ReplaceNode (systemVoid, SyntaxFactory.ParseTypeName ("void")); var newRoot = root.ReplaceNode (typeDecl, typeDecl.AddMembers ((MemberDeclarationSyntax)newMember.WithAdditionalAnnotations (Simplifier.Annotation, Formatter.Annotation, insertedMemberAnnotation))); var doc = await IdeApp.Workbench.OpenDocument (part.SourceTree.FilePath, project, true); var policy = project.Policies.Get<CSharpFormattingPolicy> ("text/x-csharp"); var textPolicy = project.Policies.Get<TextStylePolicy> ("text/x-csharp"); var projectOptions = policy.CreateOptions (textPolicy); document = document.WithSyntaxRoot (newRoot); document = await Formatter.FormatAsync (document, Formatter.Annotation, projectOptions, cancellationToken).ConfigureAwait (false); document = await Simplifier.ReduceAsync (document, Simplifier.Annotation, projectOptions, cancellationToken).ConfigureAwait (false); root = await document.GetSyntaxRootAsync (cancellationToken).ConfigureAwait (false); var node = root.GetAnnotatedNodes (insertedMemberAnnotation).Single (); Application.Invoke (async delegate { var insertionPoints = InsertionPointService.GetInsertionPoints ( doc.Editor, await doc.UpdateParseDocument (), type, part.SourceSpan.Start ); var options = new InsertionModeOptions ( operation, insertionPoints, point => { if (!point.Success) return; var text = node.ToString (); point.InsertionPoint.Insert (doc.Editor, doc, text); } ); doc.Editor.StartInsertionMode (options); }); }