diff options
Diffstat (limited to 'main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/CompletionTextEditorExtension.cs')
-rw-r--r-- | main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/CompletionTextEditorExtension.cs | 578 |
1 files changed, 578 insertions, 0 deletions
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/CompletionTextEditorExtension.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/CompletionTextEditorExtension.cs new file mode 100644 index 0000000000..2425d511de --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/CompletionTextEditorExtension.cs @@ -0,0 +1,578 @@ +// CompletionTextEditorExtension.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// + + +using System; +using MonoDevelop.Projects; +using MonoDevelop.Ide.CodeCompletion; +using MonoDevelop.Components.Commands; +using MonoDevelop.Ide.Commands; +using MonoDevelop.Core; +using MonoDevelop.Ide.CodeTemplates; +using MonoDevelop.Ide.Editor; +using System.Threading.Tasks; +using System.Threading; +using Gtk; + +namespace MonoDevelop.Ide.Editor.Extension +{ + public class CompletionTextEditorExtension : TextEditorExtension + { + protected CodeCompletionContext currentCompletionContext; + + bool autoHideCompletionWindow = true, autoHideParameterWindow = true;
+
+ #region Completion related IDE + // public readonly static PropertyWrapper<bool> EnableCodeCompletion = PropertyService.Wrap ("EnableCodeCompletion", true);
+ // public readonly static PropertyWrapper<bool> EnableParameterInsight = PropertyService.Wrap ("EnableParameterInsight", true);
+ public readonly static PropertyWrapper<bool> EnableAutoCodeCompletion = PropertyService.Wrap ("EnableAutoCodeCompletion", true); + public readonly static PropertyWrapper<bool> AddImportedItemsToCompletionList = PropertyService.Wrap ("AddImportedItemsToCompletionList", false); + public readonly static PropertyWrapper<bool> IncludeKeywordsInCompletionList = PropertyService.Wrap ("IncludeKeywordsInCompletionList", true); + public readonly static PropertyWrapper<bool> IncludeCodeSnippetsInCompletionList = PropertyService.Wrap ("IncludeCodeSnippetsInCompletionList", true); + public readonly static PropertyWrapper<bool> AddParenthesesAfterCompletion = PropertyService.Wrap ("AddParenthesesAfterCompletion", false); + public readonly static PropertyWrapper<bool> AddOpeningOnly = PropertyService.Wrap ("AddOpeningOnly", false); + public readonly static int CompletionListRows = 7; + + public readonly static PropertyWrapper<bool> FilterCompletionListByEditorBrowsable = PropertyService.Wrap ("FilterCompletionListByEditorBrowsable", true); + public readonly static PropertyWrapper<bool> IncludeEditorBrowsableAdvancedMembers = PropertyService.Wrap ("IncludeEditorBrowsableAdvancedMembers", true);
+
+ #endregion +
+ internal ICompletionWidget CompletionWidget + { + get; + set; + } + + + public virtual string CompletionLanguage + { + get + { + return "Other"; + } + } + + public void ShowCompletion (ICompletionDataList completionList) + { + currentCompletionContext = CompletionWidget.CreateCodeCompletionContext (Editor.CaretOffset); + int cpos, wlen; + if (!GetCompletionCommandOffset (out cpos, out wlen)) { + cpos = Editor.CaretOffset; + wlen = 0; + } + currentCompletionContext.TriggerOffset = cpos; + currentCompletionContext.TriggerWordLength = wlen; + + CompletionWindowManager.ShowWindow (this, '\0', completionList, CompletionWidget, currentCompletionContext); + } + CancellationTokenSource completionTokenSrc = new CancellationTokenSource (); + CancellationTokenSource parameterHintingSrc = new CancellationTokenSource ();
+ // When a key is pressed, and before the key is processed by the editor, this method will be invoked.
+ // Return true if the key press should be processed by the editor.
+ public override bool KeyPress (KeyDescriptor descriptor) + { + bool res; + if (currentCompletionContext != null) { + if (CompletionWindowManager.PreProcessKeyEvent (descriptor)) { + CompletionWindowManager.PostProcessKeyEvent (descriptor); + autoHideCompletionWindow = true;
+ // in named parameter case leave the parameter window open.
+ autoHideParameterWindow = descriptor.KeyChar != ':'; + if (!autoHideParameterWindow && ParameterInformationWindowManager.IsWindowVisible) + ParameterInformationWindowManager.PostProcessKeyEvent (this, CompletionWidget, descriptor); + + return false; + } + autoHideCompletionWindow = autoHideParameterWindow = false; + } + + if (ParameterInformationWindowManager.IsWindowVisible) { + if (ParameterInformationWindowManager.ProcessKeyEvent (this, CompletionWidget, descriptor)) + return false; + autoHideCompletionWindow = autoHideParameterWindow = false; + }
+
+ // int oldPos = Editor.CursorPosition;
+ // int oldLen = Editor.TextLength;
+ res = base.KeyPress (descriptor); + + CompletionWindowManager.PostProcessKeyEvent (descriptor); + + var ignoreMods = ModifierKeys.Control | ModifierKeys.Alt + | ModifierKeys.Command;
+ // Handle parameter completion
+ if (ParameterInformationWindowManager.IsWindowVisible) { + ParameterInformationWindowManager.PostProcessKeyEvent (this, CompletionWidget, descriptor); + } + + if ((descriptor.ModifierKeys & ignoreMods) != 0) + return res;
+
+ // don't complete on block selection
+ if (/*!EnableCodeCompletion ||*/ Editor.SelectionMode == MonoDevelop.Ide.Editor.SelectionMode.Block) + return res;
+
+ // Handle code completion + if (descriptor.KeyChar != '\0' && CompletionWidget != null && !CompletionWindowManager.IsVisible) { + currentCompletionContext = CompletionWidget.CurrentCodeCompletionContext; + completionTokenSrc.Cancel (); + completionTokenSrc = new CancellationTokenSource (); + var token = completionTokenSrc.Token; + var caretOffset = Editor.CaretOffset; + try { + var task = HandleCodeCompletionAsync (currentCompletionContext, descriptor.KeyChar, token); + if (task != null) { + var result = task.Result; + if (result != null) { + int triggerWordLength = result.TriggerWordLength; + + if (triggerWordLength > 0 && (triggerWordLength < caretOffset + || (triggerWordLength == 1 && caretOffset == 1))) { + currentCompletionContext = CompletionWidget.CreateCodeCompletionContext (caretOffset - triggerWordLength); + currentCompletionContext.TriggerWordLength = triggerWordLength; + } + + if (!CompletionWindowManager.ShowWindow (this, descriptor.KeyChar, result, CompletionWidget, currentCompletionContext)) + currentCompletionContext = null; + } else { + currentCompletionContext = null; + } + } else { + currentCompletionContext = null; + } + } catch (TaskCanceledException) { + currentCompletionContext = null; + } catch (AggregateException) { + currentCompletionContext = null; + } + } + + if (CompletionWidget != null) { + CodeCompletionContext ctx = CompletionWidget.CurrentCodeCompletionContext; + parameterHintingSrc.Cancel (); + parameterHintingSrc = new CancellationTokenSource (); + var token = parameterHintingSrc.Token; + try { + var task = HandleParameterCompletionAsync (ctx, descriptor.KeyChar, token); + if (task != null) { + var result = task.Result; + if (result != null) { + ParameterInformationWindowManager.ShowWindow (this, CompletionWidget, ctx, result); + } + } + } catch (TaskCanceledException) { + } catch (AggregateException) { + } + + }
+ /* autoHideCompletionWindow = true; + autoHideParameterWindow = keyChar != ':';*/
+ return res; + } + + protected void ShowCompletion (ICompletionDataList completionList, int triggerWordLength, char keyChar) + { + if (Editor.SelectionMode == SelectionMode.Block) + return; + if (CompletionWidget != null && currentCompletionContext == null) { + currentCompletionContext = CompletionWidget.CurrentCodeCompletionContext; + if (triggerWordLength > 0 && triggerWordLength < Editor.CaretOffset) { + currentCompletionContext = + CompletionWidget.CreateCodeCompletionContext (Editor.CaretOffset - triggerWordLength); + currentCompletionContext.TriggerWordLength = triggerWordLength; + } + if (completionList != null) + CompletionWindowManager.ShowWindow (this, keyChar, completionList, CompletionWidget, currentCompletionContext); + else + currentCompletionContext = null; + } + autoHideCompletionWindow = autoHideParameterWindow = true; + } + + public virtual int GetCurrentParameterIndex (int startOffset) + { + return -1; + } + + + protected void OnCompletionContextChanged (object o, EventArgs a) + { + if (autoHideCompletionWindow) + CompletionWindowManager.HideWindow (); + if (autoHideParameterWindow) + ParameterInformationWindowManager.HideWindow (this, CompletionWidget); + ParameterInformationWindowManager.UpdateCursorPosition (this, CompletionWidget); + } + + [CommandUpdateHandler(TextEditorCommands.ShowCompletionWindow)] + internal void OnUpdateCompletionCommand (CommandInfo info) + { + info.Bypass = !CanRunCompletionCommand () && !CompletionWindowManager.IsVisible; + } + + [CommandUpdateHandler(TextEditorCommands.ShowParameterCompletionWindow)] + internal void OnUpdateParameterCompletionCommand (CommandInfo info) + { + info.Bypass = !CanRunParameterCompletionCommand (); + } + + [CommandHandler(TextEditorCommands.ShowCompletionWindow)] + public virtual void RunCompletionCommand () + { + if (Editor.SelectionMode == SelectionMode.Block) + return; + + if (CompletionWindowManager.IsVisible) { + CompletionWindowManager.Wnd.ToggleCategoryMode (); + return; + } + ICompletionDataList completionList = null; + int cpos, wlen; + if (!GetCompletionCommandOffset (out cpos, out wlen)) { + cpos = Editor.CaretOffset; + wlen = 0; + } + currentCompletionContext = CompletionWidget.CreateCodeCompletionContext (cpos); + currentCompletionContext.TriggerWordLength = wlen; + completionList = CodeCompletionCommand (currentCompletionContext); + if (completionList != null) + CompletionWindowManager.ShowWindow (this, (char)0, completionList, CompletionWidget, currentCompletionContext); + else + currentCompletionContext = null; + } + + [CommandHandler(TextEditorCommands.ShowCodeTemplateWindow)] + public virtual void RunShowCodeTemplatesWindow () + { + ICompletionDataList completionList = null; + int cpos, wlen; + if (!GetCompletionCommandOffset (out cpos, out wlen)) { + cpos = Editor.CaretOffset; + wlen = 0; + } + + var ctx = CompletionWidget.CreateCodeCompletionContext (cpos); + ctx.TriggerWordLength = wlen; + completionList = Editor.IsSomethingSelected ? ShowCodeSurroundingsCommand (ctx) : ShowCodeTemplatesCommand (ctx); + if (completionList == null) { + return; + } + var wnd = new CompletionListWindow (Gtk.WindowType.Toplevel); + wnd.TypeHint = Gdk.WindowTypeHint.Dialog; + wnd.SkipPagerHint = true; + wnd.SkipTaskbarHint = true; + wnd.Decorated = false; + wnd.Extension = this; + wnd.ShowListWindow ((char)0, completionList, CompletionWidget, ctx); + } + + [CommandUpdateHandler(TextEditorCommands.ShowCodeTemplateWindow)] + internal void OnUpdateShowCodeTemplatesWindow (CommandInfo info) + { + ICompletionDataList completionList = null; + int cpos, wlen; + if (!GetCompletionCommandOffset (out cpos, out wlen)) { + cpos = Editor.CaretOffset; + wlen = 0; + } + try { + var ctx = CompletionWidget.CreateCodeCompletionContext (cpos); + ctx.TriggerWordLength = wlen; + completionList = Editor.IsSomethingSelected ? ShowCodeSurroundingsCommand (ctx) : ShowCodeTemplatesCommand (ctx); + + info.Bypass = completionList == null; + info.Text = Editor.IsSomethingSelected ? GettextCatalog.GetString ("_Surround With...") : GettextCatalog.GetString ("I_nsert Template..."); + } catch (Exception e) { + LoggingService.LogError ("Error while update show code templates window", e); + info.Bypass = true; + } + } + + + [CommandHandler(TextEditorCommands.ShowParameterCompletionWindow)] + public virtual void RunParameterCompletionCommand () + { + if (Editor.SelectionMode == SelectionMode.Block || CompletionWidget == null) + return; + ParameterHintingResult cp = null; + int cpos = Editor.CaretOffset; + CodeCompletionContext ctx = CompletionWidget.CreateCodeCompletionContext (cpos); + cp = ParameterCompletionCommand (ctx); + if (cp != null) { + ParameterInformationWindowManager.ShowWindow (this, CompletionWidget, ctx, cp); + ParameterInformationWindowManager.PostProcessKeyEvent (this, CompletionWidget, KeyDescriptor.FromGtk (Gdk.Key.F, 'f', Gdk.ModifierType.None)); + } + } + + public virtual bool CanRunCompletionCommand () + { + return (CompletionWidget != null && currentCompletionContext == null); + } + + public virtual bool CanRunParameterCompletionCommand () + { + return (CompletionWidget != null && !ParameterInformationWindowManager.IsWindowVisible); + } + + static readonly ICompletionDataList emptyList = new CompletionDataList (); + + public virtual Task<ICompletionDataList> HandleCodeCompletionAsync (CodeCompletionContext completionContext, char completionChar, CancellationToken token = default(CancellationToken)) + { + return Task.FromResult (emptyList); + } + + public virtual Task<ParameterHintingResult> HandleParameterCompletionAsync (CodeCompletionContext completionContext, char completionChar, CancellationToken token = default(CancellationToken)) + { + return Task.FromResult (ParameterHintingResult.Empty); + }
+
+ // return false if completion can't be shown
+ public virtual bool GetCompletionCommandOffset (out int cpos, out int wlen) + { + cpos = wlen = 0; + int pos = Editor.CaretOffset - 1; + while (pos >= 0) { + char c = Editor.GetCharAt (pos); + if (!char.IsLetterOrDigit (c) && c != '_') + break; + pos--; + } + if (pos == -1) + return false; + + pos++; + cpos = pos; + int len = Editor.Length; + + while (pos < len) { + char c = Editor.GetCharAt (pos); + if (!char.IsLetterOrDigit (c) && c != '_') + break; + pos++; + } + wlen = pos - cpos; + return true; + } + + + public virtual ICompletionDataList ShowCodeSurroundingsCommand (CodeCompletionContext completionContext) + { + CompletionDataList list = new CompletionDataList (); + list.AutoSelect = true; + list.AutoCompleteEmptyMatch = true; + list.CompletionSelectionMode = CompletionSelectionMode.OwnTextField; + var templateWidget = DocumentContext.GetContent<ICodeTemplateContextProvider> (); + CodeTemplateContext ctx = CodeTemplateContext.Standard; + if (templateWidget != null) + ctx = templateWidget.GetCodeTemplateContext (); + foreach (CodeTemplate template in CodeTemplateService.GetCodeTemplatesForFile (DocumentContext.Name)) { + if ((template.CodeTemplateType & CodeTemplateType.SurroundsWith) == CodeTemplateType.SurroundsWith) { + if (ctx == template.CodeTemplateContext) + list.Add (new CodeTemplateCompletionData (this, template)); + } + } + return list; + } + + public virtual ICompletionDataList ShowCodeTemplatesCommand (CodeCompletionContext completionContext) + { + CompletionDataList list = new CompletionDataList (); + list.AutoSelect = true; + list.AutoCompleteEmptyMatch = true; + list.CompletionSelectionMode = CompletionSelectionMode.OwnTextField; + foreach (CodeTemplate template in CodeTemplateService.GetCodeTemplatesForFile (DocumentContext.Name)) { + if (template.CodeTemplateType != CodeTemplateType.SurroundsWith) { + list.Add (new CodeTemplateCompletionData (this, template)); + } + } + return list; + } + const int CompletionTimeoutInMs = 500; + + public virtual ICompletionDataList CodeCompletionCommand (CodeCompletionContext completionContext) + { + // This default implementation of CodeCompletionCommand calls HandleCodeCompletion providing + // the char at the cursor position. If it returns a provider, just return it. + + int pos = completionContext.TriggerOffset; + if (pos > 0) { + char ch = Editor.GetCharAt (pos - 1); + var csc = new CancellationTokenSource (CompletionTimeoutInMs); + try { + var task = HandleCodeCompletionAsync (completionContext, ch, csc.Token); + if (task == null) + return null; + task.Wait (csc.Token); + if (!task.IsCompleted) + return null; + var completionList = task.Result; + if (completionList != null) + return completionList; + } catch (TaskCanceledException) { + } catch (AggregateException) { + } + } + return null; + } + + public virtual ParameterHintingResult ParameterCompletionCommand (CodeCompletionContext completionContext) + { + // This default implementation of ParameterCompletionCommand calls HandleParameterCompletion providing + // the char at the cursor position. If it returns a provider, just return it. + + int pos = completionContext.TriggerOffset; + if (pos <= 0) + return null; + var csc = new CancellationTokenSource (CompletionTimeoutInMs); + try { + var task = HandleParameterCompletionAsync (completionContext, Editor.GetCharAt (pos - 1), csc.Token); + if (task == null) + return null; + task.Wait (csc.Token); + if (!task.IsCompleted) + return null; + var cp = task.Result; + if (cp != null) + return cp; + } catch (TaskCanceledException) { + } catch (AggregateException) { + } + return null; + } + + public virtual int GuessBestMethodOverload (ParameterHintingResult provider, int currentOverload) + { + int cparam = GetCurrentParameterIndex (provider.StartOffset); + + var currentHintingData = provider [currentOverload]; + if (cparam > currentHintingData.ParameterCount && !currentHintingData.IsParameterListAllowed) { + // Look for an overload which has more parameters + int bestOverload = -1; + int bestParamCount = int.MaxValue; + for (int n=0; n<provider.Count; n++) { + int pc = provider[n].ParameterCount; + if (pc < bestParamCount && pc >= cparam) { + bestOverload = n; + bestParamCount = pc; + } + } + if (bestOverload == -1) { + for (int n=0; n<provider.Count; n++) { + if (provider[n].IsParameterListAllowed) { + bestOverload = n; + break; + } + } + } + return bestOverload; + } + return -1; + } + +// void HandlePaste (int insertionOffset, string text, int insertedChars) +// { +// ParameterInformationWindowManager.HideWindow (this, CompletionWidget); +// CompletionWindowManager.HideWindow (); +// } +// +// void HandleFocusOutEvent (object o, Gtk.FocusOutEventArgs args) +// { +// ParameterInformationWindowManager.HideWindow (this, CompletionWidget); +// CompletionWindowManager.HideWindow (); +// } + + protected override void Initialize () + { + base.Initialize (); + CompletionWindowManager.WindowClosed += HandleWindowClosed; + CompletionWidget = DocumentContext.GetContent <ICompletionWidget> (); + if (CompletionWidget != null) + CompletionWidget.CompletionContextChanged += OnCompletionContextChanged; + Editor.CaretPositionChanged += HandlePositionChanged; +// document.Editor.Paste += HandlePaste; +// if (document.Editor.Parent != null) +// document.Editor.Parent.TextArea.FocusOutEvent += HandleFocusOutEvent; + } + + internal void InternalInitialize () + { + Initialize (); + } + + void HandlePositionChanged (object sender, EventArgs e) + { + CompletionWindowManager.UpdateCursorPosition (); + } + + void HandleWindowClosed (object sender, EventArgs e) + { + currentCompletionContext = null; + } + + bool disposed = false; + public override void Dispose () + { + if (!disposed) { + CompletionWindowManager.HideWindow (); + ParameterInformationWindowManager.HideWindow (this, CompletionWidget); + + disposed = true; +// if (document.Editor.Parent != null) +// document.Editor.Parent.TextArea.FocusOutEvent -= HandleFocusOutEvent; +// document.Editor.Paste -= HandlePaste; + Editor.CaretPositionChanged -= HandlePositionChanged; + CompletionWindowManager.WindowClosed -= HandleWindowClosed; + if (CompletionWidget != null) + CompletionWidget.CompletionContextChanged -= OnCompletionContextChanged; + } + base.Dispose (); + } + } + + public interface ITypeNameResolver + { + string ResolveName (string typeName); + } + class SimpleTypeNameResolver: ITypeNameResolver + { + // This simple resolver removes the namespace from all class names. + // Used in ctrl+space, since all classes shown in the completion list + // are in scope + + public static SimpleTypeNameResolver Instance = new SimpleTypeNameResolver (); + + public string ResolveName (string typeName) + { + int i = typeName.LastIndexOf ('.'); + if (i == -1) + return typeName; + else + return typeName.Substring (i+1); + } + } +} |