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

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

@ -285,6 +285,11 @@
<Name>ICSharpCode.AvalonEdit</Name> <Name>ICSharpCode.AvalonEdit</Name>
<Private>False</Private> <Private>False</Private>
</ProjectReference> </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"> <ProjectReference Include="..\..\..\..\Libraries\NRefactory\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj">
<Project>{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}</Project> <Project>{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}</Project>
<Name>ICSharpCode.NRefactory</Name> <Name>ICSharpCode.NRefactory</Name>

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

@ -16,27 +16,122 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
//using System; using System;
//using ICSharpCode.SharpDevelop.Editor; using System.IO;
//using ICSharpCode.SharpDevelop.Editor.CodeCompletion; using ICSharpCode.NRefactory;
// using ICSharpCode.NRefactory.CSharp.Resolver;
//namespace ICSharpCode.AspNet.Mvc.Completion using ICSharpCode.NRefactory.CSharp.TypeSystem;
//{ using ICSharpCode.NRefactory.TypeSystem;
// public class RazorCSharpCompletionBinding : DefaultCodeCompletionBinding using ICSharpCode.NRefactory.TypeSystem.Implementation;
// { using ICSharpCode.SharpDevelop;
// public RazorCSharpCompletionBinding() using ICSharpCode.SharpDevelop.Editor;
// { using ICSharpCode.SharpDevelop.Editor.CodeCompletion;
// }
// namespace ICSharpCode.AspNet.Mvc.Completion
// public override CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch) {
// { public class RazorCSharpCompletionBinding : ICodeCompletionBinding
// if (ch == '.') { {
// new RazorCSharpDotCompletionDataProvider().ShowCompletion(editor); public bool HandleKeyPressed(ITextEditor editor, char ch)
// return CodeCompletionKeyPressResult.Completed; {
// } else if (ch == '(') { if (ch == '.') {
// return base.HandleKeyPress(editor, ch); var binding = CreateBinding(editor);
// } return binding.HandleKeyPressed(editor, ch);
// return CodeCompletionKeyPressResult.None; }
// } 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
this.container.AddService(typeof(System.CodeDom.Compiler.CodeDomProvider), new Microsoft.CSharp.CSharpCodeProvider()); 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) if (context == null)
throw new ArgumentNullException("context"); throw new ArgumentNullException("context");
string content = GeneratePartialClassContextStub(fileName, location, context); string content = GeneratePartialClassContextStub(context);
const string caretPoint = "$__Caret_Point__$;"; const string caretPoint = "$__Caret_Point__$;";
int caretOffset = content.IndexOf(caretPoint, StringComparison.Ordinal) + expressionToComplete.Length; int caretOffset = content.IndexOf(caretPoint, StringComparison.Ordinal) + expressionToComplete.Length;
SD.Log.DebugFormatted("context used for dot completion: {0}", content.Replace(caretPoint, "$" + expressionToComplete + "|$")); SD.Log.DebugFormatted("context used for dot completion: {0}", content.Replace(caretPoint, "$" + expressionToComplete + "|$"));
var doc = new ReadOnlyDocument(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 member = context.CurrentMember;
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));
if (member == null) if (member == null)
return ""; return "";
var builder = new TypeSystemAstBuilder(); var builder = new TypeSystemAstBuilder();
MethodDeclaration decl; MethodDeclaration decl;
if (unresolvedMember is IMethod) { if (member is IMethod) {
// If it's a method, convert it directly (including parameters + type parameters) // If it's a method, convert it directly (including parameters + type parameters)
decl = (MethodDeclaration)builder.ConvertEntity(member); decl = (MethodDeclaration)builder.ConvertEntity(member);
} else { } else {
@ -97,11 +88,11 @@ namespace CSharpBinding
} }
decl.Name = "__DebuggerStub__"; decl.Name = "__DebuggerStub__";
decl.ReturnType = builder.ConvertType(member.ReturnType); 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 // Make the method look like an explicit interface implementation so that it doesn't appear in CC
decl.PrivateImplementationType = new SimpleType("__DummyType__"); decl.PrivateImplementationType = new SimpleType("__DummyType__");
decl.Body = GenerateBodyFromContext(builder, context.LocalVariables.ToArray()); 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) static BlockStatement GenerateBodyFromContext(TypeSystemAstBuilder builder, IVariable[] variables)
@ -113,7 +104,7 @@ namespace CSharpBinding
return body; return body;
} }
static AstNode WrapInType(IUnresolvedTypeDefinition entity, EntityDeclaration decl) static AstNode WrapInType(ITypeDefinition entity, EntityDeclaration decl)
{ {
if (entity == null) if (entity == null)
return decl; return decl;
@ -137,7 +128,7 @@ namespace CSharpBinding
}; };
} }
static ClassType GetClassType(IUnresolvedTypeDefinition entity) static ClassType GetClassType(ITypeDefinition entity)
{ {
switch (entity.Kind) { switch (entity.Kind) {
case TypeKind.Interface: case TypeKind.Interface:

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

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

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

@ -61,38 +61,24 @@ namespace CSharpBinding.Completion
return new CSharpCompletionContext(editor, parseInfo.SyntaxTree.ConditionalSymbols, compilation, projectContent, editor.Document, parseInfo.UnresolvedFile, editor.Caret.Location); 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); IDocument document = new ReadOnlyDocument(fileContent);
// Don't require the very latest parse information, an older cached version is OK. var projectContent = context.Compilation.MainAssembly.UnresolvedAssembly as IProjectContent;
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);
if (projectContent == null) if (projectContent == null)
return null; return null;
CSharpParser parser = new CSharpParser(project.CompilerSettings); CSharpParser parser = new CSharpParser();
parser.GenerateTypeSystemMode = false; parser.GenerateTypeSystemMode = false;
SyntaxTree cu = parser.Parse(fileContent, Path.GetRandomFileName() + ".cs"); SyntaxTree cu = parser.Parse(fileContent, Path.GetRandomFileName() + ".cs");
cu.Freeze(); cu.Freeze();
CSharpUnresolvedFile unresolvedFile = cu.ToTypeSystem(); 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) 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
ContextTextLocation = new TextLocation(frame.NextStatement.StartLine, frame.NextStatement.StartColumn); ContextTextLocation = new TextLocation(frame.NextStatement.StartLine, frame.NextStatement.StartColumn);
} }
if (ContextFileName == null) return; 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; if (binding == null) return;
binding.HandleKeyPressed(editorAdapter, '.'); binding.HandleKeyPressed(editorAdapter, '.');
} else { } else {

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

@ -88,7 +88,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
{ {
var fileName = new ICSharpCode.Core.FileName(frame.NextStatement.Filename); var fileName = new ICSharpCode.Core.FileName(frame.NextStatement.Filename);
var textLocation = new TextLocation(frame.NextStatement.StartLine, frame.NextStatement.StartColumn); 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; if (binding == null) return;
binding.HandleKeyPressed(console.TextEditor, '.'); binding.HandleKeyPressed(console.TextEditor, '.');
} }

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

@ -41,12 +41,10 @@ namespace Debugger.AddIn.Pads.Controls
return !errors.Any(); 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); return SD.LanguageService.GetLanguageByExtension(".cs")
if (lang == null) .CreateCompletionBinding(expressionToComplete, context);
return null;
return lang.CreateCompletionBinding(expressionToComplete, fileName, location, context);
} }
} }
} }

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

@ -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(); throw new NotSupportedException();
} }

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

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

Loading…
Cancel
Save