diff options
Diffstat (limited to 'main/src/addins/CSharpBinding/MonoDevelop.CSharp.Formatting/CSharpTextEditorIndentation.cs')
-rw-r--r-- | main/src/addins/CSharpBinding/MonoDevelop.CSharp.Formatting/CSharpTextEditorIndentation.cs | 520 |
1 files changed, 271 insertions, 249 deletions
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Formatting/CSharpTextEditorIndentation.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Formatting/CSharpTextEditorIndentation.cs index fce4666569..8e289dfa3f 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Formatting/CSharpTextEditorIndentation.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Formatting/CSharpTextEditorIndentation.cs @@ -31,37 +31,32 @@ using MonoDevelop.Core; using MonoDevelop.Ide.Gui.Content; using MonoDevelop.Ide.CodeCompletion; using MonoDevelop.CSharp.Formatting; -using MonoDevelop.CSharp.Refactoring; -using Mono.TextEditor; using MonoDevelop.Ide.CodeTemplates; -using MonoDevelop.SourceEditor; -using ICSharpCode.NRefactory.CSharp.Completion; -using ICSharpCode.NRefactory.Editor; using System.Linq; using System.Text; -using ICSharpCode.NRefactory.CSharp; using MonoDevelop.Ide; -using ICSharpCode.NRefactory; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Ide.Editor.Extension; +using MonoDevelop.Projects; +using MonoDevelop.Core.Text; +using ICSharpCode.NRefactory6.CSharp; +using System.Threading; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis; namespace MonoDevelop.CSharp.Formatting { class CSharpTextEditorIndentation : TextEditorExtension { - CacheIndentEngine stateTracker; + internal ICSharpCode.NRefactory6.CSharp.CacheIndentEngine stateTracker; int cursorPositionBeforeKeyPress; - TextEditorData textEditorData { - get { - return document.Editor; - } - } - readonly IEnumerable<string> types = DesktopService.GetMimeTypeInheritanceChain (CSharpFormatter.MimeType); CSharpFormattingPolicy Policy { get { - if (Document != null && Document.Project != null && Document.Project.Policies != null) { - return Document.Project.Policies.Get<CSharpFormattingPolicy> (types); + if (DocumentContext != null && DocumentContext.Project != null && DocumentContext.Project.Policies != null) { + return DocumentContext.Project.Policies.Get<CSharpFormattingPolicy> (types); } return MonoDevelop.Projects.Policies.PolicyService.GetDefaultPolicy<CSharpFormattingPolicy> (types); } @@ -69,8 +64,8 @@ namespace MonoDevelop.CSharp.Formatting TextStylePolicy TextStylePolicy { get { - if (Document != null && Document.Project != null && Document.Project.Policies != null) { - return Document.Project.Policies.Get<TextStylePolicy> (types); + if (DocumentContext != null && DocumentContext.Project != null && DocumentContext.Project.Policies != null) { + return DocumentContext.Project.Policies.Get<TextStylePolicy> (types); } return MonoDevelop.Projects.Policies.PolicyService.GetDefaultPolicy<TextStylePolicy> (types); } @@ -81,17 +76,13 @@ namespace MonoDevelop.CSharp.Formatting static CSharpTextEditorIndentation () { CompletionWindowManager.WordCompleted += delegate(object sender, CodeCompletionContextEventArgs e) { - var editor = e.Widget as IExtensibleTextEditor; + var editor = e.Widget as IServiceProvider; if (editor == null) return; - var textEditorExtension = editor.Extension; - while (textEditorExtension != null && !(textEditorExtension is CSharpTextEditorIndentation)) { - textEditorExtension = textEditorExtension.Next; - } - var extension = textEditorExtension as CSharpTextEditorIndentation; + var extension = editor.GetService (typeof(CSharpTextEditorIndentation)) as CSharpTextEditorIndentation; if (extension == null) return; - extension.SafeUpdateIndentEngine (extension.textEditorData.Caret.Offset); + extension.SafeUpdateIndentEngine (extension.Editor.CaretOffset); if (extension.stateTracker.NeedsReindent) extension.DoReSmartIndent (); }; @@ -100,44 +91,12 @@ namespace MonoDevelop.CSharp.Formatting internal void SafeUpdateIndentEngine (int offset) { try { - stateTracker.Update (offset); + stateTracker.Update (Editor, offset); } catch (Exception e) { LoggingService.LogError ("Error while updating the indentation engine", e); } } - void HandleTextPaste (int insertionOffset, string text, int insertedChars) - { - if (document.Editor.Options.IndentStyle == IndentStyle.None || - document.Editor.Options.IndentStyle == IndentStyle.Auto) - return; - - // Just correct the start line of the paste operation - the text is already indented. - var curLine = Editor.GetLineByOffset (insertionOffset); - var curLineOffset = curLine.Offset; - SafeUpdateIndentEngine (curLineOffset); - if (!stateTracker.IsInsideOrdinaryCommentOrString) { - int pos = curLineOffset; - string curIndent = curLine.GetIndentation (textEditorData.Document); - int nlwsp = curIndent.Length; - - if (!stateTracker.LineBeganInsideMultiLineComment || (nlwsp < curLine.LengthIncludingDelimiter && textEditorData.Document.GetCharAt (curLineOffset + nlwsp) == '*')) { - // Possibly replace the indent - SafeUpdateIndentEngine (curLineOffset + curLine.Length); - string newIndent = stateTracker.ThisLineIndent; - if (newIndent != curIndent) { - if (CompletionWindowManager.IsVisible) { - if (pos < CompletionWindowManager.CodeCompletionContext.TriggerOffset) - CompletionWindowManager.CodeCompletionContext.TriggerOffset -= nlwsp; - } - textEditorData.Replace (pos, nlwsp, newIndent); - textEditorData.Document.CommitLineUpdate (textEditorData.Caret.Line); - } - } - } - textEditorData.FixVirtualIndentation (); - } - public static bool OnTheFlyFormatting { get { return PropertyService.Get ("OnTheFlyFormatting", true); @@ -147,24 +106,23 @@ namespace MonoDevelop.CSharp.Formatting } } - void RunFormatter (DocumentLocation location) + void RunFormatter (MonoDevelop.Ide.Editor.DocumentLocation location) { - if (OnTheFlyFormatting && textEditorData != null && !(textEditorData.CurrentMode is TextLinkEditMode) && !(textEditorData.CurrentMode is InsertionCursorEditMode)) { - OnTheFlyFormatter.Format (Document, location); - } + if (!OnTheFlyFormatting || Editor == null || Editor.EditMode != EditMode.Edit) + return; + var offset = Editor.LocationToOffset (location); + OnTheFlyFormatter.Format (Editor, DocumentContext, offset, offset); } - public override void Initialize () + protected override void Initialize () { base.Initialize (); - - if (textEditorData != null) { - textEditorData.Options.Changed += HandleTextOptionsChanged; + if (Editor != null) { + Editor.OptionsChanged += HandleTextOptionsChanged; HandleTextOptionsChanged (this, EventArgs.Empty); - textEditorData.Document.TextReplacing += HandleTextReplacing; - textEditorData.Document.TextReplaced += HandleTextReplaced; - textEditorData.Paste += HandleTextPaste; + Editor.TextChanging += HandleTextReplacing; + Editor.TextChanged += HandleTextReplaced; } if (IdeApp.Workspace != null) IdeApp.Workspace.ActiveConfigurationChanged += HandleTextOptionsChanged; @@ -172,47 +130,61 @@ namespace MonoDevelop.CSharp.Formatting bool indentationDisabled; + public static IEnumerable<string> GetDefinedSymbols (MonoDevelop.Projects.Project project) + { + var workspace = IdeApp.Workspace; + if (workspace == null || project == null) + yield break; + var configuration = project.GetConfiguration (workspace.ActiveConfiguration) as DotNetProjectConfiguration; + if (configuration != null) { + foreach (string s in configuration.GetDefineSymbols ()) + yield return s; + // Workaround for mcs defined symbol + if (configuration.TargetRuntime.RuntimeId == "Mono") + yield return "__MonoCS__"; + } + } + void HandleTextOptionsChanged (object sender, EventArgs e) { - var policy = Policy.CreateOptions (); - var options = Editor.CreateNRefactoryTextEditorOptions (); - options.IndentBlankLines = true; - IStateMachineIndentEngine indentEngine; + //var options = Editor.CreateNRefactoryTextEditorOptions (); + var policy = Policy.CreateOptions (Editor.Options); + //options.IndentBlankLines = true; + ICSharpCode.NRefactory6.CSharp.IStateMachineIndentEngine indentEngine; try { - var csharpIndentEngine = new CSharpIndentEngine (textEditorData.Document, options, policy); + var csharpIndentEngine = new ICSharpCode.NRefactory6.CSharp.CSharpIndentEngine (policy); //csharpIndentEngine.EnableCustomIndentLevels = true; - foreach (var symbol in MonoDevelop.CSharp.Highlighting.CSharpSyntaxMode.GetDefinedSymbols (document.Project)) { + foreach (var symbol in GetDefinedSymbols (DocumentContext.Project)) { csharpIndentEngine.DefineSymbol (symbol); } indentEngine = csharpIndentEngine; } catch (Exception ex) { LoggingService.LogError ("Error while creating the c# indentation engine", ex); - indentEngine = new NullIStateMachineIndentEngine (textEditorData.Document); + indentEngine = new ICSharpCode.NRefactory6.CSharp.NullIStateMachineIndentEngine (); } - stateTracker = new CacheIndentEngine (indentEngine); + stateTracker = new ICSharpCode.NRefactory6.CSharp.CacheIndentEngine (indentEngine); if (DefaultSourceEditorOptions.Instance.IndentStyle == IndentStyle.Auto) { - textEditorData.IndentationTracker = new DefaultIndentationTracker (textEditorData.Document); + Editor.SetIndentationTracker (null); } else { - textEditorData.IndentationTracker = new IndentVirtualSpaceManager (textEditorData, stateTracker); + Editor.SetIndentationTracker (new IndentVirtualSpaceManager (Editor, stateTracker)); } indentationDisabled = DefaultSourceEditorOptions.Instance.IndentStyle == IndentStyle.Auto || DefaultSourceEditorOptions.Instance.IndentStyle == IndentStyle.None; if (indentationDisabled) { - textEditorData.TextPasteHandler = null; + Editor.SetTextPasteHandler (null); } else { - textEditorData.TextPasteHandler = new TextPasteIndentEngine (stateTracker, options, policy); + Editor.SetTextPasteHandler (new CSharpTextPasteHandler (this, stateTracker, policy)); } } public override void Dispose () { - if (textEditorData != null) { - textEditorData.TextPasteHandler = null; - textEditorData.Paste -= HandleTextPaste; - textEditorData.Options.Changed -= HandleTextOptionsChanged; - textEditorData.IndentationTracker = null; - textEditorData.Document.TextReplacing -= HandleTextReplacing; - textEditorData.Document.TextReplaced -= HandleTextReplaced; + if (Editor != null) { + Editor.SetTextPasteHandler (null); + Editor.OptionsChanged -= HandleTextOptionsChanged; + Editor.SetIndentationTracker (null); + Editor.TextChanging -= HandleTextReplacing; + Editor.TextChanged -= HandleTextReplaced; } IdeApp.Workspace.ActiveConfigurationChanged -= HandleTextOptionsChanged; stateTracker = null; @@ -221,31 +193,31 @@ namespace MonoDevelop.CSharp.Formatting bool? wasInVerbatimString; - void HandleTextReplaced (object sender, DocumentChangeEventArgs e) + void HandleTextReplaced (object sender, MonoDevelop.Core.Text.TextChangeEventArgs e) { - stateTracker.ResetEngineToPosition (e.Offset); + stateTracker.ResetEngineToPosition (Editor, e.Offset); if (wasInVerbatimString == null) return; - if (e.RemovalLength != 1 || textEditorData.Document.CurrentAtomicUndoOperationType == OperationType.Format) + if (e.RemovalLength != 1 /*|| textEditorData.Document.CurrentAtomicUndoOperationType == OperationType.Format*/) return; - SafeUpdateIndentEngine (Math.Min (textEditorData.Document.TextLength, e.Offset + e.InsertionLength + 1)); + SafeUpdateIndentEngine (Math.Min (Editor.Length, e.Offset + e.InsertionLength + 1)); if (wasInVerbatimString == true && !stateTracker.IsInsideVerbatimString) { - textEditorData.Document.TextReplacing -= HandleTextReplacing; - textEditorData.Document.TextReplaced -= HandleTextReplaced; - ConvertVerbatimStringToNormal (textEditorData, e.Offset + e.InsertionLength + 1); - textEditorData.Document.TextReplacing += HandleTextReplacing; - textEditorData.Document.TextReplaced += HandleTextReplaced; + Editor.TextChanging -= HandleTextReplacing; + Editor.TextChanged -= HandleTextReplaced; + ConvertVerbatimStringToNormal (Editor, e.Offset + e.InsertionLength + 1); + Editor.TextChanging += HandleTextReplacing; + Editor.TextChanged += HandleTextReplaced; } } - void HandleTextReplacing (object sender, DocumentChangeEventArgs e) + void HandleTextReplacing (object sender, MonoDevelop.Core.Text.TextChangeEventArgs e) { wasInVerbatimString = null; var o = e.Offset + e.RemovalLength; - if (o < 0 || o + 1 > textEditorData.Length || e.RemovalLength != 1 || textEditorData.Document.IsInUndo) { + if (o < 0 || o + 1 > Editor.Length || e.RemovalLength != 1/* || textEditorData.Document.IsInUndo*/) { return; } - if (textEditorData.GetCharAt (o) != '"') + if (Editor.GetCharAt (o) != '"') return; SafeUpdateIndentEngine (o + 1); wasInVerbatimString = stateTracker.IsInsideVerbatimString; @@ -279,7 +251,7 @@ namespace MonoDevelop.CSharp.Formatting return result.ToString (); } - static void ConvertNormalToVerbatimString (TextEditorData textEditorData, int offset) + static void ConvertNormalToVerbatimString (ITextDocument textEditorData, int offset) { var endOffset = offset; while (endOffset < textEditorData.Length) { @@ -301,10 +273,10 @@ namespace MonoDevelop.CSharp.Formatting return; var plainText = TextPasteUtils.StringLiteralPasteStrategy.Instance.Decode (textEditorData.GetTextAt (offset, endOffset - offset)); var newText = TextPasteUtils.VerbatimStringStrategy.Encode (plainText); - textEditorData.Replace (offset, endOffset - offset, newText); + textEditorData.ReplaceText (offset, endOffset - offset, newText); } - static void ConvertVerbatimStringToNormal (TextEditorData textEditorData, int offset) + static void ConvertVerbatimStringToNormal (ITextDocument textEditorData, int offset) { var endOffset = offset; while (endOffset < textEditorData.Length) { @@ -320,14 +292,12 @@ namespace MonoDevelop.CSharp.Formatting } var plainText = TextPasteUtils.VerbatimStringStrategy.Decode (textEditorData.GetTextAt (offset, endOffset - offset)); var newText = TextPasteUtils.StringLiteralPasteStrategy.Instance.Encode (plainText); - textEditorData.Replace (offset, endOffset - offset, newText); + textEditorData.ReplaceText (offset, endOffset - offset, newText); } - internal IStateMachineIndentEngine StateTracker { get { return stateTracker; } } - public bool DoInsertTemplate () { - string word = CodeTemplate.GetWordBeforeCaret (textEditorData); + string word = CodeTemplate.GetWordBeforeCaret (Editor); foreach (CodeTemplate template in CodeTemplateService.GetCodeTemplates (CSharpFormatter.MimeType)) { if (template.Shortcut == word) return true; @@ -340,8 +310,8 @@ namespace MonoDevelop.CSharp.Formatting void CheckXmlCommentCloseTag (char keyChar) { if (keyChar == '>' && stateTracker.IsInsideDocLineComment) { - var location = Editor.Caret.Location; - string lineText = Editor.GetLineText (Editor.Caret.Line); + var location = Editor.CaretLocation; + string lineText = Editor.GetLineText (Editor.CaretLine); int startIndex = Math.Min (location.Column - 2, lineText.Length - 1); while (startIndex >= 0 && lineText [startIndex] != '<') { --startIndex; @@ -357,8 +327,10 @@ namespace MonoDevelop.CSharp.Formatting endIndex++; } string tag = endIndex - startIndex > 0 ? lineText.Substring (startIndex + 1, endIndex - startIndex - 1) : null; - if (!string.IsNullOrEmpty (tag) && CSharpCompletionEngine.CommentTags.Any (t => t == tag)) { - Editor.Document.Insert (Editor.Caret.Offset, "</" + tag + ">", AnchorMovementType.BeforeInsertion); + if (!string.IsNullOrEmpty (tag) && ICSharpCode.NRefactory.CSharp.Completion.CSharpCompletionEngine.CommentTags.Any (t => t == tag)) { + var caretOffset = Editor.CaretOffset; + Editor.InsertText (caretOffset, "</" + tag + ">"); + Editor.CaretOffset = caretOffset; } } } @@ -366,99 +338,96 @@ namespace MonoDevelop.CSharp.Formatting internal void ReindentOnTab () { - int cursor = textEditorData.Caret.Offset; - if (stateTracker.IsInsideVerbatimString && cursor > 0 && cursor < textEditorData.Document.TextLength && textEditorData.GetCharAt (cursor - 1) == '"') + int cursor = Editor.CaretOffset; + if (stateTracker.IsInsideVerbatimString && cursor > 0 && cursor < Editor.Length && Editor.GetCharAt (cursor - 1) == '"') SafeUpdateIndentEngine (cursor + 1); if (stateTracker.IsInsideVerbatimString) { // insert normal tab inside @" ... " - if (textEditorData.IsSomethingSelected) { - textEditorData.SelectedText = "\t"; + if (Editor.IsSomethingSelected) { + Editor.SelectedText = "\t"; } else { - textEditorData.Insert (cursor, "\t"); + Editor.InsertText (cursor, "\t"); } - textEditorData.Document.CommitLineUpdate (textEditorData.Caret.Line); + // textEditorData.Document.CommitLineUpdate (textEditorData.CaretLine); } else if (cursor >= 1) { - if (textEditorData.Caret.Column > 1) { + if (Editor.CaretColumn > 1) { int delta = cursor - cursorPositionBeforeKeyPress; if (delta < 2 && delta > 0) { - textEditorData.Remove (cursor - delta, delta); - textEditorData.Caret.Offset = cursor - delta; - textEditorData.Document.CommitLineUpdate (textEditorData.Caret.Line); + Editor.RemoveText (cursor - delta, delta); + Editor.CaretOffset = cursor - delta; + // textEditorData.Document.CommitLineUpdate (textEditorData.CaretLine); } } - SafeUpdateIndentEngine (textEditorData.Caret.Offset); + SafeUpdateIndentEngine (Editor.CaretOffset); DoReSmartIndent (); } } - public override bool KeyPress (Gdk.Key key, char keyChar, Gdk.ModifierType modifier) + public override bool KeyPress (KeyDescriptor descriptor) { - bool skipFormatting = StateTracker.IsInsideOrdinaryCommentOrString || - StateTracker.IsInsidePreprocessorDirective; - - cursorPositionBeforeKeyPress = textEditorData.Caret.Offset; - bool isSomethingSelected = textEditorData.IsSomethingSelected; - if (key == Gdk.Key.BackSpace && textEditorData.Caret.Offset == lastInsertedSemicolon) { - textEditorData.Document.Undo (); + cursorPositionBeforeKeyPress = Editor.CaretOffset; + bool isSomethingSelected = Editor.IsSomethingSelected; + if (descriptor.SpecialKey == SpecialKey.BackSpace && Editor.CaretOffset == lastInsertedSemicolon) { + EditActions.Undo (Editor); lastInsertedSemicolon = -1; return false; } lastInsertedSemicolon = -1; - if (keyChar == ';' && !(textEditorData.CurrentMode is TextLinkEditMode) && !DoInsertTemplate () && !isSomethingSelected && PropertyService.Get ( + if (descriptor.KeyChar == ';' && Editor.EditMode == EditMode.Edit && !DoInsertTemplate () && !isSomethingSelected && PropertyService.Get ( "SmartSemicolonPlacement", false ) && !(stateTracker.IsInsideComment || stateTracker.IsInsideString)) { - bool retval = base.KeyPress (key, keyChar, modifier); - DocumentLine curLine = textEditorData.Document.GetLine (textEditorData.Caret.Line); - string text = textEditorData.Document.GetTextAt (curLine); + bool retval = base.KeyPress (descriptor); + var curLine = Editor.GetLine (Editor.CaretLine); + string text = Editor.GetTextAt (curLine); if (!(text.EndsWith (";", StringComparison.Ordinal) || text.Trim ().StartsWith ("for", StringComparison.Ordinal))) { int guessedOffset; - if (GuessSemicolonInsertionOffset (textEditorData, curLine, textEditorData.Caret.Offset, out guessedOffset)) { - using (var undo = textEditorData.OpenUndoGroup ()) { - textEditorData.Remove (textEditorData.Caret.Offset - 1, 1); - textEditorData.Caret.Offset = guessedOffset; - lastInsertedSemicolon = textEditorData.Caret.Offset + 1; - retval = base.KeyPress (key, keyChar, modifier); + if (GuessSemicolonInsertionOffset (Editor, curLine, Editor.CaretOffset, out guessedOffset)) { + using (var undo = Editor.OpenUndoGroup ()) { + Editor.RemoveText (Editor.CaretOffset - 1, 1); + Editor.CaretOffset = guessedOffset; + lastInsertedSemicolon = Editor.CaretOffset + 1; + retval = base.KeyPress (descriptor); } } } - using (var undo = textEditorData.OpenUndoGroup ()) { - if (OnTheFlyFormatting && textEditorData != null && !(textEditorData.CurrentMode is TextLinkEditMode) && !(textEditorData.CurrentMode is InsertionCursorEditMode)) { - OnTheFlyFormatter.FormatStatmentAt (Document, textEditorData.Caret.Location); + using (var undo = Editor.OpenUndoGroup ()) { + if (OnTheFlyFormatting && Editor != null && Editor.EditMode == EditMode.Edit) { + OnTheFlyFormatter.FormatStatmentAt (Editor, DocumentContext, Editor.CaretLocation); } } return retval; } - if (key == Gdk.Key.Tab) { - SafeUpdateIndentEngine (textEditorData.Caret.Offset); - if (stateTracker.IsInsideStringLiteral && !textEditorData.IsSomethingSelected) { - var lexer = new CSharpCompletionEngineBase.MiniLexer (textEditorData.Document.GetTextAt (0, textEditorData.Caret.Offset)); + if (descriptor.SpecialKey == SpecialKey.Tab) { + SafeUpdateIndentEngine (Editor.CaretOffset); + if (stateTracker.IsInsideStringLiteral && !Editor.IsSomethingSelected) { + var lexer = new ICSharpCode.NRefactory.CSharp.Completion.CSharpCompletionEngineBase.MiniLexer (Editor.GetTextAt (0, Editor.CaretOffset)); lexer.Parse (); if (lexer.IsInString) { - textEditorData.InsertAtCaret ("\\t"); + Editor.InsertAtCaret ("\\t"); return false; } } } - if (key == Gdk.Key.Tab && DefaultSourceEditorOptions.Instance.TabIsReindent && !CompletionWindowManager.IsVisible && !(textEditorData.CurrentMode is TextLinkEditMode) && !DoInsertTemplate () && !isSomethingSelected) { + if (descriptor.SpecialKey == SpecialKey.Tab && DefaultSourceEditorOptions.Instance.TabIsReindent && !CompletionWindowManager.IsVisible && Editor.EditMode == EditMode.Edit && !DoInsertTemplate () && !isSomethingSelected) { ReindentOnTab (); return false; } - SafeUpdateIndentEngine (textEditorData.Caret.Offset); + SafeUpdateIndentEngine (Editor.CaretOffset); if (!stateTracker.IsInsideOrdinaryCommentOrString) { - if (keyChar == '@') { - var retval = base.KeyPress (key, keyChar, modifier); - int cursor = textEditorData.Caret.Offset; - if (cursor < textEditorData.Length && textEditorData.GetCharAt (cursor) == '"') - ConvertNormalToVerbatimString (textEditorData, cursor + 1); + if (descriptor.KeyChar == '@') { + var retval = base.KeyPress (descriptor); + int cursor = Editor.CaretOffset; + if (cursor < Editor.Length && Editor.GetCharAt (cursor) == '"') + ConvertNormalToVerbatimString (Editor, cursor + 1); return retval; } } @@ -468,34 +437,34 @@ namespace MonoDevelop.CSharp.Formatting if (!indentationDisabled) { bool retval; //capture some of the current state - int oldBufLen = textEditorData.Length; - int oldLine = textEditorData.Caret.Line + 1; + int oldBufLen = Editor.Length; + int oldLine = Editor.CaretLine + 1; bool reIndent = false; //pass through to the base class, which actually inserts the character //and calls HandleCodeCompletion etc to handles completion - using (var undo = textEditorData.OpenUndoGroup ()) { - DoPreInsertionSmartIndent (key); + using (var undo = Editor.OpenUndoGroup ()) { + DoPreInsertionSmartIndent (descriptor.SpecialKey); } wasInStringLiteral = stateTracker.IsInsideStringLiteral; bool automaticReindent; // need to be outside of an undo group - otherwise it interferes with other text editor extension // esp. the documentation insertion undo steps. - retval = base.KeyPress (key, keyChar, modifier); + retval = base.KeyPress (descriptor); //handle inserted characters - if (textEditorData.Caret.Offset <= 0 || textEditorData.IsSomethingSelected) + if (Editor.CaretOffset <= 0 || Editor.IsSomethingSelected) return retval; - lastCharInserted = TranslateKeyCharForIndenter (key, keyChar, textEditorData.GetCharAt (textEditorData.Caret.Offset - 1)); + lastCharInserted = TranslateKeyCharForIndenter (descriptor.SpecialKey, descriptor.KeyChar, Editor.GetCharAt (Editor.CaretOffset - 1)); if (lastCharInserted == '\0') return retval; - using (var undo = textEditorData.OpenUndoGroup ()) { - SafeUpdateIndentEngine (textEditorData.Caret.Offset); + using (var undo = Editor.OpenUndoGroup ()) { + SafeUpdateIndentEngine (Editor.CaretOffset); - if (key == Gdk.Key.Return && modifier == Gdk.ModifierType.ControlMask) { - FixLineStart (textEditorData, stateTracker, textEditorData.Caret.Line + 1); + if (descriptor.SpecialKey == SpecialKey.Return && descriptor.ModifierKeys == ModifierKeys.Control) { + FixLineStart (Editor, stateTracker, Editor.CaretLine + 1); } else { - if (!(oldLine == textEditorData.Caret.Line + 1 && lastCharInserted == '\n') && (oldBufLen != textEditorData.Length || lastCharInserted != '\0')) { + if (!(oldLine == Editor.CaretLine + 1 && lastCharInserted == '\n') && (oldBufLen != Editor.Length || lastCharInserted != '\0')) { DoPostInsertionSmartIndent (lastCharInserted, out reIndent); } else { reIndent = lastCharInserted == '\n'; @@ -505,67 +474,121 @@ namespace MonoDevelop.CSharp.Formatting //N.B. if the engine says we need to reindent, make sure that it's because a char was //inserted rather than just updating the stack due to moving around - SafeUpdateIndentEngine (textEditorData.Caret.Offset); + SafeUpdateIndentEngine (Editor.CaretOffset); // Automatically reindent in text link mode will cause the mode exiting, therefore we need to prevent that. - automaticReindent = (stateTracker.NeedsReindent && lastCharInserted != '\0') && !(textEditorData.CurrentMode is TextLinkEditMode); - if (key == Gdk.Key.Return && (reIndent || automaticReindent)) { - if (textEditorData.Options.IndentStyle == IndentStyle.Virtual) { - if (textEditorData.GetLine (textEditorData.Caret.Line).Length == 0) - textEditorData.Caret.Column = textEditorData.IndentationTracker.GetVirtualIndentationColumn (textEditorData.Caret.Location); + automaticReindent = (stateTracker.NeedsReindent && lastCharInserted != '\0') && Editor.EditMode == EditMode.Edit; + if (descriptor.SpecialKey == SpecialKey.Return && (reIndent || automaticReindent)) { + if (Editor.Options.IndentStyle == IndentStyle.Virtual) { + if (Editor.GetLine (Editor.CaretLine).Length == 0) + Editor.CaretColumn = Editor.GetVirtualIndentationColumn (Editor.CaretLine); } else { DoReSmartIndent (); } } } + const string reindentChars = ";){}"; - if (reIndent || key != Gdk.Key.Return && key != Gdk.Key.Tab && automaticReindent && reindentChars.Contains (keyChar)) { - using (var undo = textEditorData.OpenUndoGroup ()) { + if (reIndent || descriptor.SpecialKey != SpecialKey.Return && descriptor.SpecialKey != SpecialKey.Tab && automaticReindent && reindentChars.Contains (descriptor.KeyChar)) { + using (var undo = Editor.OpenUndoGroup ()) { DoReSmartIndent (); } } - if (!skipFormatting && !(stateTracker.IsInsideComment || stateTracker.IsInsideString)) { - if (keyChar == ';' || keyChar == '}') { - using (var undo = textEditorData.OpenUndoGroup ()) { - if (OnTheFlyFormatting && textEditorData != null && !(textEditorData.CurrentMode is TextLinkEditMode) && !(textEditorData.CurrentMode is InsertionCursorEditMode)) { - OnTheFlyFormatter.FormatStatmentAt (Document, textEditorData.Caret.Location); - } - } - } - } - - SafeUpdateIndentEngine (textEditorData.Caret.Offset); + HandleOnTheFlyFormatting (descriptor); + SafeUpdateIndentEngine (Editor.CaretOffset); lastCharInserted = '\0'; - CheckXmlCommentCloseTag (keyChar); + CheckXmlCommentCloseTag (descriptor.KeyChar); return retval; } - if (textEditorData.Options.IndentStyle == IndentStyle.Auto && DefaultSourceEditorOptions.Instance.TabIsReindent && key == Gdk.Key.Tab) { - bool retval = base.KeyPress (key, keyChar, modifier); + if (Editor.Options.IndentStyle == IndentStyle.Auto && DefaultSourceEditorOptions.Instance.TabIsReindent && descriptor.SpecialKey == SpecialKey.Tab) { + bool retval = base.KeyPress (descriptor); DoReSmartIndent (); - CheckXmlCommentCloseTag (keyChar); + CheckXmlCommentCloseTag (descriptor.KeyChar); return retval; } //pass through to the base class, which actually inserts the character //and calls HandleCodeCompletion etc to handles completion - var result = base.KeyPress (key, keyChar, modifier); + var result = base.KeyPress (descriptor); - if (!indentationDisabled && (key == Gdk.Key.Return || key == Gdk.Key.KP_Enter)) { + if (!indentationDisabled && (descriptor.SpecialKey == SpecialKey.Return)) { DoReSmartIndent (); } - CheckXmlCommentCloseTag (keyChar); + CheckXmlCommentCloseTag (descriptor.KeyChar); - if (!skipFormatting && keyChar == '}') - RunFormatter (new DocumentLocation (textEditorData.Caret.Location.Line, textEditorData.Caret.Location.Column)); + HandleOnTheFlyFormatting (descriptor); + return result; } - static bool IsSemicolonalreadyPlaced (TextEditorData data, int caretOffset) + void HandleOnTheFlyFormatting (KeyDescriptor descriptor) + { + if (descriptor.KeyChar == '{') + return; + SafeUpdateIndentEngine (Editor.CaretOffset); + bool skipFormatting = stateTracker.IsInsideOrdinaryCommentOrString || stateTracker.IsInsidePreprocessorDirective; + if (!skipFormatting && !(stateTracker.IsInsideComment || stateTracker.IsInsideString)) { + if (DocumentContext.ParsedDocument == null || DocumentContext.ParsedDocument.GetAst<SemanticModel> () == null) + return; + var document = DocumentContext.AnalysisDocument; + if (document == null) + return; + if (!skipFormatting && service.SupportsFormattingOnTypedCharacter (document, descriptor.KeyChar)) { + var caretPosition = Editor.CaretOffset; + var token = CSharpEditorFormattingService.GetTokenBeforeTheCaretAsync (document, caretPosition, default(CancellationToken)).Result; + if (token.IsMissing || !service.ValidSingleOrMultiCharactersTokenKind (descriptor.KeyChar, token.Kind ()) || token.IsKind (SyntaxKind.EndOfFileToken, SyntaxKind.None)) + return; + if (CSharpEditorFormattingService.TokenShouldNotFormatOnTypeChar (token)) + return; + using (var undo = Editor.OpenUndoGroup ()) { + if (OnTheFlyFormatting && Editor != null && Editor.EditMode == EditMode.Edit) { + OnTheFlyFormatter.FormatStatmentAt (Editor, DocumentContext, Editor.CaretLocation); + } + } + } + } + if (OnTheFlyFormatting && descriptor.SpecialKey == SpecialKey.Return) { + try { + FormatOnReturn (); + } catch (Exception e) { + LoggingService.LogError ("Exception while formatting", e); + } + } + } + + async void FormatOnReturn (CancellationToken cancellationToken = default(CancellationToken)) + { + var document = DocumentContext.AnalysisDocument; + if (document == null) + return; + var caretPosition = Editor.CaretOffset; + var token = await CSharpEditorFormattingService.GetTokenBeforeTheCaretAsync(document, caretPosition, cancellationToken).ConfigureAwait(false); + if (token.IsMissing) + return; + + string text = null; + if (service.IsInvalidToken(token, ref text)) + return; + // Check to see if the token is ')' and also the parent is a using statement. If not, bail + if (CSharpEditorFormattingService.TokenShouldNotFormatOnReturn(token)) + return; + var tokenRange = FormattingRangeHelper.FindAppropriateRange(token); + if (tokenRange == null || !tokenRange.HasValue || tokenRange.Value.Item1.Equals(tokenRange.Value.Item2)) + return; + var value = tokenRange.Value; + using (var undo = Editor.OpenUndoGroup ()) { + OnTheFlyFormatter.Format (Editor, DocumentContext, value.Item1.SpanStart, value.Item2.Span.End); + } + } + + CSharpEditorFormattingService service = new CSharpEditorFormattingService (); + + static bool IsSemicolonalreadyPlaced (IReadonlyTextDocument data, int caretOffset) { for (int pos2 = caretOffset - 1; pos2-- > 0;) { - var ch2 = data.Document.GetCharAt (pos2); + var ch2 = data.GetCharAt (pos2); if (ch2 == ';') { return true; } @@ -575,7 +598,7 @@ namespace MonoDevelop.CSharp.Formatting return false; } - public static bool GuessSemicolonInsertionOffset (TextEditorData data, ISegment curLine, int caretOffset, out int outOffset) + public static bool GuessSemicolonInsertionOffset (IReadonlyTextDocument data, MonoDevelop.Core.Text.ISegment curLine, int caretOffset, out int outOffset) { int lastNonWsOffset = caretOffset; char lastNonWsChar = '\0'; @@ -598,7 +621,7 @@ namespace MonoDevelop.CSharp.Formatting var offset = curLine.Offset; string lineText = data.GetTextAt (caretOffset, max - caretOffset); - var lexer = new CSharpCompletionEngineBase.MiniLexer (lineText); + var lexer = new ICSharpCode.NRefactory.CSharp.Completion.CSharpCompletionEngineBase.MiniLexer (lineText); lexer.Parse ((ch, i) => { if (lexer.IsInSingleComment || lexer.IsInMultiLineComment) return true; @@ -619,13 +642,12 @@ namespace MonoDevelop.CSharp.Formatting return true; } - static char TranslateKeyCharForIndenter (Gdk.Key key, char keyChar, char docChar) + static char TranslateKeyCharForIndenter (SpecialKey key, char keyChar, char docChar) { switch (key) { - case Gdk.Key.Return: - case Gdk.Key.KP_Enter: + case SpecialKey.Return: return '\n'; - case Gdk.Key.Tab: + case SpecialKey.Tab: return '\t'; default: if (docChar == keyChar) @@ -637,14 +659,14 @@ namespace MonoDevelop.CSharp.Formatting // removes "\s*\+\s*" patterns (used for special behaviour inside strings) void HandleStringConcatinationDeletion (int start, int end) { - if (start < 0 || end >= textEditorData.Length || textEditorData.IsSomethingSelected) + if (start < 0 || end >= Editor.Length || Editor.IsSomethingSelected) return; - char ch = textEditorData.GetCharAt (start); + char ch = Editor.GetCharAt (start); if (ch == '"') { int sgn = Math.Sign (end - start); bool foundPlus = false; - for (int max = start + sgn; max != end && max >= 0 && max < textEditorData.Length; max += sgn) { - ch = textEditorData.GetCharAt (max); + for (int max = start + sgn; max != end && max >= 0 && max < Editor.Length; max += sgn) { + ch = Editor.GetCharAt (max); if (Char.IsWhiteSpace (ch)) continue; if (ch == '+') { @@ -655,11 +677,11 @@ namespace MonoDevelop.CSharp.Formatting if (!foundPlus) break; if (sgn < 0) { - textEditorData.Remove (max, start - max); - textEditorData.Caret.Offset = max + 1; + Editor.RemoveText (max, start - max); + Editor.CaretOffset = max + 1; } else { - textEditorData.Remove (start + sgn, max - start); - textEditorData.Caret.Offset = start; + Editor.RemoveText (start + sgn, max - start); + Editor.CaretOffset = start; } break; } else { @@ -669,23 +691,23 @@ namespace MonoDevelop.CSharp.Formatting } } - void DoPreInsertionSmartIndent (Gdk.Key key) + void DoPreInsertionSmartIndent (SpecialKey key) { switch (key) { - case Gdk.Key.BackSpace: - SafeUpdateIndentEngine (textEditorData.Caret.Offset); - HandleStringConcatinationDeletion (textEditorData.Caret.Offset - 1, 0); + case SpecialKey.BackSpace: + SafeUpdateIndentEngine (Editor.CaretOffset); + HandleStringConcatinationDeletion (Editor.CaretOffset - 1, 0); break; - case Gdk.Key.Delete: - SafeUpdateIndentEngine (textEditorData.Caret.Offset); - HandleStringConcatinationDeletion (textEditorData.Caret.Offset, textEditorData.Length); + case SpecialKey.Delete: + SafeUpdateIndentEngine (Editor.CaretOffset); + HandleStringConcatinationDeletion (Editor.CaretOffset, Editor.Length); break; } } //special handling for certain characters just inserted , for comments etc void DoPostInsertionSmartIndent (char charInserted, out bool reIndent) { - SafeUpdateIndentEngine (textEditorData.Caret.Offset); + SafeUpdateIndentEngine (Editor.CaretOffset); reIndent = false; switch (charInserted) { case '}': @@ -693,7 +715,7 @@ namespace MonoDevelop.CSharp.Formatting reIndent = true; break; case '\n': - if (FixLineStart (textEditorData, stateTracker, stateTracker.Location.Line)) + if (FixLineStart (Editor, stateTracker, Editor.OffsetToLineNumber (stateTracker.Offset))) return; //newline always reindents unless it's had special handling reIndent = true; @@ -703,17 +725,17 @@ namespace MonoDevelop.CSharp.Formatting internal bool wasInStringLiteral; - public bool FixLineStart (TextEditorData textEditorData, IStateMachineIndentEngine stateTracker, int lineNumber) + public bool FixLineStart (TextEditor textEditorData, ICSharpCode.NRefactory6.CSharp.IStateMachineIndentEngine stateTracker, int lineNumber) { - if (lineNumber > DocumentLocation.MinLine) { - DocumentLine line = textEditorData.Document.GetLine (lineNumber); + if (lineNumber > 1) { + var line = textEditorData.GetLine (lineNumber); if (line == null) return false; - DocumentLine prevLine = textEditorData.Document.GetLine (lineNumber - 1); + var prevLine = textEditorData.GetLine (lineNumber - 1); if (prevLine == null) return false; - string trimmedPreviousLine = textEditorData.Document.GetTextAt (prevLine).TrimStart (); + string trimmedPreviousLine = textEditorData.GetTextAt (prevLine).TrimStart (); //xml doc comments //check previous line was a doc comment @@ -723,13 +745,13 @@ namespace MonoDevelop.CSharp.Formatting return false; //check that the newline command actually inserted a newline textEditorData.EnsureCaretIsNotVirtual (); - var nextLineSegment = textEditorData.Document.GetLine (lineNumber + 1); - string nextLine = nextLineSegment != null ? textEditorData.Document.GetTextAt (nextLineSegment).TrimStart () : ""; + var nextLineSegment = textEditorData.GetLine (lineNumber + 1); + string nextLine = nextLineSegment != null ? textEditorData.GetTextAt (nextLineSegment).TrimStart () : ""; if (trimmedPreviousLine.Length > "///".Length || nextLine.StartsWith ("///", StringComparison.Ordinal)) { - var insertionPoint = textEditorData.Caret.Offset; - int inserted = textEditorData.Insert (insertionPoint, "/// "); - textEditorData.Caret.Offset = insertionPoint + inserted; + var insertionPoint = textEditorData.CaretOffset; + textEditorData.InsertText (insertionPoint, "/// "); + textEditorData.CaretOffset = insertionPoint + "/// ".Length; return true; } //multi-line comments @@ -746,22 +768,22 @@ namespace MonoDevelop.CSharp.Formatting commentPrefix = "*"; } - int indentSize = line.GetIndentation (textEditorData.Document).Length; - var insertedText = prevLine.GetIndentation (textEditorData.Document) + commentPrefix; - textEditorData.Replace (line.Offset, indentSize, insertedText); - textEditorData.Caret.Offset = line.Offset + insertedText.Length; + int indentSize = line.GetIndentation (textEditorData).Length; + var insertedText = prevLine.GetIndentation (textEditorData) + commentPrefix; + textEditorData.ReplaceText (line.Offset, indentSize, insertedText); + textEditorData.CaretOffset = line.Offset + insertedText.Length; return true; } else if (wasInStringLiteral) { - var lexer = new CSharpCompletionEngineBase.MiniLexer (textEditorData.Document.GetTextAt (0, prevLine.EndOffset).TrimEnd ()); + var lexer = new ICSharpCode.NRefactory.CSharp.Completion.CSharpCompletionEngineBase.MiniLexer (textEditorData.GetTextAt (0, prevLine.EndOffset).TrimEnd ()); lexer.Parse (); if (!lexer.IsInString) return false; textEditorData.EnsureCaretIsNotVirtual (); - textEditorData.Insert (prevLine.Offset + prevLine.Length, "\" +"); + textEditorData.InsertText (prevLine.Offset + prevLine.Length, "\" +"); - int indentSize = textEditorData.Caret.Offset - line.Offset; - var insertedText = prevLine.GetIndentation (textEditorData.Document) + (trimmedPreviousLine.StartsWith ("\"", StringComparison.Ordinal) ? "" : "\t") + "\""; - textEditorData.Replace (line.Offset, indentSize, insertedText); + int indentSize = textEditorData.CaretOffset - line.Offset; + var insertedText = prevLine.GetIndentation (textEditorData) + (trimmedPreviousLine.StartsWith ("\"", StringComparison.Ordinal) ? "" : "\t") + "\""; + textEditorData.ReplaceText (line.Offset, indentSize, insertedText); return true; } } @@ -770,7 +792,7 @@ namespace MonoDevelop.CSharp.Formatting //does re-indenting and cursor positioning void DoReSmartIndent () { - DoReSmartIndent (textEditorData.Caret.Offset); + DoReSmartIndent (Editor.CaretOffset); } void DoReSmartIndent (int cursor) @@ -779,26 +801,26 @@ namespace MonoDevelop.CSharp.Formatting if (stateTracker.LineBeganInsideVerbatimString || stateTracker.LineBeganInsideMultiLineComment) return; if (DefaultSourceEditorOptions.Instance.IndentStyle == IndentStyle.Auto) { - textEditorData.FixVirtualIndentation (); + Editor.FixVirtualIndentation (); return; } - var line = textEditorData.Document.GetLineByOffset (cursor); + var line = Editor.GetLineByOffset (cursor); // Get context to the end of the line w/o changing the main engine's state var curTracker = stateTracker.Clone (); try { for (int max = cursor; max < line.EndOffset; max++) { - curTracker.Push (textEditorData.Document.GetCharAt (max)); + curTracker.Push (Editor.GetCharAt (max)); } } catch (Exception e) { LoggingService.LogError ("Exception during indentation", e); } int pos = line.Offset; - string curIndent = line.GetIndentation (textEditorData.Document); + string curIndent = line.GetIndentation (Editor); int nlwsp = curIndent.Length; int offset = cursor > pos + nlwsp ? cursor - (pos + nlwsp) : 0; - if (!stateTracker.LineBeganInsideMultiLineComment || (nlwsp < line.LengthIncludingDelimiter && textEditorData.Document.GetCharAt (line.Offset + nlwsp) == '*')) { + if (!stateTracker.LineBeganInsideMultiLineComment || (nlwsp < line.LengthIncludingDelimiter && Editor.GetCharAt (line.Offset + nlwsp) == '*')) { // Possibly replace the indent string newIndent = curTracker.ThisLineIndent; int newIndentLength = newIndent.Length; @@ -807,9 +829,9 @@ namespace MonoDevelop.CSharp.Formatting if (pos < CompletionWindowManager.CodeCompletionContext.TriggerOffset) CompletionWindowManager.CodeCompletionContext.TriggerOffset -= nlwsp; } - - newIndentLength = textEditorData.Replace (pos, nlwsp, newIndent); - textEditorData.Document.CommitLineUpdate (textEditorData.Caret.Line); + newIndentLength = newIndent.Length; + Editor.ReplaceText (pos, nlwsp, newIndent); + //textEditorData.CommitLineUpdate (textEditorData.CaretLine); CompletionWindowManager.HideWindow (); } pos += newIndentLength; @@ -819,7 +841,7 @@ namespace MonoDevelop.CSharp.Formatting pos += offset; - textEditorData.FixVirtualIndentation (); + Editor.FixVirtualIndentation (); } /* |