Skip to content
This repository was archived by the owner on Oct 16, 2020. It is now read-only.

Commit 466aa3a

Browse files
fix #371: No code completion in Razor .cshtml files
1 parent 8063942 commit 466aa3a

11 files changed

Lines changed: 152 additions & 76 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@
130130
</ComplexCondition>
131131
</Path>
132132

133-
<Path name = "/AddIns/DefaultTextEditor/CodeCompletion">
133+
<Path name = "/SharpDevelop/ViewContent/TextEditor/CodeCompletion">
134134
<CodeCompletionBinding id = "RazorCSharp" extensions = ".cshtml" class = "ICSharpCode.AspNet.Mvc.Completion.RazorCSharpCompletionBinding"/>
135135
</Path>
136136

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,11 @@
285285
<Name>ICSharpCode.AvalonEdit</Name>
286286
<Private>False</Private>
287287
</ProjectReference>
288+
<ProjectReference Include="..\..\..\..\Libraries\NRefactory\ICSharpCode.NRefactory.CSharp\ICSharpCode.NRefactory.CSharp.csproj">
289+
<Project>{53DCA265-3C3C-42F9-B647-F72BA678122B}</Project>
290+
<Name>ICSharpCode.NRefactory.CSharp</Name>
291+
<Private>False</Private>
292+
</ProjectReference>
288293
<ProjectReference Include="..\..\..\..\Libraries\NRefactory\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj">
289294
<Project>{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}</Project>
290295
<Name>ICSharpCode.NRefactory</Name>

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

Lines changed: 119 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,122 @@
1616
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
1717
// DEALINGS IN THE SOFTWARE.
1818

19-
//using System;
20-
//using ICSharpCode.SharpDevelop.Editor;
21-
//using ICSharpCode.SharpDevelop.Editor.CodeCompletion;
22-
//
23-
//namespace ICSharpCode.AspNet.Mvc.Completion
24-
//{
25-
// public class RazorCSharpCompletionBinding : DefaultCodeCompletionBinding
26-
// {
27-
// public RazorCSharpCompletionBinding()
28-
// {
29-
// }
30-
//
31-
// public override CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch)
32-
// {
33-
// if (ch == '.') {
34-
// new RazorCSharpDotCompletionDataProvider().ShowCompletion(editor);
35-
// return CodeCompletionKeyPressResult.Completed;
36-
// } else if (ch == '(') {
37-
// return base.HandleKeyPress(editor, ch);
38-
// }
39-
// return CodeCompletionKeyPressResult.None;
40-
// }
41-
// }
42-
//}
19+
using System;
20+
using System.IO;
21+
using ICSharpCode.NRefactory;
22+
using ICSharpCode.NRefactory.CSharp.Resolver;
23+
using ICSharpCode.NRefactory.CSharp.TypeSystem;
24+
using ICSharpCode.NRefactory.TypeSystem;
25+
using ICSharpCode.NRefactory.TypeSystem.Implementation;
26+
using ICSharpCode.SharpDevelop;
27+
using ICSharpCode.SharpDevelop.Editor;
28+
using ICSharpCode.SharpDevelop.Editor.CodeCompletion;
29+
30+
namespace ICSharpCode.AspNet.Mvc.Completion
31+
{
32+
public class RazorCSharpCompletionBinding : ICodeCompletionBinding
33+
{
34+
public bool HandleKeyPressed(ITextEditor editor, char ch)
35+
{
36+
if (ch == '.') {
37+
var binding = CreateBinding(editor);
38+
return binding.HandleKeyPressed(editor, ch);
39+
}
40+
return false;
41+
}
42+
43+
public bool CtrlSpace(ITextEditor editor)
44+
{
45+
return false;
46+
}
47+
48+
public CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch)
49+
{
50+
// We use HandleKeyPressed instead.
51+
return CodeCompletionKeyPressResult.None;
52+
}
53+
54+
ICodeCompletionBinding CreateBinding(ITextEditor editor)
55+
{
56+
return SD.LanguageService.GetLanguageByExtension(".cs")
57+
.CreateCompletionBinding(FindExpressionToComplete(editor), CreateContext(editor));
58+
}
59+
60+
string FindExpressionToComplete(ITextEditor editor)
61+
{
62+
int endOffset = editor.Caret.Offset;
63+
int startOffset = endOffset;
64+
while (startOffset > 0 && IsValidCharacter(editor.Document.GetCharAt(startOffset - 1)))
65+
startOffset--;
66+
return editor.Document.GetText(startOffset, endOffset - startOffset);
67+
}
68+
69+
bool IsValidCharacter(char ch)
70+
{
71+
return Char.IsLetterOrDigit(ch) ||
72+
(ch == '.') ||
73+
(ch == '_');
74+
}
75+
76+
ICodeContext CreateContext(ITextEditor editor)
77+
{
78+
var compilation = SD.ParserService.GetCompilationForFile(editor.FileName);
79+
var project = SD.ProjectService.FindProjectContainingFile(editor.FileName);
80+
81+
var resolveContext = new SimpleTypeResolveContext(compilation.MainAssembly);
82+
83+
var currentTypeDefinition = new DefaultUnresolvedTypeDefinition(project.RootNamespace, Path.GetFileNameWithoutExtension(editor.FileName));
84+
ITypeReference baseTypeReference = new GetClassTypeReference("System.Web.Mvc", "WebViewPage", 1);
85+
baseTypeReference = new ParameterizedTypeReference(baseTypeReference, new[] { KnownTypeReference.Object });
86+
currentTypeDefinition.BaseTypes.Add(baseTypeReference);
87+
88+
var currentMethod = new DefaultUnresolvedMethod(currentTypeDefinition, "__ContextStub__");
89+
currentMethod.ReturnType = KnownTypeReference.Void;
90+
currentTypeDefinition.Members.Add(currentMethod);
91+
92+
var currentResolvedTypeDef = new DefaultResolvedTypeDefinition(resolveContext, currentTypeDefinition);
93+
94+
var projectContent = compilation.MainAssembly.UnresolvedAssembly as IProjectContent;
95+
96+
var currentFile = new CSharpUnresolvedFile();
97+
98+
currentFile.RootUsingScope.AddSimpleUsing("System.Web.Mvc");
99+
currentFile.RootUsingScope.AddSimpleUsing("System.Web.Mvc.Ajax");
100+
currentFile.RootUsingScope.AddSimpleUsing("System.Web.Mvc.Html");
101+
currentFile.RootUsingScope.AddSimpleUsing("System.Web.Routing");
102+
103+
currentFile.TopLevelTypeDefinitions.Add(currentTypeDefinition);
104+
105+
if (projectContent != null) {
106+
compilation = projectContent.AddOrUpdateFiles(currentFile).CreateCompilation(SD.ParserService.GetCurrentSolutionSnapshot());
107+
}
108+
109+
var context = new CSharpTypeResolveContext(compilation.MainAssembly,
110+
currentFile.RootUsingScope.Resolve(compilation),
111+
currentResolvedTypeDef,
112+
currentMethod.CreateResolved(resolveContext.WithCurrentTypeDefinition(currentResolvedTypeDef)));
113+
return new CSharpResolver(context);
114+
}
115+
}
116+
117+
static class NRUtils
118+
{
119+
/// <remarks>Does not support type arguments!</remarks>
120+
public static void AddSimpleUsing(this UsingScope scope, string fullName)
121+
{
122+
if (scope == null)
123+
throw new ArgumentNullException("scope");
124+
string[] parts = fullName.Trim().Split('.');
125+
TypeOrNamespaceReference reference = null;
126+
foreach (var part in parts) {
127+
if (reference != null) {
128+
reference = new MemberTypeOrNamespaceReference(reference, part, EmptyList<ITypeReference>.Instance);
129+
} else {
130+
reference = new SimpleTypeOrNamespaceReference(part, EmptyList<ITypeReference>.Instance);
131+
}
132+
}
133+
134+
scope.Usings.AddIfNotNull(reference);
135+
}
136+
}
137+
}

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

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -55,35 +55,26 @@ public CSharpLanguageBinding()
5555
this.container.AddService(typeof(System.CodeDom.Compiler.CodeDomProvider), new Microsoft.CSharp.CSharpCodeProvider());
5656
}
5757

58-
public override ICodeCompletionBinding CreateCompletionBinding(string expressionToComplete, FileName fileName, TextLocation location, ICodeContext context)
58+
public override ICodeCompletionBinding CreateCompletionBinding(string expressionToComplete, ICodeContext context)
5959
{
60-
if (fileName == null)
61-
throw new ArgumentNullException("fileName");
6260
if (context == null)
6361
throw new ArgumentNullException("context");
64-
string content = GeneratePartialClassContextStub(fileName, location, context);
62+
string content = GeneratePartialClassContextStub(context);
6563
const string caretPoint = "$__Caret_Point__$;";
6664
int caretOffset = content.IndexOf(caretPoint, StringComparison.Ordinal) + expressionToComplete.Length;
6765
SD.Log.DebugFormatted("context used for dot completion: {0}", content.Replace(caretPoint, "$" + expressionToComplete + "|$"));
6866
var doc = new ReadOnlyDocument(content.Replace(caretPoint, expressionToComplete));
69-
return new CSharpCompletionBinding(fileName, doc.GetLocation(caretOffset), doc.CreateSnapshot());
67+
return new CSharpCompletionBinding(context, doc.GetLocation(caretOffset), doc.CreateSnapshot());
7068
}
7169

72-
static string GeneratePartialClassContextStub(FileName fileName, TextLocation location, ICodeContext context)
70+
static string GeneratePartialClassContextStub(ICodeContext context)
7371
{
74-
var compilation = SD.ParserService.GetCompilationForFile(fileName);
75-
var file = SD.ParserService.GetExistingUnresolvedFile(fileName);
76-
if (compilation == null || file == null)
77-
return "";
78-
var unresolvedMember = file.GetMember(location);
79-
if (unresolvedMember == null)
80-
return "";
81-
var member = unresolvedMember.Resolve(new SimpleTypeResolveContext(compilation.MainAssembly));
72+
var member = context.CurrentMember;
8273
if (member == null)
8374
return "";
8475
var builder = new TypeSystemAstBuilder();
8576
MethodDeclaration decl;
86-
if (unresolvedMember is IMethod) {
77+
if (member is IMethod) {
8778
// If it's a method, convert it directly (including parameters + type parameters)
8879
decl = (MethodDeclaration)builder.ConvertEntity(member);
8980
} else {
@@ -97,11 +88,11 @@ static string GeneratePartialClassContextStub(FileName fileName, TextLocation lo
9788
}
9889
decl.Name = "__DebuggerStub__";
9990
decl.ReturnType = builder.ConvertType(member.ReturnType);
100-
decl.Modifiers = unresolvedMember.IsStatic ? Modifiers.Static : Modifiers.None;
91+
decl.Modifiers = member.IsStatic ? Modifiers.Static : Modifiers.None;
10192
// Make the method look like an explicit interface implementation so that it doesn't appear in CC
10293
decl.PrivateImplementationType = new SimpleType("__DummyType__");
10394
decl.Body = GenerateBodyFromContext(builder, context.LocalVariables.ToArray());
104-
return WrapInType(unresolvedMember.DeclaringTypeDefinition, decl).ToString();
95+
return WrapInType(context.CurrentTypeDefinition, decl).ToString();
10596
}
10697

10798
static BlockStatement GenerateBodyFromContext(TypeSystemAstBuilder builder, IVariable[] variables)
@@ -113,7 +104,7 @@ static BlockStatement GenerateBodyFromContext(TypeSystemAstBuilder builder, IVar
113104
return body;
114105
}
115106

116-
static AstNode WrapInType(IUnresolvedTypeDefinition entity, EntityDeclaration decl)
107+
static AstNode WrapInType(ITypeDefinition entity, EntityDeclaration decl)
117108
{
118109
if (entity == null)
119110
return decl;
@@ -137,7 +128,7 @@ static AstNode WrapInType(IUnresolvedTypeDefinition entity, EntityDeclaration de
137128
};
138129
}
139130

140-
static ClassType GetClassType(IUnresolvedTypeDefinition entity)
131+
static ClassType GetClassType(ITypeDefinition entity)
141132
{
142133
switch (entity.Kind) {
143134
case TypeKind.Interface:

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
using ICSharpCode.NRefactory.Completion;
2727
using ICSharpCode.NRefactory.CSharp.Completion;
2828
using ICSharpCode.NRefactory.Editor;
29+
using ICSharpCode.NRefactory.TypeSystem;
2930
using ICSharpCode.SharpDevelop;
3031
using ICSharpCode.SharpDevelop.Editor;
3132
using ICSharpCode.SharpDevelop.Editor.CodeCompletion;
@@ -35,7 +36,7 @@ namespace CSharpBinding.Completion
3536
{
3637
public class CSharpCompletionBinding : ICodeCompletionBinding
3738
{
38-
FileName contextFileName;
39+
ICodeContext context;
3940
TextLocation currentLocation;
4041
ITextSource fileContent;
4142

@@ -44,9 +45,9 @@ public CSharpCompletionBinding()
4445
{
4546
}
4647

47-
public CSharpCompletionBinding(FileName contextFileName, TextLocation currentLocation, ITextSource fileContent)
48+
public CSharpCompletionBinding(ICodeContext context, TextLocation currentLocation, ITextSource fileContent)
4849
{
49-
this.contextFileName = contextFileName;
50+
this.context = context;
5051
this.currentLocation = currentLocation;
5152
this.fileContent = fileContent;
5253
}
@@ -75,7 +76,7 @@ bool ShowCompletion(ITextEditor editor, char completionChar, bool ctrlSpace)
7576
if (fileContent == null) {
7677
completionContext = CSharpCompletionContext.Get(editor);
7778
} else {
78-
completionContext = CSharpCompletionContext.Get(editor, fileContent, currentLocation, contextFileName);
79+
completionContext = CSharpCompletionContext.Get(editor, context, currentLocation, fileContent);
7980
}
8081
if (completionContext == null)
8182
return false;

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

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -61,38 +61,24 @@ public static CSharpCompletionContext Get(ITextEditor editor)
6161
return new CSharpCompletionContext(editor, parseInfo.SyntaxTree.ConditionalSymbols, compilation, projectContent, editor.Document, parseInfo.UnresolvedFile, editor.Caret.Location);
6262
}
6363

64-
public static CSharpCompletionContext Get(ITextEditor editor, ITextSource fileContent, TextLocation currentLocation, FileName fileName)
64+
public static CSharpCompletionContext Get(ITextEditor editor, ICodeContext context, TextLocation currentLocation, ITextSource fileContent)
6565
{
6666
IDocument document = new ReadOnlyDocument(fileContent);
6767

68-
// Don't require the very latest parse information, an older cached version is OK.
69-
var parseInfo = SD.ParserService.GetCachedParseInformation(fileName) as CSharpFullParseInformation;
70-
if (parseInfo == null) {
71-
parseInfo = SD.ParserService.Parse(fileName) as CSharpFullParseInformation;
72-
}
73-
if (parseInfo == null)
74-
return null;
75-
76-
77-
var project = SD.ProjectService.FindProjectContainingFile(fileName)as CSharpProject;
78-
if (project == null)
79-
return null;
80-
81-
var solutionSnapshot = SD.ParserService.GetCurrentSolutionSnapshot();
82-
var projectContent = solutionSnapshot.GetProjectContent(project);
68+
var projectContent = context.Compilation.MainAssembly.UnresolvedAssembly as IProjectContent;
8369
if (projectContent == null)
8470
return null;
8571

86-
CSharpParser parser = new CSharpParser(project.CompilerSettings);
72+
CSharpParser parser = new CSharpParser();
8773
parser.GenerateTypeSystemMode = false;
8874

8975
SyntaxTree cu = parser.Parse(fileContent, Path.GetRandomFileName() + ".cs");
9076
cu.Freeze();
9177

9278
CSharpUnresolvedFile unresolvedFile = cu.ToTypeSystem();
93-
ICompilation compilation = projectContent.AddOrUpdateFiles(unresolvedFile).CreateCompilation(solutionSnapshot);
79+
ICompilation compilation = projectContent.AddOrUpdateFiles(unresolvedFile).CreateCompilation(SD.ParserService.GetCurrentSolutionSnapshot());
9480

95-
return new CSharpCompletionContext(editor, parseInfo.SyntaxTree.ConditionalSymbols, compilation, projectContent, document, unresolvedFile, currentLocation);
81+
return new CSharpCompletionContext(editor, EmptyList<string>.Instance, compilation, projectContent, document, unresolvedFile, currentLocation);
9682
}
9783

9884
private CSharpCompletionContext(ITextEditor editor, IList<string> conditionalSymbols, ICompilation compilation, IProjectContent projectContent, IDocument document, CSharpUnresolvedFile unresolvedFile, TextLocation caretLocation)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ void editor_TextArea_TextEntered(object sender, TextCompositionEventArgs e)
134134
ContextTextLocation = new TextLocation(frame.NextStatement.StartLine, frame.NextStatement.StartColumn);
135135
}
136136
if (ContextFileName == null) return;
137-
var binding = DebuggerDotCompletion.PrepareDotCompletion(editor.Text.Substring(0, editor.CaretOffset), ContextFileName, ContextTextLocation, SD.ParserService.ResolveContext(ContextFileName, ContextTextLocation));
137+
var binding = DebuggerDotCompletion.PrepareDotCompletion(editor.Text.Substring(0, editor.CaretOffset), SD.ParserService.ResolveContext(ContextFileName, ContextTextLocation));
138138
if (binding == null) return;
139139
binding.HandleKeyPressed(editorAdapter, '.');
140140
} else {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ void ShowDotCompletion(StackFrame frame, string currentText)
8888
{
8989
var fileName = new ICSharpCode.Core.FileName(frame.NextStatement.Filename);
9090
var textLocation = new TextLocation(frame.NextStatement.StartLine, frame.NextStatement.StartColumn);
91-
var binding = DebuggerDotCompletion.PrepareDotCompletion(currentText, fileName, textLocation, SD.ParserService.ResolveContext(fileName, textLocation));
91+
var binding = DebuggerDotCompletion.PrepareDotCompletion(currentText, SD.ParserService.ResolveContext(fileName, textLocation));
9292
if (binding == null) return;
9393
binding.HandleKeyPressed(console.TextEditor, '.');
9494
}

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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,10 @@ public static bool CheckSyntax(string expression, out Error[] errors)
4141
return !errors.Any();
4242
}
4343

44-
public static ICodeCompletionBinding PrepareDotCompletion(string expressionToComplete, FileName fileName, TextLocation location, ICodeContext context)
44+
public static ICodeCompletionBinding PrepareDotCompletion(string expressionToComplete, ICodeContext context)
4545
{
46-
var lang = SD.LanguageService.GetLanguageByFileName(fileName);
47-
if (lang == null)
48-
return null;
49-
return lang.CreateCompletionBinding(expressionToComplete, fileName, location, context);
46+
return SD.LanguageService.GetLanguageByExtension(".cs")
47+
.CreateCompletionBinding(expressionToComplete, context);
5048
}
5149
}
5250
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public System.CodeDom.Compiler.CodeDomProvider CodeDomProvider {
7575
}
7676
}
7777

78-
public virtual ICodeCompletionBinding CreateCompletionBinding(string expressionToComplete, FileName fileName, TextLocation location, ICodeContext context)
78+
public virtual ICodeCompletionBinding CreateCompletionBinding(string expressionToComplete, ICodeContext context)
7979
{
8080
throw new NotSupportedException();
8181
}

0 commit comments

Comments
 (0)