// // AspNetEditorExtension.cs // // Author: // Michael Hutchinson // // Copyright (C) 2008 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 System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using MonoDevelop.Core; using MonoDevelop.DesignerSupport; using MonoDevelop.Ide.CodeCompletion; using MonoDevelop.Ide.Gui.Content; using MonoDevelop.Ide.TypeSystem; using MonoDevelop.Ide.Editor; using MonoDevelop.AspNet.Html; using MonoDevelop.AspNet.Html.Parser; using MonoDevelop.AspNet.Projects; using MonoDevelop.AspNet.WebForms.Parser; using S = MonoDevelop.Xml.Parser; using MonoDevelop.AspNet.WebForms.Dom; using MonoDevelop.Xml.Parser; using MonoDevelop.Xml.Dom; using MonoDevelop.Projects; using Microsoft.CodeAnalysis; using MonoDevelop.Ide.Editor.Extension; using MonoDevelop.Ide.Editor.Projection; namespace MonoDevelop.AspNet.WebForms { class WebFormsEditorExtension : BaseHtmlEditorExtension { static readonly System.Text.RegularExpressions.Regex DocTypeRegex = new System.Text.RegularExpressions.Regex (@"(?:PUBLIC|public)\s+""(?[^""]*)""\s+""(?[^""]*)"""); WebFormsParsedDocument aspDoc; DotNetProject project; WebFormsTypeContext refman = new WebFormsTypeContext (); ILanguageCompletionBuilder documentBuilder; Projection localDocumentProjection; DocumentInfo documentInfo; ICompletionWidget defaultCompletionWidget; DocumentContext defaultDocumentContext; TextEditor defaultEditor; TextEditor projectedEditor; bool HasDoc { get { return aspDoc != null; } } public WebFormsTypeContext ReferenceManager { get { return refman; } } #region Setup and teardown protected override XmlRootState CreateRootState () { return new WebFormsRootState (); } #endregion protected override void OnParsedDocumentUpdated () { base.OnParsedDocumentUpdated (); aspDoc = CU as WebFormsParsedDocument; if (HasDoc) refman.Doc = aspDoc; var newProj = base.DocumentContext.Project as DotNetProject; if (newProj != null) { project = newProj; refman.Project = newProj; } if (HasDoc) { documentInfo = new DocumentInfo (aspDoc, refman.GetUsings ()); // localDocumentProjection = new MonoDevelop.AspNet.WebForms.CSharp.CSharpProjector ().CreateProjection (documentInfo, Editor, true).Result; // projectedEditor = localDocumentProjection.CreateProjectedEditor (DocumentContext); // Editor.SetOrUpdateProjections (DocumentContext, new [] { localDocumentProjection }); } } static INamedTypeSymbol CreateCodeBesideClass (DocumentInfo info, WebFormsTypeContext refman) { //TODO: Roslyn port // var memberList = new WebFormsMemberListBuilder (refman, info.AspNetDocument.XDocument); // memberList.Build (); // var t = new ICSharpCode.NRefactory.TypeSystem.Implementation.DefaultUnresolvedTypeDefinition (info.ClassName); // var dom = refman.Compilation; // var baseType = ReflectionHelper.ParseReflectionName (info.BaseType).Resolve (dom); // foreach (var m in WebFormsCodeBehind.GetDesignerMembers (memberList.Members.Values, baseType, null)) { // t.Members.Add (new ICSharpCode.NRefactory.TypeSystem.Implementation.DefaultUnresolvedField (t, m.Name) { // Accessibility = Accessibility.Protected, // ReturnType = m.Type.ToTypeReference () // }); // } // return t; return null; } protected override Task HandleCodeCompletion ( CodeCompletionContext completionContext, bool forced, CancellationToken token) { // completionChar may be a space even if the current char isn't, when ctrl-space is fired t char currentChar = completionContext.TriggerOffset < 1? ' ' : Editor.GetCharAt (completionContext.TriggerOffset - 1); //char previousChar = completionContext.TriggerOffset < 2? ' ' : buf.GetCharAt (completionContext.TriggerOffset - 2); //directive names if (Tracker.Engine.CurrentState is WebFormsDirectiveState) { var directive = Tracker.Engine.Nodes.Peek () as WebFormsDirective; if (HasDoc && directive != null && directive.Region.BeginLine == completionContext.TriggerLine && directive.Region.BeginColumn + 3 == completionContext.TriggerLineOffset) { return Task.FromResult ((ICompletionDataList)WebFormsDirectiveCompletion.GetDirectives (aspDoc.Type)); } return null; } if (Tracker.Engine.CurrentState is XmlNameState && Tracker.Engine.CurrentState.Parent is WebFormsDirectiveState) { var directive = Tracker.Engine.Nodes.Peek () as WebFormsDirective; if (HasDoc && directive != null && directive.Region.BeginLine == completionContext.TriggerLine && directive.Region.BeginColumn + 4 == completionContext.TriggerLineOffset && char.IsLetter (currentChar)) { completionContext.TriggerWordLength = 1; completionContext.TriggerOffset -= 1; return Task.FromResult ((ICompletionDataList)WebFormsDirectiveCompletion.GetDirectives (aspDoc.Type)); } return null; } bool isAspExprState = Tracker.Engine.CurrentState is WebFormsExpressionState; //non-xml tag completion if (currentChar == '<' && !(isAspExprState || Tracker.Engine.CurrentState is XmlRootState)) { var list = new CompletionDataList (); AddAspBeginExpressions (list); return Task.FromResult ((ICompletionDataList)list); } if (!HasDoc || aspDoc.Info.DocType == null) { //FIXME: get doctype from master page DocType = null; } else { DocType = new XDocType (DocumentLocation.Empty); var matches = DocTypeRegex.Match (aspDoc.Info.DocType); DocType.PublicFpi = matches.Groups["fpi"].Value; DocType.Uri = matches.Groups["uri"].Value; } if (Tracker.Engine.CurrentState is HtmlScriptBodyState) { var el = Tracker.Engine.Nodes.Peek () as XElement; if (el != null) { if (el.IsRunatServer ()) { if (documentBuilder != null) { // TODO: C# completion } } /* else { att = GetHtmlAtt (el, "language"); if (att == null || "javascript".Equals (att.Value, StringComparison.OrdinalIgnoreCase)) { att = GetHtmlAtt (el, "type"); if (att == null || "text/javascript".Equals (att.Value, StringComparison.OrdinalIgnoreCase)) { // TODO: JS completion } } }*/ } } return base.HandleCodeCompletion (completionContext, forced, token); } //case insensitive, no prefix static XAttribute GetHtmlAtt (XElement el, string name) { return el.Attributes.FirstOrDefault (a => a.IsNamed && !a.Name.HasPrefix && a.Name.Name.Equals (name, StringComparison.OrdinalIgnoreCase)); } public void InitializeCodeCompletion (char ch) { int caretOffset = Editor.CaretOffset; int start = caretOffset - Tracker.Engine.CurrentStateLength; if (Editor.GetCharAt (start) == '=') start++; string sourceText = Editor.GetTextBetween (start, caretOffset); if (ch != '\0') sourceText += ch; string textAfterCaret = Editor.GetTextBetween (caretOffset, Math.Min (Editor.Length, Math.Max (caretOffset, Tracker.Engine.Position + Tracker.Engine.CurrentStateLength - 2))); if (documentBuilder == null) { localDocumentProjection = null; return; } // var viewContent = new MonoDevelop.Ide.Gui.HiddenTextEditorViewContent (); // viewContent.Project = DocumentContext.Project; // viewContent.ContentName = localDocumentInfo.ParsedLocalDocument.FileName; // // viewContent.Text = localDocumentInfo.LocalDocument; // viewContent.Editor.CaretOffset = localDocumentInfo.CaretPosition; // // var workbenchWindow = new MonoDevelop.Ide.Gui.HiddenWorkbenchWindow (); // workbenchWindow.ViewContent = viewContent; // localDocumentInfo.HiddenDocument = new HiddenDocument (workbenchWindow) { // HiddenParsedDocument = localDocumentInfo.ParsedLocalDocument // }; } /* public override Task CodeCompletionCommand (CodeCompletionContext completionContext) { //completion for ASP.NET expressions // TODO: Detect