public void StartInsertionMode(InsertionModeOptions insertionModeOptions)
 {
     if (insertionModeOptions == null)
     {
         throw new ArgumentNullException(nameof(insertionModeOptions));
     }
     textEditorImpl.StartInsertionMode(insertionModeOptions);
 }
Пример #2
0
		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);

		}
Пример #5
0
		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
					)
				));
			};
		}
Пример #6
0
		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);
			});
		}