Browse Source

fix #371: No code completion in Razor .cshtml files

pull/520/merge
Siegfried Pammer 11 years ago
parent
commit
466aa3a986
  1. 2
      src/AddIns/BackendBindings/AspNet.Mvc/Project/AspNet.Mvc.addin
  2. 5
      src/AddIns/BackendBindings/AspNet.Mvc/Project/AspNet.Mvc.csproj
  3. 143
      src/AddIns/BackendBindings/AspNet.Mvc/Project/Src/Completion/RazorCSharpCompletionBinding.cs
  4. 29
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpLanguageBinding.cs
  5. 9
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionBinding.cs
  6. 24
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionContext.cs
  7. 2
      src/AddIns/Debugger/Debugger.AddIn/Pads/AutoCompleteTextBox.cs
  8. 2
      src/AddIns/Debugger/Debugger.AddIn/Pads/ConsolePad.cs
  9. 8
      src/AddIns/Debugger/Debugger.AddIn/Pads/DebuggerDotCompletion.cs
  10. 2
      src/Main/Base/Project/Src/Services/LanguageBinding/DefaultLanguageBinding.cs
  11. 2
      src/Main/Base/Project/Src/Services/LanguageBinding/ILanguageBinding.cs

2
src/AddIns/BackendBindings/AspNet.Mvc/Project/AspNet.Mvc.addin

@ -130,7 +130,7 @@ @@ -130,7 +130,7 @@
</ComplexCondition>
</Path>
<Path name = "/AddIns/DefaultTextEditor/CodeCompletion">
<Path name = "/SharpDevelop/ViewContent/TextEditor/CodeCompletion">
<CodeCompletionBinding id = "RazorCSharp" extensions = ".cshtml" class = "ICSharpCode.AspNet.Mvc.Completion.RazorCSharpCompletionBinding"/>
</Path>

5
src/AddIns/BackendBindings/AspNet.Mvc/Project/AspNet.Mvc.csproj

@ -285,6 +285,11 @@ @@ -285,6 +285,11 @@
<Name>ICSharpCode.AvalonEdit</Name>
<Private>False</Private>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Libraries\NRefactory\ICSharpCode.NRefactory.CSharp\ICSharpCode.NRefactory.CSharp.csproj">
<Project>{53DCA265-3C3C-42F9-B647-F72BA678122B}</Project>
<Name>ICSharpCode.NRefactory.CSharp</Name>
<Private>False</Private>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Libraries\NRefactory\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj">
<Project>{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}</Project>
<Name>ICSharpCode.NRefactory</Name>

143
src/AddIns/BackendBindings/AspNet.Mvc/Project/Src/Completion/RazorCSharpCompletionBinding.cs

@ -16,27 +16,122 @@ @@ -16,27 +16,122 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//using System;
//using ICSharpCode.SharpDevelop.Editor;
//using ICSharpCode.SharpDevelop.Editor.CodeCompletion;
//
//namespace ICSharpCode.AspNet.Mvc.Completion
//{
// public class RazorCSharpCompletionBinding : DefaultCodeCompletionBinding
// {
// public RazorCSharpCompletionBinding()
// {
// }
//
// public override CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch)
// {
// if (ch == '.') {
// new RazorCSharpDotCompletionDataProvider().ShowCompletion(editor);
// return CodeCompletionKeyPressResult.Completed;
// } else if (ch == '(') {
// return base.HandleKeyPress(editor, ch);
// }
// return CodeCompletionKeyPressResult.None;
// }
// }
//}
using System;
using System.IO;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.CSharp.Resolver;
using ICSharpCode.NRefactory.CSharp.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Editor.CodeCompletion;
namespace ICSharpCode.AspNet.Mvc.Completion
{
public class RazorCSharpCompletionBinding : ICodeCompletionBinding
{
public bool HandleKeyPressed(ITextEditor editor, char ch)
{
if (ch == '.') {
var binding = CreateBinding(editor);
return binding.HandleKeyPressed(editor, ch);
}
return false;
}
public bool CtrlSpace(ITextEditor editor)
{
return false;
}
public CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch)
{
// We use HandleKeyPressed instead.
return CodeCompletionKeyPressResult.None;
}
ICodeCompletionBinding CreateBinding(ITextEditor editor)
{
return SD.LanguageService.GetLanguageByExtension(".cs")
.CreateCompletionBinding(FindExpressionToComplete(editor), CreateContext(editor));
}
string FindExpressionToComplete(ITextEditor editor)
{
int endOffset = editor.Caret.Offset;
int startOffset = endOffset;
while (startOffset > 0 && IsValidCharacter(editor.Document.GetCharAt(startOffset - 1)))
startOffset--;
return editor.Document.GetText(startOffset, endOffset - startOffset);
}
bool IsValidCharacter(char ch)
{
return Char.IsLetterOrDigit(ch) ||
(ch == '.') ||
(ch == '_');
}
ICodeContext CreateContext(ITextEditor editor)
{
var compilation = SD.ParserService.GetCompilationForFile(editor.FileName);
var project = SD.ProjectService.FindProjectContainingFile(editor.FileName);
var resolveContext = new SimpleTypeResolveContext(compilation.MainAssembly);
var currentTypeDefinition = new DefaultUnresolvedTypeDefinition(project.RootNamespace, Path.GetFileNameWithoutExtension(editor.FileName));
ITypeReference baseTypeReference = new GetClassTypeReference("System.Web.Mvc", "WebViewPage", 1);
baseTypeReference = new ParameterizedTypeReference(baseTypeReference, new[] { KnownTypeReference.Object });
currentTypeDefinition.BaseTypes.Add(baseTypeReference);
var currentMethod = new DefaultUnresolvedMethod(currentTypeDefinition, "__ContextStub__");
currentMethod.ReturnType = KnownTypeReference.Void;
currentTypeDefinition.Members.Add(currentMethod);
var currentResolvedTypeDef = new DefaultResolvedTypeDefinition(resolveContext, currentTypeDefinition);
var projectContent = compilation.MainAssembly.UnresolvedAssembly as IProjectContent;
var currentFile = new CSharpUnresolvedFile();
currentFile.RootUsingScope.AddSimpleUsing("System.Web.Mvc");
currentFile.RootUsingScope.AddSimpleUsing("System.Web.Mvc.Ajax");
currentFile.RootUsingScope.AddSimpleUsing("System.Web.Mvc.Html");
currentFile.RootUsingScope.AddSimpleUsing("System.Web.Routing");
currentFile.TopLevelTypeDefinitions.Add(currentTypeDefinition);
if (projectContent != null) {
compilation = projectContent.AddOrUpdateFiles(currentFile).CreateCompilation(SD.ParserService.GetCurrentSolutionSnapshot());
}
var context = new CSharpTypeResolveContext(compilation.MainAssembly,
currentFile.RootUsingScope.Resolve(compilation),
currentResolvedTypeDef,
currentMethod.CreateResolved(resolveContext.WithCurrentTypeDefinition(currentResolvedTypeDef)));
return new CSharpResolver(context);
}
}
static class NRUtils
{
/// <remarks>Does not support type arguments!</remarks>
public static void AddSimpleUsing(this UsingScope scope, string fullName)
{
if (scope == null)
throw new ArgumentNullException("scope");
string[] parts = fullName.Trim().Split('.');
TypeOrNamespaceReference reference = null;
foreach (var part in parts) {
if (reference != null) {
reference = new MemberTypeOrNamespaceReference(reference, part, EmptyList<ITypeReference>.Instance);
} else {
reference = new SimpleTypeOrNamespaceReference(part, EmptyList<ITypeReference>.Instance);
}
}
scope.Usings.AddIfNotNull(reference);
}
}
}

29
src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpLanguageBinding.cs

@ -55,35 +55,26 @@ namespace CSharpBinding @@ -55,35 +55,26 @@ namespace CSharpBinding
this.container.AddService(typeof(System.CodeDom.Compiler.CodeDomProvider), new Microsoft.CSharp.CSharpCodeProvider());
}
public override ICodeCompletionBinding CreateCompletionBinding(string expressionToComplete, FileName fileName, TextLocation location, ICodeContext context)
public override ICodeCompletionBinding CreateCompletionBinding(string expressionToComplete, ICodeContext context)
{
if (fileName == null)
throw new ArgumentNullException("fileName");
if (context == null)
throw new ArgumentNullException("context");
string content = GeneratePartialClassContextStub(fileName, location, context);
string content = GeneratePartialClassContextStub(context);
const string caretPoint = "$__Caret_Point__$;";
int caretOffset = content.IndexOf(caretPoint, StringComparison.Ordinal) + expressionToComplete.Length;
SD.Log.DebugFormatted("context used for dot completion: {0}", content.Replace(caretPoint, "$" + expressionToComplete + "|$"));
var doc = new ReadOnlyDocument(content.Replace(caretPoint, expressionToComplete));
return new CSharpCompletionBinding(fileName, doc.GetLocation(caretOffset), doc.CreateSnapshot());
return new CSharpCompletionBinding(context, doc.GetLocation(caretOffset), doc.CreateSnapshot());
}
static string GeneratePartialClassContextStub(FileName fileName, TextLocation location, ICodeContext context)
static string GeneratePartialClassContextStub(ICodeContext context)
{
var compilation = SD.ParserService.GetCompilationForFile(fileName);
var file = SD.ParserService.GetExistingUnresolvedFile(fileName);
if (compilation == null || file == null)
return "";
var unresolvedMember = file.GetMember(location);
if (unresolvedMember == null)
return "";
var member = unresolvedMember.Resolve(new SimpleTypeResolveContext(compilation.MainAssembly));
var member = context.CurrentMember;
if (member == null)
return "";
var builder = new TypeSystemAstBuilder();
MethodDeclaration decl;
if (unresolvedMember is IMethod) {
if (member is IMethod) {
// If it's a method, convert it directly (including parameters + type parameters)
decl = (MethodDeclaration)builder.ConvertEntity(member);
} else {
@ -97,11 +88,11 @@ namespace CSharpBinding @@ -97,11 +88,11 @@ namespace CSharpBinding
}
decl.Name = "__DebuggerStub__";
decl.ReturnType = builder.ConvertType(member.ReturnType);
decl.Modifiers = unresolvedMember.IsStatic ? Modifiers.Static : Modifiers.None;
decl.Modifiers = member.IsStatic ? Modifiers.Static : Modifiers.None;
// Make the method look like an explicit interface implementation so that it doesn't appear in CC
decl.PrivateImplementationType = new SimpleType("__DummyType__");
decl.Body = GenerateBodyFromContext(builder, context.LocalVariables.ToArray());
return WrapInType(unresolvedMember.DeclaringTypeDefinition, decl).ToString();
return WrapInType(context.CurrentTypeDefinition, decl).ToString();
}
static BlockStatement GenerateBodyFromContext(TypeSystemAstBuilder builder, IVariable[] variables)
@ -113,7 +104,7 @@ namespace CSharpBinding @@ -113,7 +104,7 @@ namespace CSharpBinding
return body;
}
static AstNode WrapInType(IUnresolvedTypeDefinition entity, EntityDeclaration decl)
static AstNode WrapInType(ITypeDefinition entity, EntityDeclaration decl)
{
if (entity == null)
return decl;
@ -137,7 +128,7 @@ namespace CSharpBinding @@ -137,7 +128,7 @@ namespace CSharpBinding
};
}
static ClassType GetClassType(IUnresolvedTypeDefinition entity)
static ClassType GetClassType(ITypeDefinition entity)
{
switch (entity.Kind) {
case TypeKind.Interface:

9
src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionBinding.cs

@ -26,6 +26,7 @@ using ICSharpCode.NRefactory.CSharp.Resolver; @@ -26,6 +26,7 @@ using ICSharpCode.NRefactory.CSharp.Resolver;
using ICSharpCode.NRefactory.Completion;
using ICSharpCode.NRefactory.CSharp.Completion;
using ICSharpCode.NRefactory.Editor;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Editor.CodeCompletion;
@ -35,7 +36,7 @@ namespace CSharpBinding.Completion @@ -35,7 +36,7 @@ namespace CSharpBinding.Completion
{
public class CSharpCompletionBinding : ICodeCompletionBinding
{
FileName contextFileName;
ICodeContext context;
TextLocation currentLocation;
ITextSource fileContent;
@ -44,9 +45,9 @@ namespace CSharpBinding.Completion @@ -44,9 +45,9 @@ namespace CSharpBinding.Completion
{
}
public CSharpCompletionBinding(FileName contextFileName, TextLocation currentLocation, ITextSource fileContent)
public CSharpCompletionBinding(ICodeContext context, TextLocation currentLocation, ITextSource fileContent)
{
this.contextFileName = contextFileName;
this.context = context;
this.currentLocation = currentLocation;
this.fileContent = fileContent;
}
@ -75,7 +76,7 @@ namespace CSharpBinding.Completion @@ -75,7 +76,7 @@ namespace CSharpBinding.Completion
if (fileContent == null) {
completionContext = CSharpCompletionContext.Get(editor);
} else {
completionContext = CSharpCompletionContext.Get(editor, fileContent, currentLocation, contextFileName);
completionContext = CSharpCompletionContext.Get(editor, context, currentLocation, fileContent);
}
if (completionContext == null)
return false;

24
src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionContext.cs

@ -61,38 +61,24 @@ namespace CSharpBinding.Completion @@ -61,38 +61,24 @@ namespace CSharpBinding.Completion
return new CSharpCompletionContext(editor, parseInfo.SyntaxTree.ConditionalSymbols, compilation, projectContent, editor.Document, parseInfo.UnresolvedFile, editor.Caret.Location);
}
public static CSharpCompletionContext Get(ITextEditor editor, ITextSource fileContent, TextLocation currentLocation, FileName fileName)
public static CSharpCompletionContext Get(ITextEditor editor, ICodeContext context, TextLocation currentLocation, ITextSource fileContent)
{
IDocument document = new ReadOnlyDocument(fileContent);
// Don't require the very latest parse information, an older cached version is OK.
var parseInfo = SD.ParserService.GetCachedParseInformation(fileName) as CSharpFullParseInformation;
if (parseInfo == null) {
parseInfo = SD.ParserService.Parse(fileName) as CSharpFullParseInformation;
}
if (parseInfo == null)
return null;
var project = SD.ProjectService.FindProjectContainingFile(fileName)as CSharpProject;
if (project == null)
return null;
var solutionSnapshot = SD.ParserService.GetCurrentSolutionSnapshot();
var projectContent = solutionSnapshot.GetProjectContent(project);
var projectContent = context.Compilation.MainAssembly.UnresolvedAssembly as IProjectContent;
if (projectContent == null)
return null;
CSharpParser parser = new CSharpParser(project.CompilerSettings);
CSharpParser parser = new CSharpParser();
parser.GenerateTypeSystemMode = false;
SyntaxTree cu = parser.Parse(fileContent, Path.GetRandomFileName() + ".cs");
cu.Freeze();
CSharpUnresolvedFile unresolvedFile = cu.ToTypeSystem();
ICompilation compilation = projectContent.AddOrUpdateFiles(unresolvedFile).CreateCompilation(solutionSnapshot);
ICompilation compilation = projectContent.AddOrUpdateFiles(unresolvedFile).CreateCompilation(SD.ParserService.GetCurrentSolutionSnapshot());
return new CSharpCompletionContext(editor, parseInfo.SyntaxTree.ConditionalSymbols, compilation, projectContent, document, unresolvedFile, currentLocation);
return new CSharpCompletionContext(editor, EmptyList<string>.Instance, compilation, projectContent, document, unresolvedFile, currentLocation);
}
private CSharpCompletionContext(ITextEditor editor, IList<string> conditionalSymbols, ICompilation compilation, IProjectContent projectContent, IDocument document, CSharpUnresolvedFile unresolvedFile, TextLocation caretLocation)

2
src/AddIns/Debugger/Debugger.AddIn/Pads/AutoCompleteTextBox.cs

@ -134,7 +134,7 @@ namespace Debugger.AddIn.Pads.Controls @@ -134,7 +134,7 @@ namespace Debugger.AddIn.Pads.Controls
ContextTextLocation = new TextLocation(frame.NextStatement.StartLine, frame.NextStatement.StartColumn);
}
if (ContextFileName == null) return;
var binding = DebuggerDotCompletion.PrepareDotCompletion(editor.Text.Substring(0, editor.CaretOffset), ContextFileName, ContextTextLocation, SD.ParserService.ResolveContext(ContextFileName, ContextTextLocation));
var binding = DebuggerDotCompletion.PrepareDotCompletion(editor.Text.Substring(0, editor.CaretOffset), SD.ParserService.ResolveContext(ContextFileName, ContextTextLocation));
if (binding == null) return;
binding.HandleKeyPressed(editorAdapter, '.');
} else {

2
src/AddIns/Debugger/Debugger.AddIn/Pads/ConsolePad.cs

@ -88,7 +88,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads @@ -88,7 +88,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
{
var fileName = new ICSharpCode.Core.FileName(frame.NextStatement.Filename);
var textLocation = new TextLocation(frame.NextStatement.StartLine, frame.NextStatement.StartColumn);
var binding = DebuggerDotCompletion.PrepareDotCompletion(currentText, fileName, textLocation, SD.ParserService.ResolveContext(fileName, textLocation));
var binding = DebuggerDotCompletion.PrepareDotCompletion(currentText, SD.ParserService.ResolveContext(fileName, textLocation));
if (binding == null) return;
binding.HandleKeyPressed(console.TextEditor, '.');
}

8
src/AddIns/Debugger/Debugger.AddIn/Pads/DebuggerDotCompletion.cs

@ -41,12 +41,10 @@ namespace Debugger.AddIn.Pads.Controls @@ -41,12 +41,10 @@ namespace Debugger.AddIn.Pads.Controls
return !errors.Any();
}
public static ICodeCompletionBinding PrepareDotCompletion(string expressionToComplete, FileName fileName, TextLocation location, ICodeContext context)
public static ICodeCompletionBinding PrepareDotCompletion(string expressionToComplete, ICodeContext context)
{
var lang = SD.LanguageService.GetLanguageByFileName(fileName);
if (lang == null)
return null;
return lang.CreateCompletionBinding(expressionToComplete, fileName, location, context);
return SD.LanguageService.GetLanguageByExtension(".cs")
.CreateCompletionBinding(expressionToComplete, context);
}
}
}

2
src/Main/Base/Project/Src/Services/LanguageBinding/DefaultLanguageBinding.cs

@ -75,7 +75,7 @@ namespace ICSharpCode.SharpDevelop @@ -75,7 +75,7 @@ namespace ICSharpCode.SharpDevelop
}
}
public virtual ICodeCompletionBinding CreateCompletionBinding(string expressionToComplete, FileName fileName, TextLocation location, ICodeContext context)
public virtual ICodeCompletionBinding CreateCompletionBinding(string expressionToComplete, ICodeContext context)
{
throw new NotSupportedException();
}

2
src/Main/Base/Project/Src/Services/LanguageBinding/ILanguageBinding.cs

@ -65,6 +65,6 @@ namespace ICSharpCode.SharpDevelop @@ -65,6 +65,6 @@ namespace ICSharpCode.SharpDevelop
/// <summary>
/// Creates a completion binding for a given expression and context.
/// </summary>
ICodeCompletionBinding CreateCompletionBinding(string expressionToComplete, FileName fileName, TextLocation location, ICodeContext context);
ICodeCompletionBinding CreateCompletionBinding(string expressionToComplete, ICodeContext context);
}
}

Loading…
Cancel
Save