// // 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 ICSharpCode.NRefactory; using ICSharpCode.NRefactory.TypeSystem; using MonoDevelop.Core; using MonoDevelop.DesignerSupport; using MonoDevelop.Ide.CodeCompletion; using MonoDevelop.Ide.Gui.Content; using MonoDevelop.Ide.TypeSystem; 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; namespace MonoDevelop.AspNet.WebForms { public class WebFormsEditorExtension : BaseHtmlEditorExtension { static readonly Regex DocTypeRegex = new Regex (@"(?:PUBLIC|public)\s+""(?[^""]*)""\s+""(?[^""]*)"""); WebFormsParsedDocument aspDoc; DotNetProject project; WebFormsTypeContext refman = new WebFormsTypeContext (); ILanguageCompletionBuilder documentBuilder; LocalDocumentInfo localDocumentInfo; DocumentInfo documentInfo; ICompletionWidget defaultCompletionWidget; MonoDevelop.Ide.Gui.Document defaultDocument; 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 = Document.Project as DotNetProject; if (newProj != null) { project = newProj; refman.Project = newProj; } documentBuilder = HasDoc ? LanguageCompletionBuilderService.GetBuilder (aspDoc.Info.Language) : null; if (documentBuilder != null) { documentInfo = new DocumentInfo (refman.Compilation, aspDoc, refman.GetUsings ()); documentInfo.ParsedDocument = documentBuilder.BuildDocument (documentInfo, Editor); documentInfo.CodeBesideClass = CreateCodeBesideClass (documentInfo, refman); } } static IUnresolvedTypeDefinition CreateCodeBesideClass (DocumentInfo info, WebFormsTypeContext refman) { 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; } protected override ICompletionDataList HandleCodeCompletion (CodeCompletionContext completionContext, bool forced, ref int triggerWordLength) { ITextBuffer buf = Buffer; // completionChar may be a space even if the current char isn't, when ctrl-space is fired t char currentChar = completionContext.TriggerOffset < 1? ' ' : buf.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 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)) { triggerWordLength = 1; return 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 list; } if (!HasDoc || aspDoc.Info.DocType == null) { //FIXME: get doctype from master page DocType = null; } else { DocType = new XDocType (TextLocation.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, ref triggerWordLength); } //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 = Document.Editor.Caret.Offset; int start = caretOffset - Tracker.Engine.CurrentStateLength; if (Document.Editor.GetCharAt (start) == '=') start++; string sourceText = Document.Editor.GetTextBetween (start, caretOffset); if (ch != '\0') sourceText += ch; string textAfterCaret = Document.Editor.GetTextBetween (caretOffset, Math.Min (Document.Editor.Length, Math.Max (caretOffset, Tracker.Engine.Position + Tracker.Engine.CurrentStateLength - 2))); if (documentBuilder == null){ localDocumentInfo = null; return; } localDocumentInfo = documentBuilder.BuildLocalDocument (documentInfo, Editor, sourceText, textAfterCaret, true); var viewContent = new MonoDevelop.Ide.Gui.HiddenTextEditorViewContent (); viewContent.Project = Document.Project; viewContent.ContentName = localDocumentInfo.ParsedLocalDocument.FileName; viewContent.Text = localDocumentInfo.LocalDocument; viewContent.GetTextEditorData ().Caret.Offset = localDocumentInfo.CaretPosition; var workbenchWindow = new MonoDevelop.Ide.Gui.HiddenWorkbenchWindow (); workbenchWindow.ViewContent = viewContent; localDocumentInfo.HiddenDocument = new HiddenDocument (workbenchWindow) { HiddenParsedDocument = localDocumentInfo.ParsedLocalDocument }; } public override ICompletionDataList CodeCompletionCommand (CodeCompletionContext completionContext) { //completion for ASP.NET expressions // TODO: Detect