diff --git a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs index 270a308a36..fcb8af2bba 100644 --- a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs +++ b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs @@ -57,7 +57,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion public bool CloseOnSquareBrackets; #endregion - public CSharpCompletionEngine(IDocument document, ICompletionDataFactory factory, IProjectContent content, CSharpTypeResolveContext ctx, CompilationUnit unit, CSharpParsedFile parsedFile) : base (content, ctx, unit, parsedFile) + public CSharpCompletionEngine(IDocument document, ICompletionContextProvider completionContextProvider, ICompletionDataFactory factory, IProjectContent content, CSharpTypeResolveContext ctx) : base (content, completionContextProvider, ctx) { if (document == null) { throw new ArgumentNullException("document"); @@ -239,7 +239,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion parent = (ArrayInitializerExpression)parent.Parent; if (p != null) { var contextList = new CompletionDataWrapper(this); - var initializerResult = ResolveExpression(p, unit); + var initializerResult = ResolveExpression(p); if (initializerResult != null && initializerResult.Item1.Type.Kind != TypeKind.Unknown) { // check 3 cases: // 1) New initalizer { xpr @@ -255,7 +255,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion } if (prev != null && !(prev is NamedExpression)) { - AddContextCompletion(contextList, GetState(), n, unit); + AddContextCompletion(contextList, GetState(), n); // case 3) return contextList.Result; } @@ -279,7 +279,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion return contextList.Result; } - AddContextCompletion(contextList, GetState(), n, unit); + AddContextCompletion(contextList, GetState(), n); return contextList.Result; } } @@ -480,8 +480,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion AddContextCompletion( wrapper, resolveResult.Item2, - expressionOrVariableDeclaration.Node, - expressionOrVariableDeclaration.Unit); + expressionOrVariableDeclaration.Node); AddEnumMembers(wrapper, resolveResult.Item1.Type, resolveResult.Item2); AutoCompleteEmptyMatch = false; return wrapper.Result; @@ -742,8 +741,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion if (n != null && n.Parent is InvocationExpression) { var invokeParent = (InvocationExpression)n.Parent; var invokeResult = ResolveExpression( - invokeParent.Target, - identifierStart.Unit + invokeParent.Target ); var mgr = invokeResult != null ? invokeResult.Item1 as MethodGroupResolveResult : null; if (mgr != null) { @@ -777,7 +775,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion } if (n != null && n.Parent is ObjectCreateExpression) { - var invokeResult = ResolveExpression(n.Parent, identifierStart.Unit); + var invokeResult = ResolveExpression(n.Parent); var mgr = invokeResult != null ? invokeResult.Item1 as ResolveResult : null; if (mgr != null) { foreach (var constructor in mgr.Type.GetConstructors ()) { @@ -797,14 +795,13 @@ namespace ICSharpCode.NRefactory.CSharp.Completion } // check for compare to enum case if (evaluationExpr != null) { - resolveResult = ResolveExpression(evaluationExpr, identifierStart.Unit); + resolveResult = ResolveExpression(evaluationExpr); if (resolveResult != null && resolveResult.Item1.Type.Kind == TypeKind.Enum) { var wrapper = new CompletionDataWrapper(this); AddContextCompletion( wrapper, resolveResult.Item2, - evaluationExpr, - identifierStart.Unit + evaluationExpr ); AddEnumMembers(wrapper, resolveResult.Item1.Type, resolveResult.Item2); AutoCompleteEmptyMatch = false; @@ -827,7 +824,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion return DefaultControlSpaceItems(); } - var initalizerResult = ResolveExpression(n.Parent, identifierStart.Unit); + var initalizerResult = ResolveExpression(n.Parent); var concreteNode = identifierStart.Unit.GetNodeAt(location); // check if we're on the right side of an initializer expression @@ -869,8 +866,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion } if (n is MemberType) { resolveResult = ResolveExpression( - ((MemberType)n).Target, - identifierStart.Unit + ((MemberType)n).Target ); return CreateTypeAndNamespaceCompletionData( location, @@ -886,11 +882,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion if (n.Parent is ICSharpCode.NRefactory.CSharp.Attribute) { nodes.Add(n.Parent); } - var astResolver = new CSharpAstResolver( - csResolver, - identifierStart.Unit, - CSharpParsedFile - ); + var astResolver = CompletionContextProvider.GetResolver (csResolver, identifierStart.Unit); astResolver.ApplyNavigator(new NodeListResolveVisitorNavigator(nodes)); try { csResolver = astResolver.GetResolverStateBefore(n); @@ -917,8 +909,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion AddContextCompletion( contextList, csResolver, - identifierStart.Node, - identifierStart.Unit + identifierStart.Node ); return contextList.Result; // if (stub.Parent is BlockStatement) @@ -1111,7 +1102,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion Tuple rr; if (xp != null) { node = xp.Node; - rr = ResolveExpression(node, xp.Unit); + rr = ResolveExpression(node); unit = xp.Unit; } else { unit = ParseStub("foo", false); @@ -1120,7 +1111,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion location.Column + 2, n => n is Expression || n is AstType ); - rr = ResolveExpression(node, unit); + rr = ResolveExpression(node); } if (node is Identifier && node.Parent is ForeachStatement) { var foreachStmt = (ForeachStatement)node.Parent; @@ -1152,7 +1143,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion return wrapper.Result; } } - if (Unit != null && (node == null || node is TypeDeclaration)) { +/* if (Unit != null && (node == null || node is TypeDeclaration)) { var constructor = Unit.GetNodeAt( location.Line, location.Column - 3 @@ -1162,7 +1153,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion wrapper.AddCustom("base"); return wrapper.Result; } - } + }*/ var initializer = node != null ? node.Parent as ArrayInitializerExpression : null; if (initializer != null) { @@ -1190,12 +1181,12 @@ namespace ICSharpCode.NRefactory.CSharp.Completion csResolver = GetState(); } } - AddContextCompletion(wrapper, csResolver, node, unit); + AddContextCompletion(wrapper, csResolver, node); return wrapper.Result; } - void AddContextCompletion(CompletionDataWrapper wrapper, CSharpResolver state, AstNode node, CompilationUnit unit) + void AddContextCompletion(CompletionDataWrapper wrapper, CSharpResolver state, AstNode node) { if (state != null && !(node is AstType)) { foreach (var variable in state.LocalVariables) { @@ -1266,7 +1257,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion wrapper.Result.AddRange(factory.CreateCodeTemplateCompletionData()); if (node != null && node.Role == Roles.Argument) { - var resolved = ResolveExpression(node.Parent, unit); + var resolved = ResolveExpression(node.Parent); var invokeResult = resolved != null ? resolved.Item1 as CSharpInvocationResolveResult : null; if (invokeResult != null) { int argNum = 0; @@ -1284,7 +1275,10 @@ namespace ICSharpCode.NRefactory.CSharp.Completion } if (node is Expression) { - var astResolver = new CSharpAstResolver(state, unit, CSharpParsedFile); + var root = node; + while (root.Parent != null) + root = root.Parent; + var astResolver = CompletionContextProvider.GetResolver (state, root); foreach (var type in CreateFieldAction.GetValidTypes(astResolver, (Expression)node)) { if (type.Kind == TypeKind.Enum) { AddEnumMembers(wrapper, type, state); @@ -1320,10 +1314,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion void AddTypesAndNamespaces(CompletionDataWrapper wrapper, CSharpResolver state, AstNode node, Func typePred = null, Predicate memberPred = null, Action callback = null) { - var lookup = new MemberLookup( - ctx.CurrentTypeDefinition, - Compilation.MainAssembly - ); + var lookup = new MemberLookup(ctx.CurrentTypeDefinition, Compilation.MainAssembly); if (currentType != null) { for (var ct = currentType; ct != null; ct = ct.DeclaringTypeDefinition) { foreach (var nestedType in ct.NestedTypes) { @@ -1384,7 +1375,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion wrapper.AddTypeParameter(p); } } - var scope = CSharpParsedFile.GetUsingScope(location).Resolve(Compilation); + var scope = ctx.CurrentUsingScope; for (var n = scope; n != null; n = n.Parent) { foreach (var pair in n.UsingAliases) { @@ -1523,7 +1514,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion parent = parent.Parent; } if (parent is VariableDeclarationStatement) { - var resolved = ResolveExpression(parent, isAsExpression.Unit); + var resolved = ResolveExpression(parent); if (resolved != null) { isAsType = resolved.Item1.Type; } @@ -1662,11 +1653,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion var expressionOrVariableDeclaration = GetNewExpressionAt(j); if (expressionOrVariableDeclaration == null) return null; - var astResolver = new CSharpAstResolver( - GetState(), - expressionOrVariableDeclaration.Unit, - CSharpParsedFile - ); + var astResolver = CompletionContextProvider.GetResolver(GetState(), expressionOrVariableDeclaration.Unit); hintType = CreateFieldAction.GetValidTypes( astResolver, expressionOrVariableDeclaration.Node as Expression @@ -1689,8 +1676,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion AddContextCompletion( inList, rr != null ? rr.Item2 : GetState(), - expr.Node, - Unit + expr.Node ); return inList.Result; } @@ -2149,11 +2135,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion var exprParent = resolvedNode.GetParent(); var unit = exprParent != null ? exprParent.GetParent() : null; - var astResolver = unit != null ? new CSharpAstResolver( - state, - unit, - CSharpParsedFile - ) : null; + var astResolver = unit != null ? CompletionContextProvider.GetResolver(state, unit) : null; IType hintType = exprParent != null && astResolver != null ? CreateFieldAction.GetValidTypes(astResolver, exprParent) .FirstOrDefault() : null; @@ -2238,7 +2220,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion AutoCompleteEmptyMatch = false; AutoSelect = false; } - AddContextCompletion(result, state, invocation, unit); + AddContextCompletion(result, state, invocation); // resolver.AddAccessibleCodeCompletionData (ExpressionContext.MethodBody, cdc); // if (addedDelegates.Count > 0) { @@ -2372,7 +2354,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion } } // ADD Aliases - var scope = CSharpParsedFile.GetUsingScope(location).Resolve(Compilation); + var scope = ctx.CurrentUsingScope; for (var n = scope; n != null; n = n.Parent) { foreach (var pair in n.UsingAliases) { @@ -2752,7 +2734,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion sb.Append("a;"); AppendMissingClosingBrackets(sb, text, false); var stream = new System.IO.StringReader(sb.ToString()); - var completionUnit = parser.Parse(stream, CSharpParsedFile.FileName, 0); + var completionUnit = parser.Parse(stream, "a.cs", 0); stream.Close(); var loc = document.GetLocation(offset); @@ -2775,7 +2757,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion AppendMissingClosingBrackets(sb, text, false); var stream = new System.IO.StringReader(sb.ToString()); - var completionUnit = parser.Parse(stream, CSharpParsedFile.FileName, 0); + var completionUnit = parser.Parse(stream, "a.cs", 0); stream.Close(); var loc = document.GetLocation(offset); @@ -2786,7 +2768,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion sb.Append("a ()"); AppendMissingClosingBrackets(sb, text, false); stream = new System.IO.StringReader(sb.ToString()); - completionUnit = parser.Parse(stream, CSharpParsedFile.FileName, 0); + completionUnit = parser.Parse(stream, "a.cs", 0); stream.Close(); loc = document.GetLocation(offset); diff --git a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngineBase.cs b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngineBase.cs index 7ec124b5cb..a2ec8ec116 100644 --- a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngineBase.cs +++ b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngineBase.cs @@ -1,4 +1,4 @@ -// +// // CSharpCompletionEngineBase.cs // // Author: @@ -51,10 +51,6 @@ namespace ICSharpCode.NRefactory.CSharp.Completion #region Input properties public CSharpTypeResolveContext ctx { get; private set; } - public CompilationUnit Unit { get; private set; } - - public CSharpParsedFile CSharpParsedFile { get; private set; } - public IProjectContent ProjectContent { get; private set; } ICompilation compilation; @@ -68,27 +64,24 @@ namespace ICSharpCode.NRefactory.CSharp.Completion } #endregion - protected CSharpCompletionEngineBase (IProjectContent content, CSharpTypeResolveContext ctx, CompilationUnit unit, CSharpParsedFile parsedFile) + protected CSharpCompletionEngineBase(IProjectContent content, ICompletionContextProvider completionContextProvider, CSharpTypeResolveContext ctx) { if (content == null) - throw new ArgumentNullException ("content"); + throw new ArgumentNullException("content"); if (ctx == null) - throw new ArgumentNullException ("ctx"); - if (unit == null) - throw new ArgumentNullException ("unit"); - if (parsedFile == null) - throw new ArgumentNullException ("parsedFile"); + throw new ArgumentNullException("ctx"); + if (completionContextProvider == null) + throw new ArgumentNullException("completionContextProvider"); this.ProjectContent = content; + this.CompletionContextProvider = completionContextProvider; this.ctx = ctx; - this.Unit = unit; - this.CSharpParsedFile = parsedFile; } - public IMemberProvider MemberProvider { + public ICompletionContextProvider CompletionContextProvider { get; - set; + private set; } public void SetOffset (int offset) @@ -97,8 +90,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion this.offset = offset; this.location = document.GetLocation (offset); - var provider = MemberProvider ?? new DefaultMemberProvider (this); - provider.GetCurrentMembers (offset, out currentType, out currentMember); + CompletionContextProvider.GetCurrentMembers (offset, out currentType, out currentMember); } public bool GetParameterCompletionCommandOffset (out int cpos) @@ -702,34 +694,16 @@ namespace ICSharpCode.NRefactory.CSharp.Completion } } - string cachedText = null; +// string cachedText = null; protected virtual void Reset () { - cachedText = null; +// cachedText = null; } protected Tuple GetMemberTextToCaret() { - int startOffset; - if (currentMember != null && currentType != null && currentType.Kind != TypeKind.Enum) { - startOffset = document.GetOffset(currentMember.Region.Begin); - } else if (currentType != null) { - startOffset = document.GetOffset(currentType.Region.Begin); - } else { - startOffset = 0; - } - while (startOffset > 0) { - char ch = document.GetCharAt(startOffset - 1); - if (ch != ' ' && ch != '\t') { - break; - } - --startOffset; - } - if (cachedText == null) - cachedText = document.GetText (startOffset, offset - startOffset); - - return Tuple.Create (cachedText, document.GetLocation (startOffset)); + return CompletionContextProvider.GetMemberTextToCaret(offset, currentType, currentMember); } protected ExpressionResult GetInvocationBeforeCursor(bool afterBracket) @@ -806,10 +780,10 @@ namespace ICSharpCode.NRefactory.CSharp.Completion protected Tuple ResolveExpression (ExpressionResult tuple) { - return ResolveExpression (tuple.Node, tuple.Unit); + return ResolveExpression (tuple.Node); } - protected Tuple ResolveExpression(AstNode expr, CompilationUnit unit) + protected Tuple ResolveExpression(AstNode expr) { if (expr == null) { return null; @@ -823,12 +797,11 @@ namespace ICSharpCode.NRefactory.CSharp.Completion resolveNode = expr; } try { - var ctx = CSharpParsedFile.GetResolver(Compilation, location); var root = expr.AncestorsAndSelf.FirstOrDefault(n => n is EntityDeclaration || n is CompilationUnit); if (root == null) { return null; } - var csResolver = new CSharpAstResolver (ctx, root, CSharpParsedFile); + var csResolver = CompletionContextProvider.GetResolver (GetState(), root); var result = csResolver.Resolve(resolveNode); var state = csResolver.GetResolverStateBefore(resolveNode); return Tuple.Create(result, state); @@ -839,128 +812,5 @@ namespace ICSharpCode.NRefactory.CSharp.Completion } #endregion - - class DefaultMemberProvider : IMemberProvider - { - CSharpCompletionEngineBase engine; - - - public DefaultMemberProvider (CSharpCompletionEngineBase engine) - { - this.engine = engine; - } - - public void GetCurrentMembers (int offset, out IUnresolvedTypeDefinition currentType, out IUnresolvedMember currentMember) - { - //var document = engine.document; - var location = engine.location; - - currentType = null; - - foreach (var type in engine.CSharpParsedFile.TopLevelTypeDefinitions) { - if (type.Region.Begin < location) - currentType = type; - } - currentType = FindInnerType (currentType, location); - - // location is beyond last reported end region, now we need to check, if the end region changed - if (currentType != null && currentType.Region.End < location) { - if (!IsInsideType (currentType, location)) - currentType = null; - } - currentMember = null; - if (currentType != null) { - foreach (var member in currentType.Members) { - if (member.Region.Begin < location && (currentMember == null || currentMember.Region.Begin < member.Region.Begin)) - currentMember = member; - } - } - - // location is beyond last reported end region, now we need to check, if the end region changed - // NOTE: Enums are a special case, there the "last" field needs to be treated as current member - if (currentMember != null && currentMember.Region.End < location && currentType.Kind != TypeKind.Enum) { - if (!IsInsideType (currentMember, location)) - currentMember = null; - } - var stack = GetBracketStack (engine.GetMemberTextToCaret ().Item1); - if (stack.Count == 0) - currentMember = null; - } - - IUnresolvedTypeDefinition FindInnerType (IUnresolvedTypeDefinition parent, TextLocation location) - { - if (parent == null) - return null; - var currentType = parent; - foreach (var type in parent.NestedTypes) { - if (type.Region.Begin < location && location < type.Region.End) - currentType = FindInnerType (type, location); - } - - return currentType; - } - - bool IsInsideType (IUnresolvedEntity currentType, TextLocation location) - { - var document = engine.document; - - int startOffset = document.GetOffset (currentType.Region.Begin); - int endOffset = document.GetOffset (location); - //bool foundEndBracket = false; - - var bracketStack = new Stack (); - - bool isInString = false, isInChar = false; - bool isInLineComment = false, isInBlockComment = false; - - for (int i = startOffset; i < endOffset; i++) { - char ch = document.GetCharAt (i); - switch (ch) { - case '(': - case '[': - case '{': - if (!isInString && !isInChar && !isInLineComment && !isInBlockComment) - bracketStack.Push (ch); - break; - case ')': - case ']': - case '}': - if (!isInString && !isInChar && !isInLineComment && !isInBlockComment) - if (bracketStack.Count > 0) - bracketStack.Pop (); - break; - case '\r': - case '\n': - isInLineComment = false; - break; - case '/': - if (isInBlockComment) { - if (i > 0 && document.GetCharAt (i - 1) == '*') - isInBlockComment = false; - } else if (!isInString && !isInChar && i + 1 < document.TextLength) { - char nextChar = document.GetCharAt (i + 1); - if (nextChar == '/') - isInLineComment = true; - if (!isInLineComment && nextChar == '*') - isInBlockComment = true; - } - break; - case '"': - if (!(isInChar || isInLineComment || isInBlockComment)) - isInString = !isInString; - break; - case '\'': - if (!(isInString || isInLineComment || isInBlockComment)) - isInChar = !isInChar; - break; - default : - break; - } - } - return bracketStack.Any (t => t == '{'); - } - } - - } } \ No newline at end of file diff --git a/ICSharpCode.NRefactory.CSharp/Completion/CSharpParameterCompletionEngine.cs b/ICSharpCode.NRefactory.CSharp/Completion/CSharpParameterCompletionEngine.cs index 5ba4e01ce4..229016533c 100644 --- a/ICSharpCode.NRefactory.CSharp/Completion/CSharpParameterCompletionEngine.cs +++ b/ICSharpCode.NRefactory.CSharp/Completion/CSharpParameterCompletionEngine.cs @@ -39,7 +39,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion { internal IParameterCompletionDataFactory factory; - public CSharpParameterCompletionEngine(IDocument document, IParameterCompletionDataFactory factory, IProjectContent content, CSharpTypeResolveContext ctx, CompilationUnit unit, CSharpParsedFile parsedFile) : base (content, ctx, unit, parsedFile) + public CSharpParameterCompletionEngine(IDocument document, ICompletionContextProvider completionContextProvider, IParameterCompletionDataFactory factory, IProjectContent content, CSharpTypeResolveContext ctx) : base (content, completionContextProvider, ctx) { if (document == null) { throw new ArgumentNullException("document"); @@ -57,9 +57,6 @@ namespace ICSharpCode.NRefactory.CSharp.Completion if (currentMember == null && currentType == null) { return null; } - if (Unit == null) { - return null; - } baseUnit = ParseStub("x] = a[1"); //var memberLocation = currentMember != null ? currentMember.Region.Begin : currentType.Region.Begin; @@ -80,9 +77,6 @@ namespace ICSharpCode.NRefactory.CSharp.Completion if (currentMember == null && currentType == null) { return null; } - if (Unit == null) { - return null; - } baseUnit = ParseStub("a) {}", false); var expr = baseUnit.GetNodeAt (location); @@ -98,9 +92,6 @@ namespace ICSharpCode.NRefactory.CSharp.Completion if (currentMember == null && currentType == null) { return null; } - if (Unit == null) { - return null; - } baseUnit = ParseStub("x> a"); //var memberLocation = currentMember != null ? currentMember.Region.Begin : currentType.Region.Begin; @@ -171,7 +162,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion } } if (invoke.Node is ObjectCreateExpression) { - var createType = ResolveExpression(((ObjectCreateExpression)invoke.Node).Type, invoke.Unit); + var createType = ResolveExpression(((ObjectCreateExpression)invoke.Node).Type); if (createType.Item1.Type.Kind == TypeKind.Unknown) return null; return factory.CreateConstructorProvider(document.GetOffset(invoke.Node.StartLocation), createType.Item1.Type); @@ -234,7 +225,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion if (GetCurrentParameterIndex(document.GetOffset(invoke.Node.StartLocation), offset) < 0) return null; if (invoke.Node is ObjectCreateExpression) { - var createType = ResolveExpression(((ObjectCreateExpression)invoke.Node).Type, invoke.Unit); + var createType = ResolveExpression(((ObjectCreateExpression)invoke.Node).Type); return factory.CreateConstructorProvider(document.GetOffset(invoke.Node.StartLocation), createType.Item1.Type); } @@ -316,17 +307,12 @@ namespace ICSharpCode.NRefactory.CSharp.Completion List GetUsedNamespaces() { - var scope = CSharpParsedFile.GetUsingScope(location); + var scope = ctx.CurrentUsingScope; var result = new List(); - var resolver = new CSharpResolver(ctx); while (scope != null) { - result.Add(scope.NamespaceName); + result.Add(scope.Namespace.FullName); - foreach (var u in scope.Usings) { - var ns = u.ResolveNamespace(resolver); - if (ns == null) { - continue; - } + foreach (var ns in scope.Usings) { result.Add(ns.FullName); } scope = scope.Parent; diff --git a/ICSharpCode.NRefactory.CSharp/Completion/ICompletionContextProvider.cs b/ICSharpCode.NRefactory.CSharp/Completion/ICompletionContextProvider.cs new file mode 100644 index 0000000000..b477dc1bbf --- /dev/null +++ b/ICSharpCode.NRefactory.CSharp/Completion/ICompletionContextProvider.cs @@ -0,0 +1,198 @@ +// +// IMemberProvider.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2012 Xamarin Inc. +// +// 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 ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.Editor; +using ICSharpCode.NRefactory.CSharp.TypeSystem; +using System.Linq; +using ICSharpCode.NRefactory.CSharp.Resolver; + +namespace ICSharpCode.NRefactory.CSharp.Completion +{ + public interface ICompletionContextProvider + { + void GetCurrentMembers (int offset, out IUnresolvedTypeDefinition currentType, out IUnresolvedMember currentMember); + + Tuple GetMemberTextToCaret(int caretOffset, IUnresolvedTypeDefinition currentType, IUnresolvedMember currentMember); + + CSharpAstResolver GetResolver (CSharpResolver resolver, AstNode rootNode); + } + + public class DefaultCompletionContextProvider : ICompletionContextProvider + { + readonly IDocument document; + readonly CSharpParsedFile parsedFile; + + public DefaultCompletionContextProvider (IDocument document, CSharpParsedFile parsedFile) + { + if (document == null) + throw new ArgumentNullException("document"); + if (parsedFile == null) + throw new ArgumentNullException("parsedFile"); + this.document = document; + this.parsedFile = parsedFile; + } + + public void GetCurrentMembers(int offset, out IUnresolvedTypeDefinition currentType, out IUnresolvedMember currentMember) + { + //var document = engine.document; + var location = document.GetLocation(offset); + + currentType = null; + + foreach (var type in parsedFile.TopLevelTypeDefinitions) { + if (type.Region.Begin < location) + currentType = type; + } + currentType = FindInnerType (currentType, location); + + // location is beyond last reported end region, now we need to check, if the end region changed + if (currentType != null && currentType.Region.End < location) { + if (!IsInsideType (currentType, location)) + currentType = null; + } + currentMember = null; + if (currentType != null) { + foreach (var member in currentType.Members) { + if (member.Region.Begin < location && (currentMember == null || currentMember.Region.Begin < member.Region.Begin)) + currentMember = member; + } + } + + // location is beyond last reported end region, now we need to check, if the end region changed + // NOTE: Enums are a special case, there the "last" field needs to be treated as current member + if (currentMember != null && currentMember.Region.End < location && currentType.Kind != TypeKind.Enum) { + if (!IsInsideType (currentMember, location)) + currentMember = null; + }/* + var stack = GetBracketStack (engine.GetMemberTextToCaret ().Item1); + if (stack.Count == 0) + currentMember = null;*/ + } + + IUnresolvedTypeDefinition FindInnerType (IUnresolvedTypeDefinition parent, TextLocation location) + { + if (parent == null) + return null; + var currentType = parent; + foreach (var type in parent.NestedTypes) { + if (type.Region.Begin < location && location < type.Region.End) + currentType = FindInnerType (type, location); + } + + return currentType; + } + + bool IsInsideType (IUnresolvedEntity currentType, TextLocation location) + { + int startOffset = document.GetOffset (currentType.Region.Begin); + int endOffset = document.GetOffset (location); + //bool foundEndBracket = false; + + var bracketStack = new Stack (); + + bool isInString = false, isInChar = false; + bool isInLineComment = false, isInBlockComment = false; + + for (int i = startOffset; i < endOffset; i++) { + char ch = document.GetCharAt (i); + switch (ch) { + case '(': + case '[': + case '{': + if (!isInString && !isInChar && !isInLineComment && !isInBlockComment) + bracketStack.Push (ch); + break; + case ')': + case ']': + case '}': + if (!isInString && !isInChar && !isInLineComment && !isInBlockComment) + if (bracketStack.Count > 0) + bracketStack.Pop (); + break; + case '\r': + case '\n': + isInLineComment = false; + break; + case '/': + if (isInBlockComment) { + if (i > 0 && document.GetCharAt (i - 1) == '*') + isInBlockComment = false; + } else if (!isInString && !isInChar && i + 1 < document.TextLength) { + char nextChar = document.GetCharAt (i + 1); + if (nextChar == '/') + isInLineComment = true; + if (!isInLineComment && nextChar == '*') + isInBlockComment = true; + } + break; + case '"': + if (!(isInChar || isInLineComment || isInBlockComment)) + isInString = !isInString; + break; + case '\'': + if (!(isInString || isInLineComment || isInBlockComment)) + isInChar = !isInChar; + break; + default : + break; + } + } + return bracketStack.Any (t => t == '{'); + } + + public Tuple GetMemberTextToCaret(int caretOffset, IUnresolvedTypeDefinition currentType, IUnresolvedMember currentMember) + { + int startOffset; + if (currentMember != null && currentType != null && currentType.Kind != TypeKind.Enum) { + startOffset = document.GetOffset(currentMember.Region.Begin); + } else if (currentType != null) { + startOffset = document.GetOffset(currentType.Region.Begin); + } else { + startOffset = 0; + } + while (startOffset > 0) { + char ch = document.GetCharAt(startOffset - 1); + if (ch != ' ' && ch != '\t') { + break; + } + --startOffset; + } + + return Tuple.Create (document.GetText (startOffset, caretOffset - startOffset), document.GetLocation (startOffset)); + } + + + public CSharpAstResolver GetResolver (CSharpResolver resolver, AstNode rootNode) + { + return new CSharpAstResolver (resolver, rootNode, parsedFile); + } + + + } +} + diff --git a/ICSharpCode.NRefactory.CSharp/Completion/IMemberProvider.cs b/ICSharpCode.NRefactory.CSharp/Completion/IMemberProvider.cs deleted file mode 100644 index 689fe96148..0000000000 --- a/ICSharpCode.NRefactory.CSharp/Completion/IMemberProvider.cs +++ /dev/null @@ -1,40 +0,0 @@ -// -// IMemberProvider.cs -// -// Author: -// Mike Krüger -// -// Copyright (c) 2012 Xamarin Inc. -// -// 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 ICSharpCode.NRefactory.TypeSystem; -using ICSharpCode.NRefactory.Editor; -using ICSharpCode.NRefactory.CSharp.TypeSystem; -using System.Linq; - -namespace ICSharpCode.NRefactory.CSharp.Completion -{ - public interface IMemberProvider - { - void GetCurrentMembers (int offset, out IUnresolvedTypeDefinition currentType, out IUnresolvedMember currentMember); - } -} - diff --git a/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj b/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj index cdcc1a1ad0..6d42599e5f 100644 --- a/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj +++ b/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj @@ -41,8 +41,7 @@ TRACE;FULL_AST - PdbOnly - false + none full @@ -310,7 +309,6 @@ - @@ -390,6 +388,7 @@ + diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/InconsistentNamingIssue/WordParser.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/InconsistentNamingIssue/WordParser.cs index 98ac57d6cd..9cbbcff0a3 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/InconsistentNamingIssue/WordParser.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/InconsistentNamingIssue/WordParser.cs @@ -37,26 +37,28 @@ namespace ICSharpCode.NRefactory.CSharp bool lastWasLower = false, lastWasUpper = false; for (int i = 0; i < identifier.Length; i++) { char c = identifier[i]; - if (c == '_') { - if ((i - wordStart) > 0) { - words.Add (identifier.Substring (wordStart, i - wordStart)); - } - wordStart = i + 1; - lastWasLower = lastWasUpper = false; - } else if (Char.IsLower (c)) { + var category = char.GetUnicodeCategory (c); + if (category == System.Globalization.UnicodeCategory.LowercaseLetter) { if (lastWasUpper && (i - wordStart) > 2) { words.Add (identifier.Substring (wordStart, i - wordStart - 1)); wordStart = i - 1; } lastWasLower = true; lastWasUpper = false; - } else if (Char.IsUpper (c)) { + } else if (category == System.Globalization.UnicodeCategory.UppercaseLetter) { if (lastWasLower) { words.Add (identifier.Substring (wordStart, i - wordStart)); wordStart = i; } lastWasLower = false; lastWasUpper = true; + } else { + if (c == '_') { + if ((i - wordStart) > 0) + words.Add(identifier.Substring(wordStart, i - wordStart)); + wordStart = i + 1; + lastWasLower = lastWasUpper = false; + } } } if (wordStart < identifier.Length) diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionBugTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionBugTests.cs index 206aeae030..80cae089e7 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionBugTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionBugTests.cs @@ -199,46 +199,48 @@ namespace ICSharpCode.NRefactory.CSharp.CodeCompletion #endregion } - static CompletionDataList CreateProvider (string text, bool isCtrlSpace) + static CompletionDataList CreateProvider(string text, bool isCtrlSpace) { string parsedText; string editorText; - int cursorPosition = text.IndexOf ('$'); - int endPos = text.IndexOf ('$', cursorPosition + 1); + int cursorPosition = text.IndexOf('$'); + int endPos = text.IndexOf('$', cursorPosition + 1); if (endPos == -1) { - parsedText = editorText = text.Substring (0, cursorPosition) + text.Substring (cursorPosition + 1); + parsedText = editorText = text.Substring(0, cursorPosition) + text.Substring(cursorPosition + 1); } else { - parsedText = text.Substring (0, cursorPosition) + new string (' ', endPos - cursorPosition) + text.Substring (endPos + 1); - editorText = text.Substring (0, cursorPosition) + text.Substring (cursorPosition + 1, endPos - cursorPosition - 1) + text.Substring (endPos + 1); + parsedText = text.Substring(0, cursorPosition) + new string(' ', endPos - cursorPosition) + text.Substring(endPos + 1); + editorText = text.Substring(0, cursorPosition) + text.Substring(cursorPosition + 1, endPos - cursorPosition - 1) + text.Substring(endPos + 1); cursorPosition = endPos - 1; } - var doc = new ReadOnlyDocument (editorText); + var doc = new ReadOnlyDocument(editorText); - IProjectContent pctx = new CSharpProjectContent (); - pctx = pctx.AddAssemblyReferences (new [] { CecilLoaderTests.Mscorlib, CecilLoaderTests.SystemCore }); + IProjectContent pctx = new CSharpProjectContent(); + pctx = pctx.AddAssemblyReferences(new [] { CecilLoaderTests.Mscorlib, CecilLoaderTests.SystemCore }); - var compilationUnit = new CSharpParser ().Parse (parsedText, "program.cs"); - compilationUnit.Freeze (); + var compilationUnit = new CSharpParser().Parse(parsedText, "program.cs"); + compilationUnit.Freeze(); - var parsedFile = compilationUnit.ToTypeSystem (); - pctx = pctx.UpdateProjectContent (null, parsedFile); + var parsedFile = compilationUnit.ToTypeSystem(); + pctx = pctx.UpdateProjectContent(null, parsedFile); - var cmp = pctx.CreateCompilation (); - var loc = doc.GetLocation (cursorPosition); + var cmp = pctx.CreateCompilation(); + var loc = doc.GetLocation(cursorPosition); - var rctx = new CSharpTypeResolveContext (cmp.MainAssembly); - rctx = rctx.WithUsingScope (parsedFile.GetUsingScope (loc).Resolve (cmp)); + var rctx = new CSharpTypeResolveContext(cmp.MainAssembly); + rctx = rctx.WithUsingScope(parsedFile.GetUsingScope(loc).Resolve(cmp)); - var curDef = parsedFile.GetInnermostTypeDefinition (loc); + var curDef = parsedFile.GetInnermostTypeDefinition(loc); if (curDef != null) { - var resolvedDef = curDef.Resolve (rctx).GetDefinition (); - rctx = rctx.WithCurrentTypeDefinition (resolvedDef); - var curMember = resolvedDef.Members.FirstOrDefault (m => m.Region.Begin <= loc && loc < m.BodyRegion.End); - if (curMember != null) - rctx = rctx.WithCurrentMember (curMember); + var resolvedDef = curDef.Resolve(rctx).GetDefinition(); + rctx = rctx.WithCurrentTypeDefinition(resolvedDef); + var curMember = resolvedDef.Members.FirstOrDefault(m => m.Region.Begin <= loc && loc < m.BodyRegion.End); + if (curMember != null) { + rctx = rctx.WithCurrentMember(curMember); + } } - var engine = new CSharpCompletionEngine (doc, new TestFactory (), pctx, rctx, compilationUnit, parsedFile); + var mb = new DefaultCompletionContextProvider(doc, parsedFile); + var engine = new CSharpCompletionEngine (doc, mb, new TestFactory (), pctx, rctx); engine.EolMarker = Environment.NewLine; engine.FormattingPolicy = FormattingOptionsFactory.CreateMono (); @@ -253,17 +255,18 @@ namespace ICSharpCode.NRefactory.CSharp.CodeCompletion }; } - Tuple GetContent (string text, CompilationUnit compilationUnit) + Tuple GetContent(string text, CompilationUnit compilationUnit) { - var doc = new ReadOnlyDocument (text); - IProjectContent pctx = new CSharpProjectContent (); - pctx = pctx.AddAssemblyReferences (new [] { CecilLoaderTests.Mscorlib, CecilLoaderTests.SystemCore }); - var parsedFile = compilationUnit.ToTypeSystem (); + var doc = new ReadOnlyDocument(text); + IProjectContent pctx = new CSharpProjectContent(); + pctx = pctx.AddAssemblyReferences(new [] { CecilLoaderTests.Mscorlib, CecilLoaderTests.SystemCore }); + var parsedFile = compilationUnit.ToTypeSystem(); - pctx = pctx.UpdateProjectContent (null, parsedFile); - var cmp = pctx.CreateCompilation (); + pctx = pctx.UpdateProjectContent(null, parsedFile); + var cmp = pctx.CreateCompilation(); - var engine = new CSharpCompletionEngine (doc, new TestFactory (), pctx, new CSharpTypeResolveContext (cmp.MainAssembly), compilationUnit, parsedFile); + var mb = new DefaultCompletionContextProvider(doc, parsedFile); + var engine = new CSharpCompletionEngine (doc, mb, new TestFactory (), pctx, new CSharpTypeResolveContext (cmp.MainAssembly)); engine.EolMarker = Environment.NewLine; engine.FormattingPolicy = FormattingOptionsFactory.CreateMono (); return Tuple.Create (doc, engine); diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/ParameterCompletionTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/ParameterCompletionTests.cs index 82ec44c466..aef48a5e15 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/ParameterCompletionTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/ParameterCompletionTests.cs @@ -254,43 +254,45 @@ namespace ICSharpCode.NRefactory.CSharp.CodeCompletion #endregion } - internal static IParameterDataProvider CreateProvider (string text) + internal static IParameterDataProvider CreateProvider(string text) { string parsedText; string editorText; - int cursorPosition = text.IndexOf ('$'); - int endPos = text.IndexOf ('$', cursorPosition + 1); - if (endPos == -1) - parsedText = editorText = text.Substring (0, cursorPosition) + text.Substring (cursorPosition + 1); - else { - parsedText = text.Substring (0, cursorPosition) + new string (' ', endPos - cursorPosition) + text.Substring (endPos + 1); - editorText = text.Substring (0, cursorPosition) + text.Substring (cursorPosition + 1, endPos - cursorPosition - 1) + text.Substring (endPos + 1); + int cursorPosition = text.IndexOf('$'); + int endPos = text.IndexOf('$', cursorPosition + 1); + if (endPos == -1) { + parsedText = editorText = text.Substring(0, cursorPosition) + text.Substring(cursorPosition + 1); + } else { + parsedText = text.Substring(0, cursorPosition) + new string(' ', endPos - cursorPosition) + text.Substring(endPos + 1); + editorText = text.Substring(0, cursorPosition) + text.Substring(cursorPosition + 1, endPos - cursorPosition - 1) + text.Substring(endPos + 1); cursorPosition = endPos - 1; } - var doc = new ReadOnlyDocument (editorText); + var doc = new ReadOnlyDocument(editorText); - IProjectContent pctx = new CSharpProjectContent (); - pctx = pctx.AddAssemblyReferences (new [] { CecilLoaderTests.Mscorlib, CecilLoaderTests.SystemCore }); + IProjectContent pctx = new CSharpProjectContent(); + pctx = pctx.AddAssemblyReferences(new [] { CecilLoaderTests.Mscorlib, CecilLoaderTests.SystemCore }); - var compilationUnit = new CSharpParser ().Parse (parsedText, "program.cs"); - compilationUnit.Freeze (); + var compilationUnit = new CSharpParser().Parse(parsedText, "program.cs"); + compilationUnit.Freeze(); - var parsedFile = compilationUnit.ToTypeSystem (); - pctx = pctx.UpdateProjectContent (null, parsedFile); - var cmp = pctx.CreateCompilation (); - var loc = doc.GetLocation (cursorPosition); + var parsedFile = compilationUnit.ToTypeSystem(); + pctx = pctx.UpdateProjectContent(null, parsedFile); + var cmp = pctx.CreateCompilation(); + var loc = doc.GetLocation(cursorPosition); - var rctx = new CSharpTypeResolveContext (cmp.MainAssembly); - rctx = rctx.WithUsingScope (parsedFile.GetUsingScope (loc).Resolve (cmp)); - var curDef = parsedFile.GetInnermostTypeDefinition (loc); + var rctx = new CSharpTypeResolveContext(cmp.MainAssembly); + rctx = rctx.WithUsingScope(parsedFile.GetUsingScope(loc).Resolve(cmp)); + var curDef = parsedFile.GetInnermostTypeDefinition(loc); if (curDef != null) { - rctx = rctx.WithCurrentTypeDefinition (curDef.Resolve (rctx).GetDefinition ()); - var curMember = parsedFile.GetMember (loc); - if (curMember != null) - rctx = rctx.WithCurrentMember (curMember.CreateResolved (rctx)); + rctx = rctx.WithCurrentTypeDefinition(curDef.Resolve(rctx).GetDefinition()); + var curMember = parsedFile.GetMember(loc); + if (curMember != null) { + rctx = rctx.WithCurrentMember(curMember.CreateResolved(rctx)); + } } - var engine = new CSharpParameterCompletionEngine (doc, new TestFactory (pctx), pctx, rctx, compilationUnit, parsedFile); + var mb = new DefaultCompletionContextProvider(doc, parsedFile); + var engine = new CSharpParameterCompletionEngine (doc, mb, new TestFactory (pctx), pctx, rctx); return engine.GetParameterDataProvider (cursorPosition, doc.GetCharAt (cursorPosition - 1)); } diff --git a/ICSharpCode.NRefactory/Documentation/XmlDocumentationProvider.cs b/ICSharpCode.NRefactory/Documentation/XmlDocumentationProvider.cs index de2105211e..e16b6c0f03 100644 --- a/ICSharpCode.NRefactory/Documentation/XmlDocumentationProvider.cs +++ b/ICSharpCode.NRefactory/Documentation/XmlDocumentationProvider.cs @@ -121,7 +121,7 @@ namespace ICSharpCode.NRefactory.Documentation this.fileName = fileName; ReadXmlDoc(xmlReader); } else { - string redirectionTarget = GetRedirectionTarget(xmlReader.GetAttribute("redirect")); + string redirectionTarget = GetRedirectionTarget(fileName, xmlReader.GetAttribute("redirect")); if (redirectionTarget != null) { Debug.WriteLine("XmlDoc " + fileName + " is redirecting to " + redirectionTarget); using (FileStream redirectedFs = new FileStream(redirectionTarget, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete)) { @@ -138,7 +138,7 @@ namespace ICSharpCode.NRefactory.Documentation } } - static string GetRedirectionTarget(string target) + static string GetRedirectionTarget(string xmlFileName, string target) { string programFilesDir = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86); programFilesDir = AppendDirectorySeparator(programFilesDir); @@ -146,8 +146,11 @@ namespace ICSharpCode.NRefactory.Documentation string corSysDir = System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory(); corSysDir = AppendDirectorySeparator(corSysDir); - return LookupLocalizedXmlDoc(target.Replace("%PROGRAMFILESDIR%", programFilesDir) - .Replace("%CORSYSDIR%", corSysDir)); + var fileName = target.Replace ("%PROGRAMFILESDIR%", programFilesDir) + .Replace ("%CORSYSDIR%", corSysDir); + if (!Path.IsPathRooted (fileName)) + fileName = Path.Combine (Path.GetDirectoryName (xmlFileName), fileName); + return LookupLocalizedXmlDoc(fileName); } static string AppendDirectorySeparator(string dir)